Java 異常層次結構 - 未選中和已檢查的異常

所有 Java 異常都是 Exception 類層次結構中的類的例項。這可以表示如下:

  • java.lang.Throwable - 這是所有異常類的基類。它的方法和建構函式實現了所有異常共有的一系列功能。

筆記:

  1. 檢查未檢查的異常之間的區別如下所述。
  2. ThrowableExceptionRuntimeException 類應該被視為 abstract; 看到陷阱 - 投擲 Throwable,Exception,Error 或 RuntimeException
  3. 在應用程式嘗試恢復不安全或不明智的情況下,JVM 會丟擲 Error 異常。
  4. 宣告 Throwable 的自定義子型別是不明智的。Java 工具和庫可以假設 ErrorExceptionThrowable 的唯一直接子型別,如果該假設不正確則行為不端。

已檢查與未經檢查的異常情況

某些程式語言中對異常支援的批評之一是很難知道給定方法或過程可能丟擲哪些異常。鑑於未處理的異常可能導致程式崩潰,這可能會使異常成為脆弱性的來源。

Java 語言通過已檢查的異常機制解決了這個問題。首先,Java 將異常分為兩類:

  • 檢查的異常通常表示應用程式應該能夠處理的預期事件。例如,IOException 及其子型別表示 I / O 操作中可能發生的錯誤情況。示例包括,檔案開啟失敗,因為檔案或目錄不存在,網路讀取和寫入失敗,因為網路連線已斷開等等。

  • 未經檢查的異常通常表示應用程式無法處理的意外事件。這些通常是應用程式中的錯誤的結果。

(在下文中,丟擲是指顯式丟擲的任何異常(通過 throw 語句),或隱式丟擲(在失敗的解除引用中,型別轉換等)。類似地,傳播是指丟擲的異常巢狀呼叫,並沒有在該呼叫中捕獲。下面的示例程式碼將說明這一點。)

檢查異常機制的第二部分是對可能發生檢查異常的方法有限制:

  • 當在方法中丟擲或傳播檢查的異常時,它必須被方法捕獲,或者在方法的 throws 子句中列出。 ( 本例中描述了 throws 子句的重要性。)
  • 當在初始化程式塊中丟擲或傳播已檢查的異常時,必須將其捕獲到該塊。
  • 在欄位初始化表示式中,方法呼叫無法傳播已檢查的異常。 (沒有辦法捕獲這樣的異常。)

簡而言之,必須處理或宣告已檢查的異常。

這些限制不適用於未經檢查的異常。這包括隱式丟擲異常的所有情況,因為所有這些情況都會丟擲未經檢查的異常。

檢查異常示例

這些程式碼段旨在說明已檢查的異常限制。在每種情況下,我們都會顯示帶有編譯錯誤的程式碼版本,以及更正錯誤的第二個版本。

// This declares a custom checked exception.
public class MyException extends Exception {
    // constructors omitted.
}

// This declares a custom unchecked exception.
public class MyException2 extends RuntimeException {
    // constructors omitted.
}

第一個示例顯示如果不應在方法中處理它們,如何將顯式丟擲的已檢查異常宣告為丟擲

// INCORRECT
public void methodThrowingCheckedException(boolean flag) {
    int i = 1 / 0;                // Compiles OK, throws ArithmeticException
    if (flag) {
        throw new MyException();  // Compilation error
    } else {
        throw new MyException2(); // Compiles OK
    }
}

// CORRECTED
public void methodThrowingCheckedException(boolean flag) throws MyException {
    int i = 1 / 0;                // Compiles OK, throws ArithmeticException
    if (flag) {
        throw new MyException();  // Compilation error
    } else {
        throw new MyException2(); // Compiles OK
    }
}

第二個示例顯示瞭如何處理傳播的已檢查異常。

// INCORRECT 
public void methodWithPropagatedCheckedException() {
    InputStream is = new FileInputStream("someFile.txt");  // Compilation error
    // FileInputStream throws IOException or a subclass if the file cannot 
    // be opened.  IOException is a checked exception.
    ...
}

// CORRECTED (Version A) 
public void methodWithPropagatedCheckedException() throws IOException {
    InputStream is = new FileInputStream("someFile.txt"); 
    ...
}

// CORRECTED (Version B) 
public void methodWithPropagatedCheckedException() {
    try {
        InputStream is = new FileInputStream("someFile.txt"); 
        ...
    } catch (IOException ex) {
        System.out.println("Cannot open file: " + ex.getMessage());
    }
}

最後一個示例顯示瞭如何在靜態欄位初始值設定項中處理已檢查的異常。

// INCORRECT
public class Test {
    private static final InputStream is = 
            new FileInputStream("someFile.txt");  // Compilation error
}

// CORRECTED
public class Test {
    private static final InputStream is;
    static {
        InputStream tmp = null;
        try {
            tmp = new FileInputStream("someFile.txt");
        } catch (IOException ex) {
            System.out.println("Cannot open file: " + ex.getMessage());
        }
        is = tmp;
    }
}

請注意,在最後一種情況下,我們還必須處理 is 不能多次分配的問題,而且即使在異常的情況下也必須分配。