1. 程式人生 > >Java的Exception和Error面試題

Java的Exception和Error面試題

在Java核心知識的面試中,你總能碰到關於 處理Exception和Error的面試題。Exception處理是Java應用開發中一個非常重要的方面,也是編寫強健 而穩定的Java程式的關鍵,這自然使它成為面試題中的常客。關於Java中Error和Exception的面試題目多是關於Exception和 Error的概念,如何處理Exception,以及 處理Exception時需要遵守的最佳實踐等 等。儘管關於多執行緒、垃圾回收、JVM概念和麵向物件設計等方面的問題依然主宰著這類面試,你仍然需要為回答“如何有效的處理錯誤”做準備。一些面試官也 會測試程式設計師的除錯技巧,因為快速的處理異常是另一個重要的Java程式設計技巧。如果一個程式設計師對於不常見且難於處理的 ClassNotFoundException或 OutOfMemoryError非常熟悉,則很有可能他擁有著良好的實戰經驗。本文中,我們將會看到在Java  J2EE面試中,初學者,有經驗者和高階Java開發者會被問到的一些關於Java Error和Exception的面試題。

JAVA 中Exception和Error 面試問題

下面是我個人總結的在Java和J2EE開發者在面試中經常被問到的有關Exception和Error的知識。在分享我的回答的時候,我也給這些問題作 了快速修訂,並且提供原始碼以便深入理解。我總結了各種難度的問題,適合新手碼農和高階Java碼農。如果你遇到了我列表中沒有的問題,並且這個問題非常 好,請在下面評論中分享出來。你也可以在評論中分享你面試時答錯的情況。

1) Java中什麼是Exception?

這個問題經常在第一次問有關異常的時候或者是面試菜鳥的時候問。我從來沒見過面高階或者資深工程師的時候有人問這玩意,但是對於菜鳥,是很願意問這個的。 簡單來說,異常是Java傳達給你的系統和程式錯誤的方式。在java中,異常功能是通過實現比如 Throwable,Exception,RuntimeException之類的類,然後還有一些處理異常時候的關鍵字,比如 throw,throws,try,catch,finally之類的。 所有的異常都是通過Throwable衍生出來的。Throwable把錯誤進 一步劃分為 java.lang.Exception 和 java.lang.Error.  java.lang.Error 用來處理系統錯誤,例如java.lang.StackOverFlowError 或者 Java.lang.OutOfMemoryError 之類的。然後 Exception用來處理程式錯誤,請求的資源不可用等等。

2) Java中的檢查型異常和非檢查型異常有什麼區別?

這又是一個非常流行的Java異常面試題,會出現在各種層次的Java面試中。檢查型異常和非檢查型異常的主要區別在於其處理方式。檢查型異常需要使用 try, catch和finally關鍵字在編譯期進行處理,否則會出現編譯器會報錯。對於非檢查型異常則不需要這樣做。Java中所有繼承自 java.lang.Exception類的異常都是檢查型異常,所有繼承自RuntimeException的異常都被稱為非檢查型異常。你也可以檢視 下一篇文章來了解 更多關於檢查型異常和非檢查型異常之間的區別。

3) Java中的NullPointerException和ArrayIndexOutOfBoundException之間有什麼相同之處?

在Java異常面試中這並不是一個很流行的問題,但會出現在不同層次的初學者面試中,用來測試應聘者對檢查型異常和非檢查型異常的概念是否熟悉。順便說一 下,該題的答案是,這兩個異常都是非檢查型異常,都繼承自RuntimeException。該問題可能會引出另一個問題,即Java和C的陣列有什麼不 同之處,因為C裡面的陣列是沒有大小限制的,絕對不會丟擲ArrayIndexOutOfBoundException。

4)在Java異常處理的過程中,你遵循的那些最好的實踐是什麼?

這個問題在面試技術經理是非常常見的一個問題。因為異常處理在專案設計中是非常關鍵的,所以精通異常處理是十分必要的。異常處理有很多最佳實踐,下面列舉集中,它們提高你程式碼的健壯性和靈活性:

1) 呼叫方法的時候返回布林值來代替返回null,這樣可以 NullPointerException。由於空指標是java異常裡最噁心的異常,你可以參考一下下面的技術文章 coding best practices to minimize NullPointerException。去看看裡面具體的例子。

2) catch塊裡別不寫程式碼。空catch塊是異常處理裡的錯誤事件,因為它只是捕獲了異常,卻沒有任何處理或者提示。通常你起碼要打印出異常資訊,當然你最好根據需求對異常資訊進行處理。

3)能拋受控異常(checked Exception)就儘量不拋受非控異常(checked Exception)。通過去掉重複的異常處理程式碼,可以提高程式碼的可讀性。

