異常處理是 Java 程式設計的基石,但許多開發者僅停留在 try-catch-finally 基本語法,忽略了異常體系架構、檢查型與非檢查型異常的選擇哲學、以及資源釋放的隱藏陷阱。本文系統梳理異常架構,剖析經典錯誤案例,提供四大最佳實踐建議。
java.lang.Throwable
├── java.lang.Error // 嚴重錯誤,應用不應捕獲(如 OutOfMemoryError)
└── java.lang.Exception
├── 檢查型異常 // 必須宣告 throws 或捕獲(如 IOException)
└── 非檢查型異常 // RuntimeException 及其子類,編譯器不強制檢查
關鍵點:ThreadDeath 雖屬 Error,但不應捕獲,捕獲會導致線程無法正常終止。
NullPointerException, IllegalArgumentException)。不應強制呼叫者捕獲,讓其向上冒泡至全域處理器。throws 與空 catch 區塊。InputStream input = null;
try {
input = new FileInputStream("myFile.txt"); // 若檔案不存在,拋 FileNotFoundException
// do something
} catch (IOException e) {
throw new WrapperException(e); // 準備拋出包裝異常
} finally {
try {
input.close(); // 此處 input 為 null!拋出 NullPointerException
} catch (IOException e) {
throw new WrapperException(e);
}
}
結果:原本的 FileNotFoundException 被 NullPointerException **掩蓋**,導致錯誤診斷困難。
InputStream input = null;
try {
input = new FileInputStream("myFile.txt");
// do something
} catch (IOException e) {
throw new WrapperException(e);
} finally {
if (input != null) { // 必須檢查非 null
try { input.close(); } catch (IOException ignored) {}
}
}
或使用 Java 7+ **try-with-resources** 自動關閉:
try (InputStream input = new FileInputStream("myFile.txt")) {
// do something
} catch (IOException e) {
throw new WrapperException(e);
}
try-catch-finally 流程,減少樣板程式碼,確保資源正確釋放。異常不僅是錯誤信號,更是 API 契約的一部分。理解異常體系、避開資源釋放陷阱、建立統一處理機制,能讓系統在異常發生時仍保持穩健、可觀測、易維護。