繼承自 `RuntimeException`,編譯器不強制捕獲/聲明,但業務邏輯錯誤高發區。掌握高頻異常成因與預防模式,是寫出健壯代碼的基本功。
| 異常類別 | 典型成因 | 代碼示例 | 預防/處理策略 |
|---|---|---|---|
NullPointerException | 調用 null 物件方法/欄位、訪問 null 陣列元素 | `user.getName().length()` / `arr[0].toString()` | Objects.requireNonNull、Optional、@NonNull 註解、防御性檢查 |
ArrayIndexOutOfBoundsException | 索引 < 0 或 >= length | `list.get(size)` / `arr[-1]` | 邊界檢查、foreach/stream 替代索引、List 而非陣列 |
ArithmeticException | 整數除以零、模零 | `int x = 1/0; int y = 5 % 0;` | 除數預檢、BigDecimal 除法指定精度/模式 |
ClassCastException | 強制轉型失敗,物件非目標類型 | `(String) list.get(0)` 實為 Integer | 泛型擦除警惕、instanceof 守衛、編譯期泛型約束 |
IllegalArgumentException | 方法參數不合法(範圍、格式、狀態) | `setAge(-1)` / `parse("abc")` | 參數驗證 (Bean Validation)、前置條件斷言 |
IllegalStateException | 物件狀態不支持當前操作 | `iterator.remove()` 未先 `next()` / 連接池已關閉 | 狀態機設計、@PreAuthorize、文檔化合法狀態遷移 |
NumberFormatException | 字串無法解析為數字 | `Integer.parseInt("1.5")` / `Long.parseLong("abc")` | 正則預檢、try-catch 包裝返回 Optional、NumberUtils |
UnsupportedOperationException | 調用不支持的可選操作 | `Collections.unmodifiableList.add()` / `iterator.remove()` | 文檔化不可變集合、介面分離 (只讀/可寫) |
IndexOutOfBoundsException | 超出序列邊界 (List/String/Buffer) | `str.charAt(len)` / `subList(0, size+1)` | 同 ArrayIndexOutOfBounds,邊界守衛 |
ConcurrentModificationException | 迭代過程中結構性修改集合 | `for (e: list) list.remove(e)` | Iterator.remove、CopyOnWriteArrayList、Stream、收集後再刪 |
| 錯誤類別 | 屬性 | 典型場景 | 應對 |
|---|---|---|---|
OutOfMemoryError | Error (不可恢復) | 堆耗盡、Metaspace 滿、直接記憶體洩漏、線程過多 | Heap Dump 分析 (MAT/JVisualVM)、調優 -Xmx、排查洩漏、啟用 GC 日誌 |
StackOverflowError | Error | 遞歸過深、循環調用、大棧幀 (大量局部變數) | 改迭代、增加 -Xss、尾遞歸優化 (JVM 不保證) |
NoClassDefFoundError | Error | 類初始化失敗、類路徑缺失、版本衝突、靜態塊拋異常 | `mvn dependency:tree`、檢查類載入器、排查靜態初始化 |
ClassNotFoundException | Checked Exception | 反射/類載入器動態載入失敗 | 同 NoClassDefFoundError,確認依賴打包 |
UnsatisfiedLinkError | Error | JNI 本地庫找不到/版本不匹配/架構不符 (x86 vs x64) | 配置 java.library.path、檢查 .so/.dll 依賴 |
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public R handleBiz(BusinessException e) {
log.warn("業務異常: {}", e.getMessage());
return R.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler(ConstraintViolationException.class)
public R handleValid(ConstraintViolationException e) {
String msg = e.getConstraintViolations().stream()
.map(v -> v.getPropertyPath() + " " + v.getMessage())
.collect(Collectors.joining("; "));
return R.fail(400, msg);
}
@ExceptionHandler(Exception.class)
public R handleAll(Exception e) {
log.error("系統異常", e); // 完整堆疊
return R.fail(500, "系統內部錯誤,請稍後重試");
}
}
異常不是敵人,是系統在說話。建立「預防 > 捕獲 > 記錄 > 告警 > 復原」的完整鏈路,比背誦異常清單更重要。下一篇將實戰《Spring Boot 3 錯誤處理機制深度解析與最佳實踐》。