4) 絕對不要讓你的資料庫相關異常顯示到客戶端。由於絕大多數資料庫和SQLException異常都是受控異常,在Java中,你應該在DAO層把異常資訊處理,然後返回處理過的能讓使用者看懂並根據異常提示資訊改正操作的異常資訊。

5) 在Java中,一定要在資料庫連線,資料庫查詢,流處理後,在finally塊中呼叫close()方法。我已經在我的文章Top 10 Java exception handling best practices中分享了關於這方面的很多知識,你們也可以看看這篇文章。

5) 既然我們可以用RuntimeException來處理錯誤,那麼你認為為什麼Java中還存在檢查型異常?

這是一個有爭議的問題,在回答該問題時你應當小心。雖然他們肯定願意聽到你的觀點,但其實他們最感興趣的還是有說服力的理由。我認為其中一個理由是,存在 檢查型異常是一個設計上的決定,受到了諸如C++等比Java更早的程式語言設計經驗的影響。絕大多數檢查型異常位於java.io包內,這是合乎情理 的,因為在你請求了不存在的系統資源的時候,一段強壯的程式必須能夠優雅的處理這種情況。通過把IOException宣告為檢查型異常,Java 確保了你能夠優雅的對異常進行處理。另一個可能的理由是,可以使用catch或finally來確保數量受限的系統資源(比如檔案描述符)在你使用後儘早 得到釋放。 Joshua Bloch編寫的Effective Java 一書 中多處涉及到了該話題,值得一讀。

6)  throw 和 throws這兩個關鍵字在java中有什麼不同?

一個java初學者應該掌握的面試問題。 throw 和 throws乍看起來是很相似的尤其是在你還是一個java初學者的時候。儘管他們看起來相似,都是在處理異常時候使用到的。但在程式碼裡的使用方法和用到的地方是不同的。throws 總是出現在一個函式頭中,用來標明該成員函式可能丟擲的各種異常, 你也可以申明未檢查的異常,但這不是編譯器強制的。如果方法丟擲了異常那麼呼叫這個方法的時候就需要將這個異常處理。另一個關鍵字  throw 是用來丟擲任意異常的,按照語法你可以丟擲任意 Throwable (i.e. Throwable 或任何Throwable的衍生類) , throw可以中斷程式執行,因此可以用來代替return . 最常見的例子是用 throw 在一個空方法中需要return的地方丟擲 UnSupportedOperationException 程式碼如下 :

  1. privatestaticvoid show() {    
  2.      thrownew UnsupportedOperationException("Not yet implemented"); 
  3.  }  

7) 什麼是“異常鏈”?

“異常鏈”是Java中非常流行的異常處理概念,是指在進行一個異常處理時丟擲了另外一個異常,由此產生了一個異常鏈條。該技術大多用於將“ 受檢查異常” ( checked exception)封裝成為“非受檢查異常”(unchecked exception)或者RuntimeException。順便說一下,如果因為因為異常你決定丟擲一個新的異常,你一定要包含原有的異常,這樣,處理 程式才可以通過getCause()和initCause()方法來訪問異常最終的根源。

8) 你曾經自定義實現過異常嗎?怎麼寫的?

很顯然,我們絕大多數都寫過自定義或者業務異常,像AccountNotFoundException。在面試過程中詢問這個Java異常問題的主要原因 是去發現你如何使用這個特性的。這可以更準確和精緻的去處理異常,當然這也跟你選擇checked 還是unchecked exception息息相關。通過為每一個特定的情況建立一個特定的異常,你就為呼叫者更好的處理異常提供了更好的選擇。相比通用異常(general exception),我更傾向更為精確的異常。大量的建立自定義異常會增加專案class的個數,因此,在自定義異常和通用異常之間維持一個平衡是成功 的關鍵。

9) JDK7中對異常處理做了什麼改變?

這是最近新出的Java異常處理的面試題。JDK7中對錯誤(Error)和異常(Exception)處理主要新增加了2個特性,一是在一個catch 塊中可以出來多個異常,就像原來用多個catch塊一樣。另一個是自動化資源管理(ARM), 也稱為try-with-resource塊。這2個特性都可以在處理異常時減少程式碼量,同時提高程式碼的可讀性。對於這些特性瞭解,不僅幫助開發者寫出更 好的異常處理的程式碼,也讓你在面試中顯的更突出。我推薦大家讀一下Java 7攻略,這樣可以更深入的瞭解這2個非常有用的特性。

10) 你遇到過 OutOfMemoryError 錯誤嘛?你是怎麼搞定的?

