異常體系總覽
Throwable
├── Error // 嚴重錯誤,應用無法恢復(OOM、StackOverflow)
└── Exception
├── RuntimeException // 運行時異常(非受檢)
│ ├── NullPointerException
│ ├── ClassCastException
│ ├── IndexOutOfBoundsException
│ ├── ArrayStoreException
│ ├── ArithmeticException
│ ├── IllegalArgumentException
│ ├── IllegalStateException
│ ├── ConcurrentModificationException
│ ├── UnsupportedOperationException
│ └── ... (50+ 子類)
└── 其他 Exception // 受檢異常
├── IOException
├── SQLException
├── ClassNotFoundException
├── InterruptedException
└── ...
RuntimeException vs 受檢異常 核心差異
| 維度 | RuntimeException (非受檢) | 受檢異常 |
| 編譯器強制 | 不強制捕獲/拋出 | 必須 try-catch 或 throws |
| 典型場景 | 編程錯誤、邏輯缺陷、不可預期 | 外部資源失效、業務可預期失敗 |
| 處理策略 | 修復代碼邏輯(預防優於捕獲) | 捕獲並業務降級/重試/轉碼 |
| API 設計 | 不污染方法簽名 | 顯式聲明 throws |
高頻五大 RuntimeException 解析
| 異常 | 觸發場景 | 預防措施 |
| NullPointerException | 調用 null 引用的方法/字段、訪問 null 數組元素 | Objects.requireNonNull()、Optional、@NonNull 註解、防禦性編程 |
| ClassCastException | 強制轉換不兼容類型 | instanceof 守衛、泛型擦除注意、集合存取類型一致 |
| IndexOutOfBoundsException | 數組/集合/字符串索引越界 | 循環邊界檢查、foreach 替代索引、List.subList 範圍校驗 |
| ArrayStoreException | 數組存入類型不匹配對象(如 Object[] 存 Integer 放 String) | 泛型集合替代數組、編譯期類型安全 |
| ArithmeticException | 整數除零(1/0)、BigDecimal 非終止小數除法無精度 | 除數非零守衛、BigDecimal 設置 MathContext |
其他高頻運行時異常速查
IllegalArgumentException:參數不合法(如負數傳給需正數參數)。
IllegalStateException:對象狀態不支持當前操作(如 Iterator 未 next() 直接 remove())。
ConcurrentModificationException:遍歷中直接修改集合(fail-fast)。
UnsupportedOperationException:調用不可選操作(如 Collections.unmodifiableList 返回列表調用 add)。
NumberFormatException:字符串解析數字格式錯誤。
最佳處理策略
1. 預防優於捕獲(針對 RuntimeException)
// ❌ 捕獲 NPE 掩蓋 Bug
try { user.getName().length(); } catch (NPE e) { return 0; }
// ✅ 防禦性檢查
if (user != null && user.getName() != null) return user.getName().length();
return 0;
// ✅ Optional 鏈式
return Optional.ofNullable(user).map(User::getName).map(String::length).orElse(0);
2. 受檢異常轉碼 + 統一異常處理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(SQLException.class)
public R handleSql(SQLException e) {
log.error("DB error", e);
return R.fail("系統繁忙,請稍後重試");
}
@ExceptionHandler(BizException.class)
public R handleBiz(BizException e) {
return R.fail(e.getCode(), e.getMessage());
}
}
3. 自定義異常體系設計
// 非受檢業務異常(繼承 RuntimeException)
public class BizException extends RuntimeException {
private final int code;
public BizException(int code, String msg) { super(msg); this.code = code; }
public int getCode() { return code; }
}
// 受檢異常僅用於「調用方必須感知並處理」的場景(極少)
常見誤區辨析
| 誤區 | 正確認知 |
| 「RuntimeException 不用處理」 | 不用強制捕獲,但必須通過代碼邏輯預防或全局攔截記錄,否則導致 500 錯誤體驗極差。 |
| 「所有異常都用 RuntimeException」 | 外部依賴失敗(DB、網絡、文件)建議受檢或轉碼,強調可恢復性。 |
| 「捕獲 Exception 即可」 | 吞噬具體異常類型,難以定位;應捕獲具體異常或重新拋出。 |
總結
- RuntimeException 表示編程錯誤,**核心策略是預防**,而非捕獲。
- 熟記高頻五大異常觸發條件與防禦模式。
- 受檢異常用於「可預期、可恢復」的外部交互,配合全局異常處理器統一轉碼返回友好錯誤碼。
- 自定義異常體系區分
BizException(Runtime) 與 SystemException(Checked),語義清晰。
- 異常處理原則:**具體捕獲、業務轉碼、全局養底、日誌留痕**。
建立清晰的異常分類認知與處理規範,是構建健壯、可維護 Java 系統的關鍵環節。