陷阱 - 丟擲 Throwable 異常錯誤或 RuntimeException
雖然捕捉 Throwable
,Exception
,Error
和 RuntimeException
異常是壞事,但丟擲它們會更糟。
基本問題是,當你的應用程式需要處理異常時,頂級異常的存在使得很難區分不同的錯誤條件。例如
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
是不好的。在這種情況下,很難區分如果 somethingBad
是 true
而被丟擲的 Exception
的預期情況,以及我們實際捕獲未經檢查的異常(例如 NullPointerException
)的意外情況。
如果允許傳播頂級異常,我們會遇到其他問題:
- 我們現在必須記住我們丟擲頂級的所有不同原因,並區分/處理它們。
- 在
Exception
和Throwable
的情況下,如果我們希望傳播異常,我們還需要將這些異常新增到方法的throws
子句中。這是有問題的,如下所述。
簡而言之,不要丟擲這些異常。丟擲一個更具體的異常,更恰當地描述已經發生的異常事件。如果需要,請定義並使用自定義異常類。
在方法的丟擲中宣告 Throwable 或 Exception 是有問題的
用 Exception
甚至`Throwable 替換方法的 throws
子句中的一長串丟擲異常是很誘人的。這是一個壞主意:
- 它強制呼叫者處理(或傳播)
Exception
。 - 我們不能再依賴編譯器來告訴我們需要處理的特定已檢查異常。
- 正確處理
Exception
很困難。很難知道可能會捕獲哪些實際異常,如果你不知道可以捕獲什麼,則很難知道哪種恢復策略是合適的。 - 處理
Throwable
更加困難,因為現在你還必須應對可能永遠無法恢復的潛在失敗。
這個建議意味著應該避免某些其他模式。例如:
try {
doSomething();
} catch (Exception ex) {
report(ex);
throw ex;
}
上述嘗試記錄所有異常,但沒有明確地處理它們。不幸的是,在 Java 7 之前,throw ex;
語句導致編譯器認為任何 Exception
都可能被丟擲。這可能會強制你將封閉方法宣告為 throws Exception
。從 Java 7 開始,編譯器知道可以(重新丟擲)的異常集更小。