更新時(shí)間:2020-01-07 14:58:15 來源:動(dòng)力節(jié)點(diǎn) 瀏覽4145次

一. 談?wù)勀銓ava平臺(tái)的理解? "Java 是解釋執(zhí)行",這句話正確么?
典型回答:
Java本身是一種面向?qū)ο蟮恼Z言,最顯著的特點(diǎn)有兩個(gè)方面,一個(gè)是所謂的"書寫一次,到處運(yùn)行";能夠非常容易地獲得跨平臺(tái)能力;另外就是垃圾收集器(GC),Java通 過垃圾收集器回收分配內(nèi)存,大部分情況下,程序員不需要自己操心內(nèi)存的分配和回收。我們?nèi)粘=佑|到JRE或者JDK。JRE也就是Java運(yùn)行環(huán)境,包含了JVM和Java類庫,以及一些模塊等。而JDK可以看作是JRE的一個(gè)超集,提供了更多的工具,比如編譯器各種診斷工具。
"對于Java是解釋執(zhí)行"這句話,這個(gè)說法不準(zhǔn)確。我們開發(fā)的Java的源代碼,首先通過Javac編譯成為字節(jié)碼,然后在運(yùn)行時(shí)通過Java虛擬機(jī)內(nèi)嵌的解釋器將字節(jié)碼轉(zhuǎn)換為最終的機(jī)器碼。但是常見的JVM,比如我們大數(shù)據(jù)情況使用的0racleJDK提供的HostpotJVM,提供了JIT編譯器,就是通常所說的動(dòng)態(tài)編譯器,JIT能夠在運(yùn)行時(shí)將熱點(diǎn)代碼(高頻調(diào)用的方法和代碼塊)編譯成機(jī)器碼,這種情況下部分熱點(diǎn)就屬于編譯執(zhí)行,而不是解釋執(zhí)行。這樣類似于緩存技術(shù),運(yùn)行時(shí)在遇到熱點(diǎn)代碼可以直接執(zhí)行,而不是先解釋在執(zhí)行。
知識擴(kuò)展:
在運(yùn)行時(shí) , JVM通過類加載器加載字節(jié)碼,解釋或者編譯執(zhí)行。就像我們前面提到的,主流的Java版本中,如JDK8實(shí)際是解釋和編譯混合的一種模式,即所謂的混合模式。JIT編譯器分為多種模式(Server模式C1ient模式AOT模式)通常運(yùn)行在Server模式的JVM,會(huì)進(jìn)行上萬次調(diào)用以收集足夠的信息進(jìn)行高效編譯,client模式這個(gè)門限是1500次。
Oracle Hostpot JVM內(nèi)置了兩個(gè)不同的JIT compiler,C1對應(yīng) 前面說的client模式,適用于對于啟動(dòng)速度敏感的應(yīng)用,比如普通Java桌面應(yīng)用;C2對應(yīng)Server模式,它的優(yōu)點(diǎn)是為長時(shí)間運(yùn)行的服務(wù)器端應(yīng)用設(shè)計(jì)的。
Java虛擬機(jī)啟動(dòng)時(shí), 可以指定不同的參數(shù)對運(yùn)行模式進(jìn)行選擇。比如,執(zhí)行"-Xint",就是告訴JVM只進(jìn)行解釋執(zhí)行,不對代碼進(jìn)行編譯,這種模式拋棄了JIT可能帶來的性能優(yōu)勢。畢竟解釋器是逐條讀入,逐條解釋運(yùn)行的。與其相對性的,還有一個(gè)"Xcomp"參數(shù),這是告訴JVM關(guān)閉解釋器,不要進(jìn)行解釋執(zhí)行,或者叫做最大優(yōu)化級別。那你可能會(huì)問這種模式是不是最高效啊?簡單來說,還真未必。"-Xcomp" 會(huì)導(dǎo)致JVM啟動(dòng)變慢非常多..
除了日常最常見的Java使用模式,其實(shí)還有一種新的編譯方式,即所謂的AOT,直接將字節(jié)碼編譯成機(jī)器碼,這樣就避免了JIT預(yù)熱等各方面的開銷,比如Oracle JDK 9就引入了實(shí)性質(zhì)的AOT特性,并且增加了jaotc工具。
另外,JVM作為一個(gè)強(qiáng)大的平臺(tái),不僅僅只有Java語言可以運(yùn)行在JVM上,本質(zhì)上合規(guī)的字節(jié)碼都可以運(yùn)行,Java語言自身也為此提供了便利,我們可以看到類似ClojureScala Groovy JRuby Jython等 大量JVM語言,活躍在不同的場景。
二. 請對比Exception和Error,另外,運(yùn)行時(shí)異常與一般異常有什么區(qū)別?
典型回答:
Exception和Error都是繼承了Throwable類,在Java中只有Throwable類型的實(shí)例才可以被拋出或者捕獲,它是異常處理機(jī)制的基本組成類型。Exceptoin和Error體現(xiàn)了Java平臺(tái)設(shè)計(jì)者對于不同異常情況的分類。Exception是程序正常運(yùn)行中,可以預(yù)料的意外情況,可以并且應(yīng)該被捕獲,進(jìn)行相應(yīng)處理。Error是指在正常情況下,不大可能出現(xiàn)的情況,絕大部分的Error都會(huì)導(dǎo)致處于非正常的不可恢復(fù)狀態(tài)。既然是非正常情況,所以不便于也不需要捕獲,常見的比如.OutofMemoryError之類,都是Error的子類。
Exceptoin又分為可檢查異常和不檢查異常,可檢查異常在代碼里必須顯示地進(jìn)行捕獲處理,這是編譯器檢查的一部分。不檢查異常就是所謂的運(yùn)行時(shí)異常,類似于Nul1PointerException ArrayIndex0utofBoundsException之類,通常是可以編碼避免的邏輯錯(cuò)誤,具體根據(jù)需要來進(jìn)行判斷是否需要捕獲,并不會(huì)在編譯期強(qiáng)制要求。
知識擴(kuò)展:
在開發(fā)中盡量不要捕獲類似Exceptio這樣的通用異常,而是應(yīng)該捕獲特定異常.這是因?yàn)槲覀冊谌粘5拈_發(fā)和合作中,我們讀代碼的機(jī)會(huì)往往超過寫代碼,軟,件工程是門協(xié)作的藝術(shù),所以我們有義務(wù)讓自己的代碼能夠直接地體現(xiàn)出盡量多的信息,而泛泛的Exception之類,恰恰隱藏了我們的目的。另外,我們也要保證程序不會(huì)捕獲到我們不希望捕獲的異常。比如,你可能更希望RuntimeException 被擴(kuò)散出來,而不是被捕獲。
在開發(fā)中不要生吞異常。這是異常處理中要特別注意的事情,因?yàn)楹芸赡軙?huì)導(dǎo)致非常難以診斷的詭異情況。生吞異常,往往是基于假設(shè)這段代碼可能不會(huì)發(fā)生,或者感覺忽略異常是無所謂的,但是千萬不要在產(chǎn)品代碼做這種假設(shè)!如果我們不把異常拋出來,或者也沒有輸出日志之類,程序可能在后續(xù)代碼以不可控的方式結(jié)束。沒有人能夠輕易判斷究竟是哪里出了異常,以及是什么原因產(chǎn)生了異常。
在開發(fā)中不要輸出標(biāo)準(zhǔn)錯(cuò)誤(STERR),因?yàn)橛袝r(shí)候你很難判斷出到底輸出到哪里去了。尤其是分布式系統(tǒng),如果發(fā)生異常,但是無法找到堆棧軌跡,這純屬是為診斷設(shè)置障礙。所以最好使用產(chǎn)品日志,詳細(xì)地輸出到日志系統(tǒng)里。
Throw early,catch late。在開發(fā)中可能會(huì)出現(xiàn)各種情況,比如獲取配置失敗之類的。在發(fā)現(xiàn)問題的時(shí)候,第一時(shí)間拋出,能夠更加清晰地反映問題,這是Throw early。 catch late就是 我們經(jīng)常煩惱的問題,捕獲異常后,需要怎么處理?最差的方式,就是我們前面提到的"生吞異常",本質(zhì)上就是掩蓋問題。如果實(shí)在不知道如何處理,可以選擇保留原有異常的cause信息,直接再拋出或者構(gòu)建新的異常拋出去。在更高層,因?yàn)橛辛饲逦?業(yè)務(wù))邏輯,往往會(huì)更清楚合適的處理方式是什么。
有時(shí)候,我們會(huì)根據(jù)需要自己定義異常,這個(gè)時(shí)候除了保證提供足夠的信息,還需要考慮兩點(diǎn)。一是否需要定異常CheckedException,因?yàn)檫@種類型的設(shè)計(jì)初衷是為了從異常情況恢復(fù),作為異常設(shè)計(jì)者,我們往往有充足信息進(jìn)行分類。在保證診斷信息足夠的同時(shí),也要考慮避免包含敏感信息,因?yàn)槟菢涌赡軙?huì)導(dǎo)致潛在的安全問題。如果我們看Java的標(biāo)準(zhǔn)類庫,你可能注意到類似java. net. ConnectException,出錯(cuò)信息是類似"Connection refused", 而不包含具體的機(jī)器名IP端口等,一個(gè)重要的考量就是信息安全。類似的情況在日志系統(tǒng)中也有,比如,用戶數(shù)據(jù)一般是不可以輸出到日志里面的。
try-catch代碼段會(huì)產(chǎn)生額外的性能開銷,或者換個(gè)角度來說,它往往會(huì)影響JVM對代碼進(jìn)行優(yōu)化,所以建議僅捕獲有必要的代碼段.盡量不要一個(gè)大的try包住整段代碼,與此同時(shí),利用異??刂拼a流程,也不是一個(gè)好主意,遠(yuǎn)比我們通常意義上的條件語句要低效。
額外:
NoClassDeF oundError和ClassNotFoundException的區(qū)別?
首先NoClassDeFoundError是一個(gè)錯(cuò)誤,ClassNotFoundException是一個(gè)異常。ClassNotFoundException的產(chǎn)生原因,Java支持使用Class. froName方法來動(dòng)態(tài)地加載類,任意一個(gè)類的類名如果被作為參數(shù)傳遞給這個(gè)方法都將導(dǎo)致該類被加載到JVM內(nèi)存中,如果這個(gè)類在類路徑中沒有被找到,那么此時(shí)就會(huì)在運(yùn)行時(shí)拋出ClassNotFoundException異常。
另外還有一個(gè)導(dǎo)致ClassNotFoundException的原因就是,當(dāng)一個(gè)類已經(jīng)被某個(gè)類加載器加載到內(nèi)存中,此時(shí)另一個(gè)類加載器又嘗試著動(dòng)態(tài)地從同一個(gè)包中加載這個(gè)類。
NoClassDeFoundError產(chǎn)生的原因在于:如果JVM或者ClassLoader實(shí)例嘗試加載類的時(shí)候找不到類的定義。例如要查找的類在編譯的時(shí)候是存在的,運(yùn)行的時(shí)候,找不到了。這個(gè)時(shí)候就會(huì)導(dǎo)致NoClassDefFoundError.造成該問題的原因可能是打包過程中漏掉了部分類,或者jar包出現(xiàn)損壞或者篡改。解決這個(gè)問題的辦法就是查找那些在開發(fā)期間存在與類路徑下,但在運(yùn)行期間卻不在類路徑下的類。

