Java 異常體系架構
在 Java 中,所有異常對象均繼承自 Throwable 類。異常體系主要分為兩大分支:
- Error:表示 JVM 內部錯誤或資源耗盡(如
OutOfMemoryError),屬於嚴重問題,通常不應捕獲處理。
- Exception:表示程序可處理的異常情況,進一步分為:
- RuntimeException(非檢查異常):編程邏輯錯誤導致,如空指針、數組越界、類型轉換錯誤。編譯器不強制處理。
- 受檢異常:外部環境因素導致,如 I/O 錯誤、數據庫連接失敗、ClassNotFound。編譯器強制要求捕獲或聲明拋出。
異常拋出的四種場景
- 調用拋出受檢異常的方法(如
BufferedReader.readLine())。
- 程序主動用
throw 拋出受檢異常。
- 程序邏輯錯誤觸發非檢查異常(如數組下標越界)。
- JVM 或運行時庫內部錯誤。
異常處理核心原則
1. 捕獲已知,傳遞未知
僅捕獲明確知道如何處理的異常,其他異常通過 throws 向上傳遞,由上層統一處理或記錄。
2. finally 塊的資源釋放
無論是否發生異常,finally 塊都會執行。雖然 Java 有垃圾回收,但文件句柄、數據庫連接、網絡套接字等稀缺資源仍需在 finally 中顯式關閉。
3. 方法覆寫與異常聲明
子類覆寫父類方法時,拋出的受檢異常範圍必須 ≤ 父類方法聲明的範圍。若父類未聲明受檢異常,子類也不可聲明。
異常使用最佳實踐
- 避免用異常控制流程:異常創建堆疊追蹤開銷大,僅用於真正的錯誤場景。
- 不壓制異常:空
catch 塊會吞沒錯誤信息,至少應記錄日誌。
- 合理封裝異常:將底層異常包裝為業務語義清晰的自定義異常再拋出。
- 優先使用標準異常:如
IllegalArgumentException、IllegalStateException 等,減少自定義異常類數量。
斷言機制詳解
啟用與禁用
斷言默認關閉。運行時通過參數控制:
-ea / -enableassertions:啟用斷言
-da / -disableassertions:禁用斷言
-esa / -enablesystemassertions:啟用系統類斷言
-dsa / -disablesystemassertions:禁用系統類斷言
可針對特定包/類啟用:-ea:com.example.mypackage...
斷言使用鐵律
- 斷言失敗不可恢復:表示程序內部邏輯錯誤,屬於致命錯誤。
- 僅用於開發測試階段:生產環境應關閉斷言(
-da)。
- 不可作為業務邏輯控制手段:不應用斷言檢查用戶輸入、網絡響應等外部依賴條件。
- 斷言表達式無副作用:斷言條件判斷不應改變程序狀態。
斷言 vs 日誌
斷言是開發調試期的「斷點檢查工具」;日誌是全生命周期的「運行記錄策略」。兩者互補,不可混淆。
《Thinking in Java》異常處理指導原則摘要
- 修復問題並重試導致異常的操作。
- 修復問題並繼續執行(不重試)。
- 計算替代結果返回。
- 在當前上下文盡可能處理,並重新拋出相同異常到上層。
- 在當前上下文盡可能處理,並拋出不同類型異常到上層。
- 終止程序運行。
- 簡化代碼:若異常處理讓代碼更複雜,寧可不處理。
- 提升庫和程序的健壯性(短期調試便利 + 長期穩定性投資)。