java面試題常見
30 道常見 Java 面試題及答案
程式之心 2017-09-01
整理了一些常見的 Java 面試題和答案,希望對您有所幫助。這裡主要是 Java 基礎知識和概念的題目,考察的是面試者對 Java 的理解程度。因篇幅有限,沒有包含框架和中介軟體的內容,將在後面的文章中整理出來。
1.面向物件的特徵有哪些方面?
面向物件的特徵主要有以下幾個方面:
1)繼承:繼承是從已有類得到繼承資訊建立新類的過程。提供繼承資訊的類被稱為父類(超類、基類);得到繼承資訊的類被稱為子類(派生類)。
2)封裝:通常認為封裝是把資料和操作資料的方法繫結起來,對資料的訪問只能通過已定義的介面。面向物件的本質就是將現實世界描繪成一系列完全自治、封閉的物件。
3)多型:多型是指允許不同子型別的物件對同一訊息作出不同的響應。簡單的說就是用同樣的物件引用呼叫同樣的方法但是做了不同的事情。
2.Java 有沒有goto?
goto 是Java中的保留字,在目前版本的Java中沒有使用。(根據James Gosling(Java之父)編寫的《The Java Programming Language》一書的附錄中給出了一個Java關鍵字列表,其中有goto和const,但是這兩個是目前無法使用的關鍵字,因此有些地方將其稱之為保留字,其實保留字這個詞應該有更廣泛的意義,因為熟悉C語言的程式設計師都知道,在系統類庫中使用過的有特殊意義的單詞或單詞的組合都被視為保留字)
3.解釋記憶體中的棧(stack)、堆(heap)和靜態儲存區的用法。
通常我們定義一個基本資料型別的變數,一個物件的引用,還有就是函式呼叫的現場儲存都使用記憶體中的棧空間;而通過new關鍵字和構造器建立的物件放在堆空間;程式中的字面量(literal)如直接書寫的100、“hello”和常量都是放在靜態儲存區中。棧空間操作最快但是也很小,通常大量的物件都是放在堆空間,整個記憶體包括硬碟上的虛擬記憶體都可以被當成堆空間來使用。
String str = new String(“hello”);
上面的語句中str放在棧上,用new創建出來的字串物件放在堆上,而“hello”這個字面量放在靜態儲存區。
4.在Java 中,如何跳出當前的多重巢狀迴圈?
在最外層迴圈前加一個標記如A,然後用break A;可以跳出多重迴圈。(Java中支援帶標籤的break和continue語句,作用有點類似於C和C++中的goto語句,但是就像要避免使用goto一樣,應該避免使用帶標籤的break和continue,因為它不會讓你的程式變得更優雅,很多時候甚至有相反的作用,所以這種語法其實不知道更好)
5.兩個物件值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
不對,如果兩個物件x和y滿足x.equals(y) == true,它們的雜湊碼(hash code)應當相同。Java對於eqauls方法和hashCode方法是這樣規定的:(1)如果兩個物件相同(equals方法返回true),那麼它們的hashCode值一定要相同;(2)如果兩個物件的hashCode相同,它們並不一定相同。當然,你未必要按照要求去做,但是如果你違背了上述原則就會發現在使用容器時,相同的物件可以出現在Set集合中,同時增加新元素的效率會大大下降(對於使用雜湊儲存的系統,如果雜湊碼頻繁的衝突將會造成存取效能急劇下降)。
6.String 和StringBuilder、StringBuffer 的區別?
Java 平臺提供了兩種型別的字串:String和StringBuffer / StringBuilder,它們可以儲存和操作字串。其中String是隻讀字串,也就意味著String引用的字串內容是不能被改變的。而StringBuffer和StringBuilder類表示的字串物件可以直接進行修改。StringBuilder是JDK 1.5中引入的,它和StringBuffer的方法完全相同,區別在於它是在單執行緒環境下使用的,因為它的所有方面都沒有被synchronized修飾,因此它的效率也比StringBuffer略高。
7.過載(Overload)和重寫(Override)的區別。過載的方法能否根據返回型別進行區分?
方法的過載和重寫都是實現多型的方式,區別在於前者實現的是編譯時的多型性,而後者實現的是執行時的多型性。過載發生在一個類中,同名的方法如果有不同的引數列表(引數型別不同、引數個數不同或者二者都不同)則視為過載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回型別,比父類被重寫方法更好訪問,不能比父類被重寫方法宣告更多的異常(里氏代換原則)。過載對返回型別沒有特殊的要求。
8.抽象類(abstract class)和介面(interface)有什麼異同?
抽象類和介面都不能夠例項化,但可以定義抽象類和介面型別的引用。一個類如果繼承了某個抽象類或者實現了某個介面都需要對其中的抽象方法全部進行實現,否則該類仍然需要被宣告為抽象類。介面比抽象類更加抽象,因為抽象類中可以定義構造器,可以有抽象方法和具體方法,而介面中不能定義構造器而且其中的方法全部都是抽象方法。抽象類中的成員可以是private、預設、protected、public的,而介面中的成員全都是public的。抽象類中可以定義成員變數,而介面中定義的成員變數實際上都是常量。有抽象方法的類必須被宣告為抽象類,而抽象類未必要有抽象方法。
9.Java 中會存在記憶體洩漏嗎,請簡單描述。
理論上Java因為有垃圾回收機制(GC)不會存在記憶體洩露問題(這也是Java被廣泛使用於伺服器端程式設計的一個重要原因);然而在實際開發中,可能會存在無用但可達的物件,這些物件不能被GC回收也會發生記憶體洩露。一個例子就是hibernate的Session(一級快取)中的物件屬於持久態,垃圾回收器是不會回收這些物件的,然而這些物件中可能存在無用的垃圾物件。
10.靜態變數和例項變數的區別?
靜態變數是被static修飾符修飾的變數,也稱為類變數,它屬於類,不屬於類的任何一個物件,一個類不管建立多少個物件,靜態變數在記憶體中有且僅有一個拷貝;例項變數必須依存於某一例項,需要先建立物件然後通過物件才能訪問到它。靜態變數可以實現讓多個物件共享記憶體。在Java開發中,上下文類和工具類中通常會有大量的靜態成員。
11.如何實現物件克隆?
有兩種方式:
1.實現Cloneable介面並重寫Object類中的clone()方法;
2.實現Serializable介面,通過物件的序列化和反序列化實現克隆,可以實現真正的深度克隆。
12.String s=new String(“xyz”);建立了幾個字串物件?
兩個物件,一個是靜態儲存區的”xyz”,一個是用new建立在堆上的物件。
13.Java 中的final關鍵字有哪些用法?
(1)修飾類:表示該類不能被繼承;
(2)修飾方法:表示方法不能被重寫;
(3)修飾變數:表示變數只能一次賦值以後值不能被修改(常量)。
14.Error 和Exception 有什麼區別?
Error 表示系統級的錯誤和程式不必處理的異常,是恢復不是不可能但很困難的情況下的一種嚴重問題;比如記憶體溢位,不可能指望程式能處理這樣的情況;Exception 表示需要捕捉或者需要程式進行處理的異常,是一種設計或實現問題;也就是說,它表示如果程式執行正常,從不會發生的情況。
15.try{}裡有一個return語句,那麼緊跟在這個try後的finally{}裡的code會不會被執行,什麼時候被執行,在return前還是後?
會執行,在方法返回呼叫者前執行。Java允許在finally中改變返回值的做法是不好的,因為如果存在finally程式碼塊,try中的return語句不會立馬返回呼叫者,而是記錄下返回值待finally程式碼塊執行完畢之後再向呼叫者返回其值,然後如果在finally中修改了返回值,這會對程式造成很大的困擾,C#中就從語法上規定不能做這樣的事。
16.Java 語言如何進行異常處理?
Java 的異常處理是通過5 個關鍵詞來實現的:try、catch、throw、throws和finally。一般情況下是用try來執行一段程式,如果出現異常,系統會丟擲(throw)一個異常,這時候你可以通過它的型別來捕捉(catch)它,或最後(finally)由預設處理器來處理;try用來指定一塊預防所有“異常”的程式;catch 子句緊跟在try塊後面,用來指定你想要捕捉的“異常”的型別;throw 語句用來明確地丟擲一個“異常”;throws用來標明一個成員函式可能丟擲的各種“異常”;finally 為確保一段程式碼不管發生什麼“異常”都被執行一段程式碼。每當遇到一個try 語句,“異常”的框架就放到棧上面,直到所有的try語句都完成。如果下一級的try語句沒有對某種“異常”進行處理,棧就會展開,直到遇到有處理這種“異常”的try 語句。
17.列出一些你常見的執行時異常?
ArithmeticException(算術異常)
ClassCastException (類轉換異常)
IllegalArgumentException (非法引數異常)
IndexOutOfBoundsException (下表越界異常)
NullPointerException (空指標異常)
SecurityException (安全異常)
18.final, finally, finalize 的區別?
final:修飾符(關鍵字)有三種用法:如果一個類被宣告為final,意味著它不能再派生出新的子類,即不能被繼承,因此它和abstract是反義詞。將變數宣告為final,可以保證它們在使用中不被改變,被宣告為final 的變數必須在宣告時給定初值,而在以後的引用中只能讀取不可修改。被宣告為final 的方法也同樣只能使用,不能在子類中被重寫。finally:通常放在try…catch的後面構造總是執行程式碼塊,這就意味著程式無論正常執行還是發生異常,這裡的程式碼只要JVM不關閉都能執行,可以將釋放外部資源的程式碼寫在finally塊中。finalize:Object類中定義的方法,Java中允許使用finalize() 方法在垃圾收集器將物件從記憶體中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在銷燬物件時呼叫的,通過重寫finalize() 方法可以整理系統資源或者執行其他清理工作。
19.ArrayList、Vector、LinkedList 的儲存效能和特性?
ArrayList 和Vector都是使用陣列方式儲存資料,此陣列元素數大於實際儲存的資料以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及陣列元素移動等記憶體操作,所以索引資料快而插入資料慢,Vector由於使用了synchronized 方法(執行緒安全),通常效能上較ArrayList 差,而LinkedList 使用雙向連結串列實現儲存,按序號索引資料需要進行前向或後向遍歷,但是插入資料時只需要記錄本項的前後項即可,所以插入速度較快。Vector屬於遺留容器,現在已經不推薦使用,但是由於ArrayList和LinkedListed都是非執行緒安全的,如果需要多個執行緒操作同一個容器,那麼可以通過工具類Collections中的synchronizedList方法將其轉換成執行緒安全的容器後再使用。
20.Collection 和Collections 的區別?
Collection 是一個介面,它是Set、List等容器的父介面;Collections 是個一個工具類,提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜尋、排序、執行緒安全化等等。
21.List、Map、Set 三個介面,存取元素時,各有什麼特點?
List以特定索引來存取元素,可有重複元素。Set不能存放重複元素(用物件的equals()方法來區分元素是否重複)。Map儲存鍵值對(key-value pair)對映,對映關係可以是一對一或多對一。Set和Map容器都有基於雜湊儲存和排序樹的兩種實現版本,基於雜湊儲存的版本理論存取時間複雜度為O(1),而基於排序樹版本的實現在插入或刪除元素時會按照元素或元素的鍵(key)構成排序樹從而達到排序和去重的效果。
22.TreeMap和TreeSet在排序時如何比較元素?Collections工具類中的sort()方法如何比較元素?
TreeSet要求存放的物件所屬的類必須實現Comparable介面,該介面提供了比較元素的compareTo()方法,當插入元素時會回撥該方法比較元素的大小。TreeMap要求存放的鍵值對對映的鍵必須實現Comparable介面從而根據鍵對元素進行排序。Collections工具類的sort方法有兩種過載的形式,第一種要求傳入的待排序容器中存放的物件比較實現Comparable介面以實現元素的比較;第二種不強制性的要求容器中的元素必須可比較,但是要求傳入第二個引數,引數是Comparator介面的子型別(需要重寫compare方法實現元素的比較),相當於一個臨時定義的排序規則,其實就是是通過介面注入比較元素大小的演算法,也是對回撥模式的應用。
23.sleep()和wait()有什麼區別?
sleep()方法是執行緒類(Thread)的靜態方法,導致此執行緒暫停執行指定時間,將執行機會給其他執行緒,但是監控狀態依然保持,到時後會自動恢復(執行緒回到就緒(ready)狀態),因為呼叫sleep 不會釋放物件鎖。wait()是Object 類的方法,對此物件呼叫wait()方法導致本執行緒放棄物件鎖(執行緒暫停執行),進入等待此物件的等待鎖定池,只有針對此物件發出notify 方法(或notifyAll)後本執行緒才進入物件鎖定池準備獲得物件鎖進入就緒狀態。
24.sleep()和yield()有什麼區別?
① sleep()方法給其他執行緒執行機會時不考慮執行緒的優先順序,因此會給低優先順序的執行緒以執行的機會;yield()方法只會給相同優先順序或更高優先順序的執行緒以執行的機會;
② 執行緒執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態;
③ sleep()方法宣告丟擲InterruptedException,而yield()方法沒有宣告任何異常;
④ sleep()方法比yield()方法(跟作業系統相關)具有更好的可移植性。
25.與執行緒同步相關的方法
wait():使一個執行緒處於等待(阻塞)狀態,並且釋放所持有的物件的鎖;
sleep():使一個正在執行的執行緒處於睡眠狀態,是一個靜態方法,呼叫此方法要捕捉InterruptedException 異常;
notify():喚醒一個處於等待狀態的執行緒,當然在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,而是由JVM確定喚醒哪個執行緒,而且與優先順序無關;
notityAll():喚醒所有處入等待狀態的執行緒,注意並不是給所有喚醒執行緒一個物件的鎖,而是讓它們競爭。
26.synchronized關鍵字的用法?
synchronized關鍵字可以將物件或者方法標記為同步,以實現對物件和方法的互斥訪問,可以用synchronized(物件) { … }定義同步程式碼塊,或者在宣告方法時將synchronized作為方法的修飾符。在第60題的例子中已經展示了synchronized關鍵字的用法。
27.啟動一個執行緒是用run()還是start()方法?
啟動一個執行緒是呼叫start()方法,使執行緒所代表的虛擬處理機處於可執行狀態,這意味著它可以由JVM 排程並執行,這並不意味著執行緒就會立即執行。run()方法是執行緒啟動後要進行回撥(callback)的方法。
28.什麼是執行緒池(thread pool)?
在面向物件程式設計中,建立和銷燬物件是很費時間的,因為建立一個物件要獲取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機器將試圖跟蹤每一個物件,以便能夠在物件銷燬後進行垃圾回收。所以提高服務程式效率的一個手段就是儘可能減少建立和銷燬物件的次數,特別是一些很耗資源的物件建立和銷燬,這就是”池化資源”技術產生的原因。執行緒池顧名思義就是事先建立若干個可執行的執行緒放入一個池(容器)中,需要的時候從池中獲取執行緒不用自行建立,使用完畢不需要銷燬執行緒而是放回池中,從而減少建立和銷燬執行緒物件的開銷。
29.執行緒的基本狀態以及狀態之間的關係?
除去起始(new)狀態和結束(finished)狀態,執行緒有三種狀態,分別是:就緒(ready)、執行(running)和阻塞(blocked)。其中就緒狀態代表執行緒具備了執行的所有條件,只等待CPU排程(萬事俱備,只欠東風);處於執行狀態的執行緒可能因為CPU排程(時間片用完了)的原因回到就緒狀態,也有可能因為呼叫了執行緒的yield方法回到就緒狀態,此時執行緒不會釋放它佔有的資源的鎖,坐等CPU以繼續執行;執行狀態的執行緒可能因為I/O中斷、執行緒休眠、呼叫了物件的wait方法而進入阻塞狀態(有的地方也稱之為等待狀態);而進入阻塞狀態的執行緒會因為休眠結束、呼叫了物件的notify方法或notifyAll方法或其他執行緒執行結束而進入就緒狀態。注意:呼叫wait方法會讓執行緒進入等待池中等待被喚醒,notify方法或notifyAll方法會讓等待鎖中的執行緒從等待池進入等鎖池,在沒有得到物件的鎖之前,執行緒仍然無法獲得CPU的排程和執行。
30.Java中如何實現序列化
序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決物件流讀寫操作時可能引發的問題(如果不進行序列化可能會存在資料亂序的問題)。
要實現序列化,需要讓一個類實現Serializable介面,該介面是一個標識性介面,標註該類物件是可被序列化的,然後使用一個輸出流來構造一個物件輸出流並通過writeObject(Object obj)方法就可以將實現物件寫出(即儲存其狀態);如果需要反序列化則可以用一個輸入流建立物件輸入流,然後通過readObject方法從流中讀取物件。序列化除了能夠實現物件的持久化之外,還能夠用於物件的深度克隆。