陷阱 - 丟擲 Throwable 異常錯誤或 RuntimeException

雖然捕捉 ThrowableExceptionErrorRuntimeException 異常是壞事,但丟擲它們會更糟。

基本問題是,當你的應用程式需要處理異常時,頂級異常的存在使得很難區分不同的錯誤條件。例如

try {
    InputStream is = new FileInputStream(someFile);  // could throw IOException
    ...
    if (somethingBad) {
        throw new Exception();  // WRONG
    }
} catch (IOException ex) {
    System.err.println("cannot open ...");
} catch (Exception ex) {
    System.err.println("something bad happened");  // WRONG
}

問題是因為我們扔了一個 Exception 例項,我們被迫捕捉它。然而,如另一個例子所述,捕捉 Exception 是不好的。在這種情況下,很難區分如果 somethingBadtrue 而被丟擲的 Exception預期情況,以及我們實際捕獲未經檢查的異常(例如 NullPointerException)的意外情況。

如果允許傳播頂級異常,我們會遇到其他問題:

  • 我們現在必須記住我們丟擲頂級的所有不同原因,並區分/處理它們。
  • ExceptionThrowable 的情況下,如果我們希望傳播異常,我們還需要將這些異常新增到方法的 throws 子句中。這是有問題的,如下所述。

簡而言之,不要丟擲這些異常。丟擲一個更具體的異常,更恰當地描述已經發生的異常事件。如果需要,請定義並使用自定義異常類。

在方法的丟擲中宣告 Throwable 或 Exception 是有問題的

Exception 甚至`Throwable 替換方法的 throws 子句中的一長串丟擲異常是很誘人的。這是一個壞主意:

  1. 它強制呼叫者處理(或傳播)Exception
  2. 我們不能再依賴編譯器來告訴我們需要處理的特定已檢查異常。
  3. 正確處理 Exception 很困難。很難知道可能會捕獲哪些實際異常,如果你不知道可以捕獲什麼,則很難知道哪種恢復策略是合適的。
  4. 處理 Throwable 更加困難,因為現在你還必須應對可能永遠無法恢復的潛在失敗。

這個建議意味著應該避免某些其他模式。例如:

try {
    doSomething();
} catch (Exception ex) {
    report(ex);
    throw ex;
}

上述嘗試記錄所有異常,但沒有明確地處理它們。不幸的是,在 Java 7 之前,throw ex; 語句導致編譯器認為任何 Exception 都可能被丟擲。這可能會強制你將封閉方法宣告為 throws Exception。從 Java 7 開始,編譯器知道可以(重新丟擲)的異常集更小。