這個面試題會在面試高階程式設計師的時候用,面試官想知道你是怎麼處理這個危險的OutOfMemoryError錯誤 的。必須承認的是,不管你做什麼專案,你都會碰到這個問題。所以你要是說沒遇到過,面試官肯定不會買賬。要是你對這個問題不熟悉,甚至就是沒碰到過,而你 又有3、4年的Java經驗了,那麼準備好處理這個問題吧。在回答這個問題的同時,你也可以藉機向面試秀一下你處理記憶體洩露、調優和除錯方面的牛逼技能。 我發現掌握這些技術的人都能給面試官留下深刻的印象。你們也可以到how to fix java.lang.OutOfMemoryError去看看我寫的另一篇關於這個問題更詳細細節的文章

11) 如果執行finally程式碼塊之前方法返回了結果,或者JVM退出了,finally塊中的程式碼還會執行嗎?

這個問題也可以換個方式問:“如果在try或者finally的程式碼塊中呼叫了System.exit(),結果會是怎樣”。瞭解finally塊是怎麼 執行的,即使是try裡面已經使用了return返回結果的情況,對了解Java的異常處理都非常有價值。只有在try裡面是有 System.exit(0)來退出JVM的情況下finally塊中的程式碼才不會執行。

12)Java中final,finalize,finally關鍵字的區別

這是一個經典的Java面試題了。我的一個朋友為Morgan Stanley招電信方面的核心Java開發人員的時候就問過這個問題。final和finally是Java的關鍵字,而finalize則是方法。 final關鍵字在建立不可變的類的時候非常有用,只是宣告這個類是final的。而finalize()方法則是垃圾回收器在回收一個物件前呼叫,但也 Java規範裡面沒有保證這個方法一定會被呼叫。finally關鍵字是唯一一個和這篇文章討論到的異常處理相關的關鍵字。在你的產品程式碼中,在關閉連線 和資原始檔的是時候都必須要用到finally塊。更多看here

13)下面的程式碼都有哪些錯誤:

  1. publicstaticvoid start() throws IOException, RuntimeException{ 
  2.    thrownew RuntimeException("Not able to Start"); 
  3. publicstaticvoid main(String args[]) { 
  4.    try { 
  5.          start(); 
  6.    } catch (Exception ex) { 
  7.            ex.printStackTrace(); 
  8.    } catch (RuntimeException re) { 
  9.            re.printStackTrace(); 
  10.    } 

這段程式碼會在捕捉異常程式碼塊的RuntimeException型別變數“re”裡丟擲編譯異常錯誤。因為Exception是 RuntimeException的超類,在start方法中所有的RuntimeException會被第一個捕捉異常塊捕捉,這樣就無法到達第二個捕 捉塊,這就是丟擲“exception java.lang.RuntimeException has already been caught”的編譯錯誤原因。

14)下面的Java程式碼都有哪些錯誤:

  1. publicclass SuperClass {   
  2.     publicvoid start() throws IOException{ 
  3.         thrownew IOException("Not able to open file"); 
  4.     } 
  5. publicclass SubClass extends SuperClass{   
  6.     publicvoid start() throws Exception{ 
  7.         thrownew Exception("Not able to start"); 
  8.     } 

這段程式碼編譯器將對子類覆蓋start方法產生不滿。因為每個Java中方法的覆蓋是有規則的,一個覆蓋的方法不能丟擲的異常比原方法繼承關係高。因為這 裡的start方法在超類中丟擲了IOException,所有在子類中的start方法只能丟擲要麼是IOExcepition或是其子類,但不能是其 超類,如Exception。

15)下面的Java異常程式碼有什麼錯誤:

  1. publicstaticvoid start(){ 
  2.    System.out.println("Java Exception interivew question Answers for Programmers"); 
  3. publicstaticvoid main(String args[]) { 
  4.    try
  5.       start(); 
  6.    }catch(IOException ioe){ 
  7.       ioe.printStackTrace(); 
  8.    } 

上面的Java異常例子程式碼中,編譯器將在處理IOException時報錯,因為IOException是受檢查異常,而start方法並沒有丟擲 IOException,所以編譯器將丟擲“異常, java.io.IOException 不會在try語句體中丟擲”,但是如果你將IOException改為Exception,編譯器報錯將消失,因為Exception可以用來捕捉所有運 行時異常,這樣就不需要宣告丟擲語句。我喜歡這樣帶有迷惑性的Java異常面試題,因為它不會讓人輕易的找出是IOException還是 Exception。你也可以在 Joshua Bloach和Neil Gafter的Java謎題中找到一些有關Java錯誤和異常的具有迷惑性問題。

我也經常看到在一些新人和有經驗的Java面試者遇到有關Java錯誤和異常的面試題。當然還有很多有關異常的問題我沒有涉及到,如果你有一些好的問題, 請告訴我,我將盡力在這個java異常問答系列中收錄這些問題。還有最後一個我留給夥伴們的問題是“為什麼Java異常被認為是比返回錯誤程式碼要好”,告 訴我你對於這裡的Java異常面試問答系列有什麼想法。