| 異常 | 類型 | 常見原因 | 處理建議 |
NullPointerException | Runtime | 引用為 null 調用方法/屬性 | 防御性檢查、Optional、@NonNull 註解 |
ArrayIndexOutOfBoundsException | Runtime | 索引 < 0 或 ≥ length | 迴圈邊界檢查、foreach 避免索引 |
ArithmeticException | Runtime | 除數為零 | 除法前檢查分母 |
ClassCastException | Runtime | 強制轉型失敗 | instanceof 檢查、泛型避免轉型 |
IllegalArgumentException | Runtime | 參數不合法 | 參數驗證、Bean Validation |
IOException | Checked | IO 操作失敗 | try-with-resources、重試機制 |
SQLException | Checked | 資料庫操作失敗 | 連接池、事務管理、異常轉換 |
資源洩漏與 finally 設計意圖
資源洩漏:獲取資源(檔案、連接、鎖)後未正確釋放,導致資源耗盡。
finally 存在的核心原因:保證清理代碼無論是否發生異常都會執行。即使 try 中有 return,finally 仍在方法返回前執行。
Try-Catch-Finally 完整語法
try {
// 可能拋出異常的代碼
// 發生異常後,後續代碼不再執行
} catch (ExceptionType1 e) {
// 處理 ExceptionType1 及其子類
} catch (ExceptionType2 e) {
// 處理 ExceptionType2
} finally {
// 無論是否異常、是否捕獲、是否 return,都會執行
// 資源關閉、鎖釋放、日誌記錄
}
多重 Catch 匹配規則
- 按宣告順序自上而下匹配
- 只執行第一個匹配的 catch 區塊
- 子類異常必須放在父類異常之前,否則編譯錯誤(不可達代碼)
- Java 7+ 支援多重捕獲:
catch (IOException | SQLException e)
異常處理兩大策略
| 策略 | 適用場景 | 程式碼模式 |
| 捕獲處理 | 當前層級知道如何處理、可恢復、需記錄日誌 | try { ... } catch (Exception e) { handle(); } |
| 拋出傳遞 | 當前層級無法處理、需上層決策、框架統一處理 | throws Exception 或 throw new CustomException() |
Checked Exception 強制處理
// 方式 1:捕獲
public void readFile() {
try (FileInputStream fis = new FileInputStream("a.txt")) {
fis.read();
} catch (IOException e) {
log.error("讀取失敗", e);
}
}
// 方式 2:拋出
public void readFile() throws IOException {
try (FileInputStream fis = new FileInputStream("a.txt")) {
fis.read();
}
// IOException 向上傳遞
}
Runtime Exception 建議不捕獲
- 多為程式邏輯錯誤(空指針、越界、參數錯誤)
- 應通過防御性程式設計預防:非空檢查、邊界檢查、參數驗證
- 框架層統一捕獲記錄,返回友好錯誤碼
自定義異常最佳實踐
設計原則
- 繼承
Exception(Checked)或 RuntimeException(Unchecked)
- 提供無參、訊息、原因、訊息+原因 四種建構子
- 命名以
Exception 結尾(BusinessException、UserNotFoundException)
- 攜帶錯誤碼、錯誤詳情,支援國際化
標準自定義異常範本
public class BusinessException extends RuntimeException {
private final int errorCode;
private final Map<String, Object> details;
public BusinessException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.details = new HashMap<>();
}
public BusinessException(int errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.details = new HashMap<>();
}
public BusinessException addDetail(String key, Object value) {
details.put(key, value);
return this;
}
// getters...
}
異常處理實戰模式
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(e.getErrorCode(), e.getMessage(), e.getDetails()));
}
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(404, e.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneric(Exception e) {
log.error("系統異常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(500, "系統內部錯誤"));
}
}
面試高頻考點深度解析
1. Try 中 return 與 Finally 執行順序
public int test() {
try {
return 1; // 1. 計算返回值 1,暫存
} finally {
return 2; // 2. 執行 finally,覆蓋返回值為 2
} // 3. 方法返回 2
}
// 輸出:2
try 有 return:先執行 finally,再返回
finally 有 return:覆蓋 try 的返回值(不推薦,易混淆)
System.exit(0):直接終止 JVM,finally 不執行
2. Final/Finally/Finalize 區別
| 關鍵字 | 類型 | 用途 |
final | 修飾符 | 類不可繼承、方法不可覆寫、變數不可變更 |
finally | 區塊 | 異常處理清理代碼,保證執行 |
finalize | 方法 | Object 方法,GC 前調用(Java 9 棄用,用 Cleaner/PhantomReference) |
3. Throw vs Throws
throw:方法體內拋出異常物件,throw new Exception()
throws:方法簽名聲明可能拋出的異常類型,throws IOException
4. 未捕獲異常處理機制
- 執行緒終止,棧追蹤輸出到標準錯誤流
- 可設置
Thread.setDefaultUncaughtExceptionHandler() 全域處理
- Swing/AWT 事件執行緒有專屬處理機制
- Spring Boot 透過
@ControllerAdvice 統一處理 Web 請求異常
最佳實踐總結
- 具體異常優於通用異常:捕獲
IOException 而非 Exception
- 異常鏈保持完整:
throw new BusinessException("失敗", e) 保留原始原因
- 避免吞沒異常:
catch (Exception e) { } 絕對禁止
- 資源使用 try-with-resources:JDK 7+ 自動關閉實現
AutoCloseable 的資源
- 業務異常用 Checked,系統異常用 Unchecked:業務可恢復用 Checked,程式錯誤用 Unchecked
- 異常不用於控制流程:效能差、語義不清,用條件判斷替代
- 上一篇文章: Java 封裝與類存取控制完整指南:從基礎到進階實戰
- 下一篇文章: javaw.exe 與 java.exe 的核心差異解析
Copyright ©
程式碼之家 All Rights Reserved