1. 程式人生 > 其它 >阿里二面面試題:請你說一下對受檢異常和非受檢異常的理解?

阿里二面面試題:請你說一下對受檢異常和非受檢異常的理解?

面試題: 請你說一下對受檢異常和非受檢異常的理解?

面試考察點

考察目的: 異常的設計,在程式開發中時非常重要的。好的異常設計能夠合理清晰的反饋程式的問題,提供排查思路。同時,還能夠很好的處理資源回收問題。所以作為有經驗的程式設計師,必須要了解異常,以及異常的差異和特性。

考察人群: 工作3年以上,3年左右一般都會參與專案中部分核心程式碼的編寫。

背景知識分享

在Java中,所有的異常都繼承自java.lang.Throwable,Throwable有兩個直接子類,Error和Exception,如圖所示。

Throwable 類是 Java 語言中所有錯誤(errors)異常(exceptions)

的父類。只有繼承於Throwable類或者其子類的異常才能夠被丟擲.

下面分別解釋一下這些異常以及特性。

Error錯誤

Error通常是程式無法處理的錯誤,這些錯誤大多數與程式碼編寫者執行的操作無關,並且它們是無法被捕獲的,因為它們在應用程式的控制和處理能力之外,比如。

  1. OutOfMemoryError, 記憶體溢位,比較常見的錯誤,是值記憶體空間不足以再提供新物件的分配。
  2. StackOverflowError,棧溢位。

以下是模擬程式中出現Error問題的例子。

  • 編寫一段使用記憶體儲存的程式

    public class ErrorException {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            /*迴圈建立物件,消耗堆記憶體*/
            for (int i= 0;i < 100000;i++){
                list.add(new String("Hello World"));
            }
        }
    }
    
  • 為了演示OOM錯誤,需要調整堆記憶體空間大小,新增VM option。

    把堆記憶體空間設定為1兆,這個記憶體空間非常小,所以很容易出現OOM錯誤。

    -Xmx1m
    
  • 執行上面這段程式,錯誤資訊如下.

    錯誤資訊中描述的是ArrayList在擴容時,發現堆記憶體空間不足,所以丟擲OOM錯誤。

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at java.util.Arrays.copyOf(Arrays.java:3210)
    	at java.util.Arrays.copyOf(Arrays.java:3181)
    	at java.util.ArrayList.grow(ArrayList.java:267)
    	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
    	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
    	at java.util.ArrayList.add(ArrayList.java:464)
    	at org.example.cl02.ErrorException.main(ErrorException.java:12)
    

在JVM中,除了程式計數器外,其他區域:方法區(Method Area)虛擬機器棧(VM Stack)本地方法棧(Native Method Stack)堆(Heap) 都是可能發生OutOfMemoryError錯誤。

  • 虛擬機器棧:如果執行緒請求的棧深度大於虛擬機器棧所允許的深度,將會出現StackOverflowError異常;如果虛擬機器動態擴充套件無法申請到足夠的記憶體,將出現OutOfMemoryError
  • 本地方法棧和虛擬機器棧一樣。
  • 堆:Java 堆可以處於物理上不連續,邏輯上連續,就像我們的磁碟空間一樣,如果堆中沒有記憶體完成例項分配,並且堆無法擴充套件時,將會丟擲 OutOfMemoryError。
  • 方法區:方法區無法滿足記憶體分配需求時,將丟擲OutOfMemoryError異常。

出現Error型別的錯誤,

Exception

Exception 位於 java.lang 包下,它是一種頂級介面,繼承於 Throwable 類,Exception 類及其子類都是 Throwable 的組成條件。

Exception是執行時的錯誤,它通常是程式執行時出現的可以預料的異常,基本上都需要Catch,然後再進行相關處理。

從前面的類關係圖中可以看到,Exception有兩類異常的實現。

  1. RuntimeException,又稱為非受檢異常,這類異常不強制使用Catch捕獲,我們可以根據實際場景來判斷是否要Catch。
  2. CheckedException,又稱為受檢異常,這類異常必須顯示地通過Catch捕獲。

受檢異常和非受檢異常

Java規範中,對非受查異常和受查異常的定義是這樣的:

The unchecked exception classes are the run-time exception classes and the error classes.

The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are Throwable and all its subclasses other than RuntimeException and its subclasses and Errorand its subclasses.

也就是說,除了 RuntimeException 和其子類,以及error和其子類,其它的所有異常都是 checkedException

受檢異常的例項

受檢異常,是值需要顯示通過Catch捕獲的異常,在Java中,除了RuntimeException以外的異常,都屬於受檢異常(checkedException).

我們以NoSuchMethodException為例,如圖所示,可以明顯看到,該異常在沒有捕獲的情況下,會顯示提示語法錯誤,有兩個解決辦法

  1. Add exception to method signature,表示把這個異常再往上拋。
  2. Surround with try/catch,表示使用try/catch捕獲。

![image-20211101164826685](../../../../../../Library/Application Support/typora-user-images/image-20211101164826685.png)

其他常見的受檢異常:

  1. NoSuchFieldException,表示該類沒有指定名稱丟擲來的異常。
  2. IllegalAccessException,不允許訪問某個類的異常。
  3. ClassNotFoundException,類沒有找到丟擲異常。
  4. IOException,IO異常。
  5. NumberFormatException,數值型別的格式錯誤

受檢異常,之所以強制讓開發者進行捕獲,是因為呼叫者接收到該異常時,可以清晰的知道哪個地方出問題了,那麼呼叫者可以根據上下文來決定在異常時做何種處理。

