- 先來科普一下 CE 到底是什么吧。Java 要求你必須在函數的類型里面聲明它可能拋出的異常。比如,你的函數如果是這樣:
void foo(string filename) throws FileNotFoundException {
if (...) {
throw new FileNotFoundException();
}
}
- Java 要求你必須在函數頭部寫上“throws FileNotFoundException”,否則它就不能編譯。這個聲明表示函數在某些情況下,會拋出 FileNotFoundException 這個異常。由於編譯器看到了這個聲明,它會嚴格檢查你對 foo 函數的用法。在調用 foo 的時候,你必須使用 try-catch 處理這個異常,或者在調用的函數頭部也聲明 “throws FileNotFoundException”,把這個異常傳遞給上一層調用者。
try {
foo("blah");
} catch (FileNotFoundException e) {
...
}
這種對異常的聲明和檢查,叫做“checked exception”。
- FileNotFoundException”,把這個異常傳遞給上一層調用
另外,C#的設計者Hejlsberg 還指出 C# 代碼里沒有被 catch 的異常,應該可以用“靜態分析”檢查出來。可以看出來,他並不理解這種靜態檢查是多大規模的問題。要能用靜態分析發現 C# 代碼里被忽略的異常,你必須進行“全局分析”,也就是說為了知道一個函數是否會拋出異常,你不能只看這個函數。你必須分析這個函數的代碼,它調用的代碼,它調用的代碼調用的代碼…… 所以你需要分析超乎想象的代碼量,而且很多時候你沒有源代碼。所以對於大型的項目,這顯然是不現實的。
相比之下,Java 要求你對異常進行 throws 顯式聲明,實質上把這個全局分析問題分解成了一個個模塊化(modular)的小問題。每個函數作者完成其中的一部分,調用它的人完成另外一部分。大家合力幫助編譯器,高效的完成靜態檢查,防止漏掉異常處理,避免不必要的 try-catch。實際上,像 Exceptional 一類的 C# 靜態檢查工具,會要求你在注釋里寫出可能拋出的異常,這樣它才能發現被忽略的異常。所以 Exceptional 其實重新發明了 Java 的 CE,只不過 throws 聲明被寫成了一個注釋而已。
Java開發中異常處理需要注意的點:
- 任何方法中,如果出現異常而不捕獲,最終將被JVM感知,這將阻斷后續執行。
- 如果異常被捕獲,並層層向上拋出,異常最終將被JVM感知,也將阻斷后續執行。
- 如果異常被捕獲,並不再向上拋出,JVM將無法感知此處異常的存在,故程序將繼續執行。
