Throwable 體系架構
Throwable (所有異常/錯誤共同祖先)
├── Error // 嚴重錯誤,JVM 層面,不可恢復
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── NoClassDefFoundError
└── Exception // 程式可預期、可恢復的異常
├── RuntimeException (非檢查型異常 Unchecked)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ └── ClassCastException
└── 非 RuntimeException (檢查型異常 Checked)
├── IOException
├── SQLException
├── ClassNotFoundException
└── ParseException
Error vs Exception 核心差異
| 維度 | Error (錯誤) | Exception (異常) |
| 本質 | JVM 執行環境嚴重問題 | 應用邏輯可預期的錯誤狀況 |
| 可恢復性 | 通常不可恢復,程式應終止 | 可透過 try-catch 處理並恢復 |
| 發生時機 | 執行期,往往非程式碼直接控制 | 特定程式碼路徑可預測發生 |
| 處理建議 | **不應** try-catch,讓程式崩潰並修復根因 | **應** 針對性捕獲處理或向上拋出 |
| 典型範例 | OOM、棧溢位、類定義缺失 | 檔案不存在、SQL 執行失敗、解析錯誤 |
檢查型 vs 非檢查型異常
Checked Exception (檢查型 / 非 RuntimeException)
- 編譯器強制要求處理:
try-catch 或 throws 宣告
- 代表可預期、可恢復的業務異常(如 IO、資料庫、網路)
- API 設計時:呼叫者**必須**處理的異常 → 用 Checked
Unchecked Exception (非檢查型 / RuntimeException 及子類)
- 編譯器**不強制**處理,可選擇性捕獲
- 代表程式邏輯缺陷(空指標、越界、型別轉換錯誤)
- 發生即為 Bug,**應修正程式碼而非捕獲掩蓋**
- API 設計時:因呼叫者邏輯錯誤導致 → 用 Unchecked
異常處理最佳實踐
1. 處理策略決策樹
異常發生
│
├─ 能在當前層級有意義處理? → try-catch 處理並恢復/轉換
│
├─ 無法處理但呼叫者可能處理? → throws 向上拋出 (Checked)
│
├─ 程式邏輯 Bug (NPE, 索引越界...)? → **不要捕獲**,讓它拋出、記錄、修復代碼
│
└─ 資源洩漏風險? → finally / try-with-resources 確保釋放
2. try-catch-finally 規範
- 多
catch 由子類到父類順序(IOException 在 Exception 前)
- 僅捕獲能處理的具體異常,避免
catch (Exception e) 吞噬錯誤
finally 必執行(除 System.exit()),用於關閉資源
- Java 7+ 優先用
try-with-resources 自動關閉 AutoCloseable 資源
3. 自訂異常設計原則
- 業務異常繼承
Exception (Checked),如 InsufficientBalanceException
- 系統/邏輯錯誤繼承
RuntimeException,如 InvalidConfigurationException
- 提供建構子:
msg、cause、msg+cause,保留堆疊追蹤
- 異常類命名以
Exception 結尾,語義清晰
常見誤區與反模式
| 反模式 | 危害 | 正確做法 |
| catch 後只印堆疊不處理 | 錯誤被吞噬,上層不知失敗 | 轉換為業務異常拋出、或記錄後重拋 |
| 用 Exception 控制流程 | 效能差、可讀性低 | 用條件判斷、Optional、Result 物件 |
| 捕獲 Error/Throwable | 掩蓋 JVM 嚴重錯誤 | 僅捕獲具體 Exception 子類 |
| finally 中拋出異常 | 掩蓋 try/catch 中的原始異常 | finally 內部 try-catch 或用 try-with-resources |
總結:異常處理鐵律
- Error 不捕獲,讓程式快速失敗暴露根因
- RuntimeException 不捕獲,修正代碼邏輯
- Checked Exception 必處理,try-catch 轉換或 throws 透傳
- 資源必關閉,finally 或 try-with-resources
- 異常轉換保留原因,
new BizException("msg", cause)