Java 異常分為受檢異常與非受檢異常,這一設計直接影響代碼的健壯性與編譯器強制要求。本文梳理異常層級結構、編譯器檢查規則、最佳處理實踐與高頻面試題。
Throwable
├── Error // 非受檢:JVM 級嚴重錯誤(OOM、StackOverflow)
└── Exception
├── RuntimeException // 非受檢:編程邏輯錯誤(NPE、索引越界、類轉換)
└── 其他 Exception // 受檢:環境/資源問題(IO、SQL、網絡、反射)
| 維度 | 受檢異常 | 非受檢異常 |
|---|---|---|
| 代表類 | IOException, SQLException, ClassNotFoundException | RuntimeException, Error 及其子類 |
| 編譯器檢查 | 強制:必須 try-catch 或 throws 聲明 | 不強制:可捕獲可拋棄 |
| 發生階段 | 運行時(外部環境依賴) | 運行時(代碼邏輯缺陷) |
| 處理責任 | 調用者必須處理 | 開發者應預防修復 |
| 典型場景 | 文件讀寫、數據庫連接、網絡請求 | 空指針、數組越界、除零、類型轉換 |
// 受檢異常:必須處理
public void readFile(String path) throws IOException {
Files.readAllLines(Paths.get(path)); // 編譯器強制 throws 或 try-catch
}
// 非受檢異常:可不處理
public int parseInt(String s) {
return Integer.parseInt(s); // NumberFormatException 為 RuntimeException
}
catch (Exception e) {} 是大忌,至少記錄日誌Objects.requireNonNull()、CollectionUtils.isNotEmpty()強制調用者感知並處理外部依賴失敗風險,提升系統健壯性。但過度使用會導致代碼冗長、異常丟失、棧追蹤污染。
try {
riskyOperation();
} catch (IOException e) {
throw new BusinessException("操作失敗", e); // 包裝為 RuntimeException 子類
}
語法上可 catch (Error e),但極不建議。Error 表示 JVM 不可恢復狀態(如 OOM),捕獲後無法修復,反而掩蓋真實問題。