一. 談談你對Java平臺的理解? "Java 是解釋執(zhí)行",這句話正確么?
典型回答:
Java本身是一種面向對象的語言,最顯著的特點有兩個方面,一個是所謂的"書寫一次,到處運行";能夠非常容易地獲得跨平臺能力;另外就是垃圾收集器(GC),Java通 過垃圾收集器回收分配內存,大部分情況下,程序員不需要自己操心內存的分配和回收。我們日常接觸到JRE或者JDK。JRE也就是Java運行環(huán)境,包含了JVM和Java類庫,以及一些模塊等。而JDK可以看作是JRE的一個超集,提供了更多的工具,比如編譯器各種診斷工具。
"對于Java是解釋執(zhí)行"這句話,這個說法不準確。我們開發(fā)的Java的源代碼,首先通過Javac編譯成為字節(jié)碼,然后在運行時通過Java虛擬機內嵌的解釋器將字節(jié)碼轉換為最終的機器碼。但是常見的JVM,比如我們大數(shù)據(jù)情況使用的0racleJDK提供的HostpotJVM,提供了JIT編譯器,就是通常所說的動態(tài)編譯器,JIT能夠在運行時將熱點代碼(高頻調用的方法和代碼塊)編譯成機器碼,這種情況下部分熱點就屬于編譯執(zhí)行,而不是解釋執(zhí)行。這樣類似于緩存技術,運行時在遇到熱點代碼可以直接執(zhí)行,而不是先解釋在執(zhí)行。
知識擴展:
在運行時 , JVM通過類加載器加載字節(jié)碼,解釋或者編譯執(zhí)行。就像我們前面提到的,主流的Java版本中,如JDK8實際是解釋和編譯混合的一種模式,即所謂的混合模式。JIT編譯器分為多種模式(Server模式C1ient模式AOT模式)通常運行在Server模式的JVM,會進行上萬次調用以收集足夠的信息進行高效編譯,client模式這個門限是1500次。
Oracle Hostpot JVM內置了兩個不同的JIT compiler,C1對應 前面說的client模式,適用于對于啟動速度敏感的應用,比如普通Java桌面應用;C2對應Server模式,它的優(yōu)點是為長時間運行的服務器端應用設計的。
Java虛擬機啟動時, 可以指定不同的參數(shù)對運行模式進行選擇。比如,執(zhí)行"-Xint",就是告訴JVM只進行解釋執(zhí)行,不對代碼進行編譯,這種模式拋棄了JIT可能帶來的性能優(yōu)勢。畢竟解釋器是逐條讀入,逐條解釋運行的。與其相對性的,還有一個"Xcomp"參數(shù),這是告訴JVM關閉解釋器,不要進行解釋執(zhí)行,或者叫做最大優(yōu)化級別。那你可能會問這種模式是不是最高效啊?簡單來說,還真未必。"-Xcomp" 會導致JVM啟動變慢非常多..
除了日常最常見的Java使用模式,其實還有一種新的編譯方式,即所謂的AOT,直接將字節(jié)碼編譯成機器碼,這樣就避免了JIT預熱等各方面的開銷,比如Oracle JDK 9就引入了實性質的AOT特性,并且增加了jaotc工具。
另外,JVM作為一個強大的平臺,不僅僅只有Java語言可以運行在JVM上,本質上合規(guī)的字節(jié)碼都可以運行,Java語言自身也為此提供了便利,我們可以看到類似ClojureScala Groovy JRuby Jython等 大量JVM語言,活躍在不同的場景。
二. 請對比Exception和Error,另外,運行時異常與一般異常有什么區(qū)別?
典型回答:
Exception和Error都是繼承了Throwable類,在Java中只有Throwable類型的實例才可以被拋出或者捕獲,它是異常處理機制的基本組成類型。Exceptoin和Error體現(xiàn)了Java平臺設計者對于不同異常情況的分類。Exception是程序正常運行中,可以預料的意外情況,可以并且應該被捕獲,進行相應處理。Error是指在正常情況下,不大可能出現(xiàn)的情況,絕大部分的Error都會導致處于非正常的不可恢復狀態(tài)。既然是非正常情況,所以不便于也不需要捕獲,常見的比如.OutofMemoryError之類,都是Error的子類。
Exceptoin又分為可檢查異常和不檢查異常,可檢查異常在代碼里必須顯示地進行捕獲處理,這是編譯器檢查的一部分。不檢查異常就是所謂的運行時異常,類似于Nul1PointerException ArrayIndex0utofBoundsException之類,通常是可以編碼避免的邏輯錯誤,具體根據(jù)需要來進行判斷是否需要捕獲,并不會在編譯期強制要求。
知識擴展:
在開發(fā)中盡量不要捕獲類似Exceptio這樣的通用異常,而是應該捕獲特定異常.這是因為我們在日常的開發(fā)和合作中,我們讀代碼的機會往往超過寫代碼,軟,件工程是門協(xié)作的藝術,所以我們有義務讓自己的代碼能夠直接地體現(xiàn)出盡量多的信息,而泛泛的Exception之類,恰恰隱藏了我們的目的。另外,我們也要保證程序不會捕獲到我們不希望捕獲的異常。比如,你可能更希望RuntimeException 被擴散出來,而不是被捕獲。
在開發(fā)中不要生吞異常。這是異常處理中要特別注意的事情,因為很可能會導致非常難以診斷的詭異情況。生吞異常,往往是基于假設這段代碼可能不會發(fā)生,或者感覺忽略異常是無所謂的,但是千萬不要在產品代碼做這種假設!如果我們不把異常拋出來,或者也沒有輸出日志之類,程序可能在后續(xù)代碼以不可控的方式結束。沒有人能夠輕易判斷究竟是哪里出了異常,以及是什么原因產生了異常。
在開發(fā)中不要輸出標準錯誤(STERR),因為有時候你很難判斷出到底輸出到哪里去了。尤其是分布式系統(tǒng),如果發(fā)生異常,但是無法找到堆棧軌跡,這純屬是為診斷設置障礙。所以最好使用產品日志,詳細地輸出到日志系統(tǒng)里。
Throw early,catch late。在開發(fā)中可能會出現(xiàn)各種情況,比如獲取配置失敗之類的。在發(fā)現(xiàn)問題的時候,第一時間拋出,能夠更加清晰地反映問題,這是Throw early。 catch late就是 我們經(jīng)常煩惱的問題,捕獲異常后,需要怎么處理?最差的方式,就是我們前面提到的"生吞異常",本質上就是掩蓋問題。如果實在不知道如何處理,可以選擇保留原有異常的cause信息,直接再拋出或者構建新的異常拋出去。在更高層,因為有了清晰的(業(yè)務)邏輯,往往會更清楚合適的處理方式是什么。
有時候,我們會根據(jù)需要自己定義異常,這個時候除了保證提供足夠的信息,還需要考慮兩點。一是否需要定異常CheckedException,因為這種類型的設計初衷是為了從異常情況恢復,作為異常設計者,我們往往有充足信息進行分類。在保證診斷信息足夠的同時,也要考慮避免包含敏感信息,因為那樣可能會導致潛在的安全問題。如果我們看Java的標準類庫,你可能注意到類似java. net. ConnectException,出錯信息是類似"Connection refused", 而不包含具體的機器名IP端口等,一個重要的考量就是信息安全。類似的情況在日志系統(tǒng)中也有,比如,用戶數(shù)據(jù)一般是不可以輸出到日志里面的。
try-catch代碼段會產生額外的性能開銷,或者換個角度來說,它往往會影響JVM對代碼進行優(yōu)化,所以建議僅捕獲有必要的代碼段.盡量不要一個大的try包住整段代碼,與此同時,利用異??刂拼a流程,也不是一個好主意,遠比我們通常意義上的條件語句要低效。
額外:
NoClassDeF oundError和ClassNotFoundException的區(qū)別?
首先NoClassDeFoundError是一個錯誤,ClassNotFoundException是一個異常。ClassNotFoundException的產生原因,Java支持使用Class. froName方法來動態(tài)地加載類,任意一個類的類名如果被作為參數(shù)傳遞給這個方法都將導致該類被加載到JVM內存中,如果這個類在類路徑中沒有被找到,那么此時就會在運行時拋出ClassNotFoundException異常。
另外還有一個導致ClassNotFoundException的原因就是,當一個類已經(jīng)被某個類加載器加載到內存中,此時另一個類加載器又嘗試著動態(tài)地從同一個包中加載這個類。
NoClassDeFoundError產生的原因在于:如果JVM或者ClassLoader實例嘗試加載類的時候找不到類的定義。例如要查找的類在編譯的時候是存在的,運行的時候,找不到了。這個時候就會導致NoClassDefFoundError.造成該問題的原因可能是打包過程中漏掉了部分類,或者jar包出現(xiàn)損壞或者篡改。解決這個問題的辦法就是查找那些在開發(fā)期間存在與類路徑下,但在運行期間卻不在類路徑下的類。
三. 對比Hashtable、HashMap、TreeMap有什么不同?
典型回答:
HashTable HashMap TreeMap都是最常見的一些Map實 現(xiàn),是以鍵值對的形式存儲和操作數(shù)據(jù)的容器類型。
HashTable是早期Java類庫提供的一個哈希表實現(xiàn),本身是同步的,不支持nu11鍵和值,由于同步導致的性能開銷,所以已經(jīng)很少被推薦使用。
HashMap是應用更加廣泛的哈希表實現(xiàn),行為大致.上與HashTable- -致,主要區(qū)別在于HashMap不是同步的,支持nul1鍵和值等。通常情況下, HashMap進行put或者get操作,可以達到常數(shù)時間的性能,所以它是絕大部分利用鍵值對存儲場景的首選,比如,實現(xiàn)一個用戶ID和用戶信息對應的運行時存儲結構。
TreeMap則是基于紅黑樹的一種提供順序訪問的Map,和HashMap不同,它的getremove之類操作都是0(long(n)的時間復雜度,具體順序可以由指定的Comparator來決定或者根據(jù)鍵的自然順序來判斷。
知識擴展:
HashMap實現(xiàn)原理是經(jīng)常被問到的一個問題,以下基于JDK1.8分析。
1. 內部存儲
HashMap的內部存儲是一個數(shù)組,數(shù)組的元素Node實現(xiàn)了Map.Entry接口(hash, key, value, next) , next非空時指向定位相同的另一個Entry,如圖:
JDK 8之前,其內部是由數(shù)組+鏈表來實現(xiàn)的,而JDK 8對于鏈表長度超過8的鏈表將轉儲為紅黑樹。
2. 容量(capacity)和負載因子(loadFactor)
簡單的說, capacity就是數(shù)組的大小,loadFactory就是數(shù)組填滿程度的最大比列。當數(shù)組中的元素的數(shù)目大于capaci ty*loadFactor時就需要擴容,調整數(shù)組的大小為當前的2 倍。同時初始化容量的大小也是2的次冪(大于等于設定容量的最小次冪),則數(shù)組的大小在擴容前后都將是2的次冪。默認的容量為16,負載因子為0.75。
3. put方法的大致的思路
如果key的值為null,則將該鍵值對添加到table[0]處,遍歷該鏈表,如果有key為null,則將value替換。
如果key不為null,獲取key的hashCode值,經(jīng)過indexFor()方法運算得到的值作為標識,但由于hashCode的值并不唯一,經(jīng)過運算獲取的值也不能保證唯一(哈希沖突),所以,經(jīng)過以上運算得來的數(shù)值只能作為數(shù)組的索引。
當通過索引定位到這個節(jié)點時,在遍歷該鏈表,判斷是否存在相同的key對象,如果存在就用新的value覆蓋舊的value
如果不存在,就創(chuàng)建一個Entry對象添加到table[i]處,如果table[i]已經(jīng)存在其他元素,那么新Entry對象將會保存在鏈表的表頭,通過next指針指向原有的Entry對象,形成鏈表結構。
當鏈表的結構太長時(默認超過8個元素),鏈表就會轉為紅黑樹。
4. get方法的大致的思路
對key進行nu11檢查。如果key是nu11,table[0]這個位置的元素將被返回
key的hashcode() 方法被調用,然后計算hash值。
indexFor (hash, table. length)用來計算要獲取的Entry對象在table數(shù)組中的精確的位置(使用剛才計算的hash值)
在獲取了table數(shù)組的索引之后,會迭代鏈表,調用equals()方法檢查key的相等性,如果equals()方法返回true,get方法返回Entry對象的value,否則,返回null。
四. ArrayList Vector linkedList的 區(qū)別?
典型回答:
這三者都是實現(xiàn)集合框架中的List,也就是所謂的有序集合,因此具體功能也比較類似,比如都提供按照位置進行定位添加或者刪除的操作,都提供迭代器以遍歷其內容等。但因為具體的設計區(qū)別在行為性能線程安全等方面,表現(xiàn)又有很大不同。
Vector是Java早期提供的線程安全的動態(tài)數(shù)組,如果不需要線程安全,并不建議選擇,畢竟同步是有額外開銷的。Vector內 部是使用對象數(shù)組來保存數(shù)據(jù),可以根據(jù)需要自動的增加容量,當數(shù)組以滿時,會創(chuàng)建新的數(shù)組,并拷貝原有數(shù)組數(shù)據(jù)。
ArrayList是應用更加廣泛的動態(tài)數(shù)組實現(xiàn),它本身不是線程安全的,所以性能要好很多。與Vector近似,ArrayList也是可以根據(jù)需要調整容量,不過兩者的調整邏輯有所區(qū)別, Vector在擴容時會提高1倍,而ArrayList則是增加50%。
linkedList顧明思議是Java提供的雙向鏈表,所以它不需要像.上面兩種那樣調整容量,它也不是線程安全的。
Vector和ArrayList作為動態(tài)數(shù)組,其內部元素以數(shù)組形式順序存儲的,所以非常適合隨即訪問的場合。除了尾部出入元素和刪除元素,往往性能會相對較差,比如我們在中間位置插入一個元素,需要移動后續(xù)所有元素。而linkedList進行節(jié)點插入刪除卻要高效得很多,但是隨即訪問性能則要比動態(tài)數(shù)組慢。
以上就是長沙一度軟件培訓Java培訓機構小編介紹的“2020年美團Java后端面試題目”的內容,希望對大家有幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務。
相關推薦
最新最全java面試題及答案(初級到高級)
史上最全的中高級JAVA工程師面試題及答案匯總
Java高級開發(fā)工程師面試題
2019史上最全java面試題題庫大全800題
哪有資深java工程師面試題