首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 異常處理核心機制:Exception 體系、Try-Catch-Finally、自定義異常與面試高頻考點

     編輯:Java 程式設計入門

異常體系架構

                    Throwable
                       │
          ┌────────────┴────────────┐
          ▼                         ▼
        Error                     Exception
          │                         │
    (JVM嚴重錯誤)              (程式可處理)
    OOM, StackOverflow        ┌─────┴─────┐
                              ▼           ▼
                        RuntimeException  Checked Exception
                        (Unchecked)       (編譯期檢查)
                        NPE, AIOOBE       IOException, SQLException

核心異常類別

資源洩漏與 finally 設計意圖

資源洩漏:獲取資源(檔案、連接、鎖)後未正確釋放,導致資源耗盡。

finally 存在的核心原因:保證清理代碼無論是否發生異常都會執行。即使 try 中有 return,finally 仍在方法返回前執行。

Try-Catch-Finally 完整語法

try {
    // 可能拋出異常的代碼
    // 發生異常後,後續代碼不再執行
} catch (ExceptionType1 e) {
    // 處理 ExceptionType1 及其子類
} catch (ExceptionType2 e) {
    // 處理 ExceptionType2
} finally {
    // 無論是否異常、是否捕獲、是否 return,都會執行
    // 資源關閉、鎖釋放、日誌記錄
}

多重 Catch 匹配規則

  1. 按宣告順序自上而下匹配
  2. 只執行第一個匹配的 catch 區塊
  3. 子類異常必須放在父類異常之前,否則編譯錯誤(不可達代碼)
  4. Java 7+ 支援多重捕獲:catch (IOException | SQLException e)

異常處理兩大策略

異常類型常見原因處理建議
NullPointerExceptionRuntime引用為 null 調用方法/屬性防御性檢查、Optional、@NonNull 註解
ArrayIndexOutOfBoundsExceptionRuntime索引 < 0 或 ≥ length迴圈邊界檢查、foreach 避免索引
ArithmeticExceptionRuntime除數為零除法前檢查分母
ClassCastExceptionRuntime強制轉型失敗instanceof 檢查、泛型避免轉型
IllegalArgumentExceptionRuntime參數不合法參數驗證、Bean Validation
IOExceptionCheckedIO 操作失敗try-with-resources、重試機制
SQLExceptionChecked資料庫操作失敗連接池、事務管理、異常轉換
策略適用場景程式碼模式
捕獲處理當前層級知道如何處理、可恢復、需記錄日誌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 建議不捕獲

  • 多為程式邏輯錯誤(空指針、越界、參數錯誤)
  • 應通過防御性程式設計預防:非空檢查、邊界檢查、參數驗證
  • 框架層統一捕獲記錄,返回友好錯誤碼

自定義異常最佳實踐

設計原則

  1. 繼承 Exception(Checked)或 RuntimeException(Unchecked)
  2. 提供無參、訊息、原因、訊息+原因 四種建構子
  3. 命名以 Exception 結尾(BusinessException、UserNotFoundException)
  4. 攜帶錯誤碼、錯誤詳情,支援國際化

標準自定義異常範本

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. 未捕獲異常處理機制

  1. 執行緒終止,棧追蹤輸出到標準錯誤流
  2. 可設置 Thread.setDefaultUncaughtExceptionHandler() 全域處理
  3. Swing/AWT 事件執行緒有專屬處理機制
  4. Spring Boot 透過 @ControllerAdvice 統一處理 Web 請求異常

最佳實踐總結

  1. 具體異常優於通用異常:捕獲 IOException 而非 Exception
  2. 異常鏈保持完整:throw new BusinessException("失敗", e) 保留原始原因
  3. 避免吞沒異常:catch (Exception e) { } 絕對禁止
  4. 資源使用 try-with-resources:JDK 7+ 自動關閉實現 AutoCloseable 的資源
  5. 業務異常用 Checked,系統異常用 Unchecked:業務可恢復用 Checked,程式錯誤用 Unchecked
  6. 異常不用於控制流程:效能差、語義不清,用條件判斷替代
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved