陷阱 - 捕獲 Throwable 異常錯誤或 RuntimeException

對於沒有經驗的 Java 程式設計師的一個常見的思維模式是異常情況是有問題負擔,並處理這個最好的辦法是一網打盡 1 ,儘快。這導致程式碼如下:

....
try {
    InputStream is = new FileInputStream(fileName);
    // process the input
} catch (Exception ex) {
    System.out.println("Could not open file " + fileName);
}

上面的程式碼有一個重大缺陷。catch 實際上會捕獲比程式設計師期望的更多異常。假設 fileName 的值是 null,由於應用程式中的其他地方的錯誤。這將導致 FileInputStream 建構函式丟擲 NullPointerException。處理程式將捕獲此資訊,並向使用者報告:

    Could not open file null

這是無益和混亂的。更糟糕的是,假設它是處理輸入程式碼引發意外異常(已檢查或未選中!)。現在,使用者將獲得針對開啟檔案時未發生的問題的誤導性訊息,並且可能與 I / O 完全無關。

問題的根源是程式設計師為 Exception 編寫了一個處理程式。這幾乎總是一個錯誤:

  • 捕獲 Exception 將捕獲所有已檢查的異常,以及大多數未經檢查的異常。
  • 捕獲 RuntimeException 將捕獲大多數未經檢查的異常。
  • 捕獲 Error 將捕獲未經檢查的異常,這些異常表示 JVM 內部錯誤。這些錯誤通常無法恢復,因此不應被捕獲。
  • 捕獲 Throwable 將捕獲所有可能的異常。

捕獲過多一組異常的問題是處理程式通常無法正確處理所有異常。在 Exception 等的情況下,程式設計師很難預測可以捕獲的內容; 即期待什麼。

一般情況下,正確的解決辦法是處理該異常丟擲。例如,你可以捕獲它們並在原地處理它們:

try {
    InputStream is = new FileInputStream(fileName);
    // process the input
} catch (FileNotFoundException ex) {
    System.out.println("Could not open file " + fileName);
}

或者你可以通過封閉方法將它們宣告為 thrown

捕捉 Exception 是合適的情況很少。通常出現的唯一一個是這樣的:

public static void main(String[] args) {
    try {
        // do stuff
    } catch (Exception ex) {
        System.err.println("Unfortunately an error has occurred. " +
                           "Please report this to X Y Z");
        // Write stacktrace to a log file.
        System.exit(1);
    }
}

在這裡,我們真正想要處理所有異常,所以捕捉 Exception(甚至 Throwable)是正確的。

1 - 也稱為 Pokemon 異常處理