比如IOException,出現該異常時,我們可以在Catch中對流資源進行釋放。

非受檢異常例項

非受檢異常,是指不需要呼叫者顯示捕獲的異常,RuntimeException以及其派生類都屬於非受檢異常。

同樣的程式碼,我們丟擲RuntimeException時,並沒有任何語法上的錯誤提示。

public class ErrorException {

    public static void main(String[] args) {
        throw new RuntimeException("Occur Exception");
    }
}

常見的非受檢異常有

  1. ArrayIndexOutOfBoundsException,陣列越界異常
  2. NullPointerException,空指標異常
  3. IllegalArgumentException,非法引數異常
  4. NegativeArraySizeException,陣列長度為負異常
  5. IllegalStateException,非法狀態異常
  6. ClassCastException,型別轉換異常

總結:受檢的異常和非受檢的異常之間最大的區別在於,受檢的異常是由編譯器強制執行的,用於指示不受程式控制的異常情況(例如,I/O 錯誤),而非受檢的異常在執行時發生,用於指示程式設計錯誤(例如,空指標)

Java異常的最佳實踐

  1. 當被呼叫的某個方法服務執行其本身的功能含義是,可以使用受檢異常。

  2. 理想情況下,絕對不應將受檢異常用於程式設計錯誤,在這種情況下,絕對不能把資源錯誤用於程式流控制。

  3. 儘量不要只捕獲java.lang.Exception這種太過於泛的異常型別,應該要捕獲到具體的錯誤型別。比如InterruptedException,原因有兩個

    1. 在多人協作開發時,別人可以通過這些程式碼很清晰的理解我們的程式。並且告訴別人更多的資訊。
    2. 我們必須要保證程式不會捕捉到不再我們預期範圍內的異常,比如RuntimeException,我們希望這類異常是要往外拋,而不是在內部被捕獲。
  4. 不要把異常吞掉,因為一旦程式出現問題,沒有異常資訊很難定位。

  5. 如果希望呼叫者能夠從異常中進行合理恢復,需要設定為受檢異常型別,如果呼叫者無法採用任何措施使得程式無法重異常中恢復,需要把該異常設定為非受檢異常。

擴充套件:一道經典的面試題

一道非常經典的面試題,NoClassDefFoundError 和 ClassNotFoundException 有什麼區別

  1. NoClassDefFoundError,表示這個類在編譯時期存在,但是在執行時不能找到合適的類導致的錯誤。例如在執行時我們想呼叫某個類的方法或者訪問這個類的靜態成員的時候,發現這個類不可用,此時Java虛擬機器就會丟擲NoClassDefFoundError錯誤。

    可能出現的錯誤情況如下:

    1. 對應的Class在java的classpath中不可用
    2. 你可能用jar命令執行你的程式,但類並沒有在jar檔案的manifest檔案中的classpath屬性中定義
    3. 可能程式的啟動指令碼覆蓋了原來的classpath環境變數
    4. 因為NoClassDefFoundError是java.lang.LinkageError的一個子類,所以可能由於程式依賴的原生的類庫不可用而導致
    5. 檢查日誌檔案中是否有java.lang.ExceptionInInitializerError這樣的錯誤,NoClassDefFoundError有可能是由於靜態初始化失敗導致的
    6. 如果你工作在J2EE的環境,有多個不同的類載入器,也可能導致NoClassDefFoundError
  2. ClassNotFoundException,它是程式執行期間的異常,比如當我們嘗試在執行時使用反射載入類時,ClassNotFoundException 就會出現。

    @CallerSensitive
    public static Class<?> forName(String className)
      throws ClassNotFoundException {
      Class<?> caller = Reflection.getCallerClass();
      return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
    

總的來說,ClassNotFoundException 和 NoClassDefFoundError 都是由 CLASSPATH 中缺少類引起的,通常是由於缺少 JAR 檔案而引起的,但是如果 JVM 認為應用執行時找不到相應的引用,就會丟擲 NoClassDefFoundError 錯誤;當你在程式碼中顯示的載入類比如 Class.forName() 呼叫時卻沒有找到相應的類,就會丟擲 java.lang.ClassNotFoundException

問題解答

面試題: 請你說一下對受檢異常和非受檢異常的理解?

回答: 受檢異常和非受檢異常,都是派生自Throwable這個類。他們的區別是

受檢異常: 是指需要呼叫者顯示通過try-catch捕獲的異常

非受檢異常: 是指不需要呼叫者顯示捕獲的異常。

之所以要定義受檢異常和非受檢異常,是因為在程式中,存在一些需要使用者在編譯期間就去檢查的問題,比如FileNotFoundException、IOException,這些異常涉及資源處理,呼叫者需要捕獲,其實它可以提醒開發者,如果被呼叫的方法出現這類異常時,程式應該做好預判並處理,比如IOExcetion,我們需要對流進行關閉操作。

而非受檢發生在執行期間,是程式執行過程中可能發生的錯誤型別,比如NullpointExcetpion,這些異常我們可以捕獲,也可以不捕獲。但是捕獲這些異常只能列印一些日誌,除此之外什麼都做不了

總結和思考

關於異常模型的設計,有一篇非常好的文章。

http://joeduffyblog.com/2016/02/07/the-error-model/

大家有空可以去了解一下。

關注[跟著Mic學架構]公眾號,獲取更多精品原創