三. 對比Hashtable、HashMap、TreeMap有什么不同?
典型回答:
HashTable HashMap TreeMap都是最常見的一些Map實(shí) 現(xiàn),是以鍵值對的形式存儲(chǔ)和操作數(shù)據(jù)的容器類型。
HashTable是早期Java類庫提供的一個(gè)哈希表實(shí)現(xiàn),本身是同步的,不支持nu11鍵和值,由于同步導(dǎo)致的性能開銷,所以已經(jīng)很少被推薦使用。
HashMap是應(yīng)用更加廣泛的哈希表實(shí)現(xiàn),行為大致.上與HashTable- -致,主要區(qū)別在于HashMap不是同步的,支持nul1鍵和值等。通常情況下, HashMap進(jìn)行put或者get操作,可以達(dá)到常數(shù)時(shí)間的性能,所以它是絕大部分利用鍵值對存儲(chǔ)場景的首選,比如,實(shí)現(xiàn)一個(gè)用戶ID和用戶信息對應(yīng)的運(yùn)行時(shí)存儲(chǔ)結(jié)構(gòu)。
TreeMap則是基于紅黑樹的一種提供順序訪問的Map,和HashMap不同,它的getremove之類操作都是0(long(n)的時(shí)間復(fù)雜度,具體順序可以由指定的Comparator來決定或者根據(jù)鍵的自然順序來判斷。
知識擴(kuò)展:
HashMap實(shí)現(xiàn)原理是經(jīng)常被問到的一個(gè)問題,以下基于JDK1.8分析。
1. 內(nèi)部存儲(chǔ)
HashMap的內(nèi)部存儲(chǔ)是一個(gè)數(shù)組,數(shù)組的元素Node實(shí)現(xiàn)了Map.Entry接口(hash, key, value, next) , next非空時(shí)指向定位相同的另一個(gè)Entry,如圖:

JDK 8之前,其內(nèi)部是由數(shù)組+鏈表來實(shí)現(xiàn)的,而JDK 8對于鏈表長度超過8的鏈表將轉(zhuǎn)儲(chǔ)為紅黑樹。
2. 容量(capacity)和負(fù)載因子(loadFactor)
簡單的說, capacity就是數(shù)組的大小,loadFactory就是數(shù)組填滿程度的最大比列。當(dāng)數(shù)組中的元素的數(shù)目大于capaci ty*loadFactor時(shí)就需要擴(kuò)容,調(diào)整數(shù)組的大小為當(dāng)前的2 倍。同時(shí)初始化容量的大小也是2的次冪(大于等于設(shè)定容量的最小次冪),則數(shù)組的大小在擴(kuò)容前后都將是2的次冪。默認(rèn)的容量為16,負(fù)載因子為0.75。
3. put方法的大致的思路
如果key的值為null,則將該鍵值對添加到table[0]處,遍歷該鏈表,如果有key為null,則將value替換。
如果key不為null,獲取key的hashCode值,經(jīng)過indexFor()方法運(yùn)算得到的值作為標(biāo)識,但由于hashCode的值并不唯一,經(jīng)過運(yùn)算獲取的值也不能保證唯一(哈希沖突),所以,經(jīng)過以上運(yùn)算得來的數(shù)值只能作為數(shù)組的索引。
當(dāng)通過索引定位到這個(gè)節(jié)點(diǎn)時(shí),在遍歷該鏈表,判斷是否存在相同的key對象,如果存在就用新的value覆蓋舊的value
如果不存在,就創(chuàng)建一個(gè)Entry對象添加到table[i]處,如果table[i]已經(jīng)存在其他元素,那么新Entry對象將會(huì)保存在鏈表的表頭,通過next指針指向原有的Entry對象,形成鏈表結(jié)構(gòu)。
當(dāng)鏈表的結(jié)構(gòu)太長時(shí)(默認(rèn)超過8個(gè)元素),鏈表就會(huì)轉(zhuǎn)為紅黑樹。
4. get方法的大致的思路
對key進(jìn)行nu11檢查。如果key是nu11,table[0]這個(gè)位置的元素將被返回
key的hashcode() 方法被調(diào)用,然后計(jì)算hash值。
indexFor (hash, table. length)用來計(jì)算要獲取的Entry對象在table數(shù)組中的精確的位置(使用剛才計(jì)算的hash值)
在獲取了table數(shù)組的索引之后,會(huì)迭代鏈表,調(diào)用equals()方法檢查key的相等性,如果equals()方法返回true,get方法返回Entry對象的value,否則,返回null。
四. ArrayList Vector LinkedList的 區(qū)別?
典型回答:
這三者都是實(shí)現(xiàn)集合框架中的List,也就是所謂的有序集合,因此具體功能也比較類似,比如都提供按照位置進(jìn)行定位添加或者刪除的操作,都提供迭代器以遍歷其內(nèi)容等。但因?yàn)榫唧w的設(shè)計(jì)區(qū)別在行為性能線程安全等方面,表現(xiàn)又有很大不同。
Vector是Java早期提供的線程安全的動(dòng)態(tài)數(shù)組,如果不需要線程安全,并不建議選擇,畢竟同步是有額外開銷的。Vector內(nèi) 部是使用對象數(shù)組來保存數(shù)據(jù),可以根據(jù)需要自動(dòng)的增加容量,當(dāng)數(shù)組以滿時(shí),會(huì)創(chuàng)建新的數(shù)組,并拷貝原有數(shù)組數(shù)據(jù)。
ArrayList是應(yīng)用更加廣泛的動(dòng)態(tài)數(shù)組實(shí)現(xiàn),它本身不是線程安全的,所以性能要好很多。與Vector近似,ArrayList也是可以根據(jù)需要調(diào)整容量,不過兩者的調(diào)整邏輯有所區(qū)別, Vector在擴(kuò)容時(shí)會(huì)提高1倍,而ArrayList則是增加50%。
LinkedList顧明思議是Java提供的雙向鏈表,所以它不需要像.上面兩種那樣調(diào)整容量,它也不是線程安全的。
Vector和ArrayList作為動(dòng)態(tài)數(shù)組,其內(nèi)部元素以數(shù)組形式順序存儲(chǔ)的,所以非常適合隨即訪問的場合。除了尾部出入元素和刪除元素,往往性能會(huì)相對較差,比如我們在中間位置插入一個(gè)元素,需要移動(dòng)后續(xù)所有元素。而LinkedList進(jìn)行節(jié)點(diǎn)插入刪除卻要高效得很多,但是隨即訪問性能則要比動(dòng)態(tài)數(shù)組慢。

以上就是動(dòng)力節(jié)點(diǎn)Java培訓(xùn)機(jī)構(gòu)小編介紹的“2020年美團(tuán)Java后端面試題目”的內(nèi)容,希望對大家有幫助,如有疑問,請?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為你服務(wù)。
相關(guān)推薦
相關(guān)閱讀
Java實(shí)驗(yàn)班
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
Java就業(yè)班
有基礎(chǔ) 直達(dá)就業(yè)
Java夜校直播班
業(yè)余時(shí)間 高薪轉(zhuǎn)行
Java在職加薪班
工作1~3年,加薪神器
Java架構(gòu)師班
工作3~5年,晉升架構(gòu)
提交申請后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)

初級 202925

初級 203221

初級 202629

初級 203743