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

《Thinking in Java》讀書筆記:第 12 章 異常處理——核心機制、自定義異常、finally 陷阱、檢查型與非檢查型抉擇

     編輯:Java 相關知識

異常處理是構建健壯系統的基石。第 12 章從異常哲學切入,系統闡述拋捕機制、異常規範、自定義異常、finally 清理、棧追蹤、異常鏈、受檢/非受檢異常抉擇、構造器異常處理、替代方案。本文提煉核心精華與實戰指南。

異常處理核心哲學

「在問題發生點,你可能不知道怎麼處理,但你知道不能繼續執行。將問題拋給更高層級、有資格決策的上下文。」

  • 異常 = 「無法在當前上下文處理的錯誤信息」,通過對象攜帶上下文向上傳遞。
  • 統一錯誤報告模型:所有錯誤通過異常對象傳遞,而非錯誤碼/返回值/全局變量。
  • 事務視角:操作要么成功,要么拋出異常回滾,保證系統穩定狀態。

基本異常流程

// 拋出
throw new IOException("網絡中斷"); // 帶信息的構造器

// 捕獲
try {
    riskOperation();
} catch (IOException e) {
    // 處理或轉換
    throw new BusinessException("業務失敗", e); // 異常鏈:保留根因
} finally {
    cleanup(); // 必執行清理
}

異常參數與類型體系

  • 所有標準異常提供兩構造器:無參、String message。
  • Throwable 為根,兩大分支:
    • Error:JVM/系統級嚴重錯誤(OutOfMemoryError、StackOverflowError),不捕獲。
    • Exception:應用級異常,可捕獲處理。
      • 受檢異常:繼承 Exception(非 RuntimeException),編譯器強制 try-catch 或 throws。例:IOException、SQLException。
      • 非受檢異常:繼承 RuntimeException,編譯器不強制。例:NullPointerException、IllegalArgumentException。代表編程錯誤,應通過代碼邏輯預防。

捕獲異常:try-catch-finally 規則

  • catch 順序:子類在前,父類在後,否則編譯錯誤(不可達代碼)。
  • 多重捕獲:Java 7+ catch (IOException | SQLException e) 合併處理。
  • finally:無論是否異常、是否 return,必執行(除非 System.exit()、JVM 崩潰、無限循環)。

finally 兩大陷阱

自定義異常最佳實踐

設計原則

  1. 業務異常 → 繼承 Exception(受檢),強制調用者感知處理。
  2. 系統/邏輯異常 → 繼承 RuntimeException,冒泡到全局處理器。
  3. 提供構造器鏈:message、cause、message+cause。
  4. 攜帶業務上下文:錯誤碼、參數快照、用戶 ID、請求 ID。

模板

public class BusinessException extends Exception {
    private final int errorCode;
    private final Map<String, Object> context;
    
    public BusinessException(int errorCode, String message) {
        super(message); this.errorCode = errorCode; this.context = new HashMap<>();
    }
    public BusinessException(int errorCode, String message, Throwable cause) {
        super(message, cause); this.errorCode = errorCode; this.context = new HashMap<>();
    }
    public BusinessException addContext(String key, Object value) { context.put(key, value); return this; }
    // getters...
}

異常規範:throws 聲明與繼承約束

  • 方法必須聲明拋出的受檢異常(throws IOException),非受檢異常可不聲明。
  • 覆蓋方法異常規範收窄原則:子類覆蓋方法只能拋出父類聲明異常的子類或相同異常,不能新增/擴大。保證多態替換時調用者 catch 塊仍有效。
  • 接口方法無異常聲明 → 實現類不可拋受檢異常。
  • 構造器無此限制,但需聲明父構造器異常。

異常鏈:保留根因堆疊

try {
    // 底層操作
} catch (SQLException e) {
    // 包裝轉換,保留 cause
    throw new DataAccessException("數據庫訪問失敗", e);
    // 或 JDK 7+ 構造器直接支持 cause
}
  • getCause() 獲取根因,printStackTrace() 會遞歸打印鏈路。
  • 日誌框架(Logback/Log4j2)配置 %ex{full} 自動輸出完整鏈。

構造器中異常處理

  • 構造器拋異常時,對象未完全構造,finally 中清理可能訪問未初始化字段 → NPE。
  • 鐵律:創建需清理的對象後,立即進入 try-finally。
    InputStream in = new FileInputStream(file);
    try {
        // 使用 in
    } finally {
        in.close(); // 確保關閉
    }

受檢 vs 非受檢:架構級抉擇

陷阱代碼後果規避
吞噬異常try { throw new A(); } finally { return 1; }異常 A 丟失,調用者感知不到finally 中絕不 return/throw/break/continue
資源關閉異常掩蓋finally { in.close(); out.close(); }in.close() 拋異常導致 out.close() 不執行各自 try-catch 包裹,或用 try-with-resources

替代方案:受檢轉非受檢

// 包裝轉換
throw new RuntimeException(checkedException); // 簡單粗暴
// 或定義統一非受檢基類
public class AppException extends RuntimeException { ... }

適用於:框架邊界、不想污染業務簽名、統一由全局處理器處理的場景。

總結

異常處理鐵律:具體捕獲、早拋晚捕、保留鏈路、finally 不 return、try-with-resources 管資源、受檢業務/非受檢系統、全局統一處理。將異常視為「帶上下文的信號」而非「錯誤碼」,配合 Spring @ControllerAdvice + 統一錯誤碼體系 + 鏈路追蹤,構建可觀測、可運維的生產級異常體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
維度受檢異常非受檢異常
適用場景可恢復的業務/外部故障(文件不存在、網絡超時、數據庫死鎖)編程錯誤、不可恢復的系統故障、框架內部錯誤
優勢編譯器強制處理,API 文檔化代碼簡潔、不污染方法簽名、適合冒泡到全局處理器
劣勢樣板代碼多、易被吞噬、擴散到調用鏈易被忽視、運行時才發現
現代趨勢Spring/框架層轉為非受檢,由全局 @ControllerAdvice 統一處理業務代碼仍建議受檢,明確錯誤語義