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

Java 異常處理實戰:try-catch-finally 語法、多重捕獲、異常鏈與最佳實踐

     編輯:Java 相關知識

異常處理不是「寫幾個 catch 塊」,而是設計錯誤傳播路徑、保證資源釋放、提供可操作的錯誤信息。本文從語法基礎、執行流程、多重捕獲規則、異常轉換鏈、資源管理、生產級最佳實踐六個維度,構建完整異常處理知識體系。

一、try-catch-finally 基礎語法與執行流程

try {
    // 受監控代碼區
    riskyOperation();
} catch (IOException e) {          // 捕獲特定異常
    handleIOError(e);
} catch (SQLException | TimeoutException e) { // JDK 7+ 多異常合併
    handleDbOrTimeout(e);
} catch (Exception e) {            // 兜底捕獲(父類在後)
    log.error("未預期錯誤", e);
    throw new RuntimeException(e); // 可轉型拋出
} finally {
    // 清理資源:關流、釋放鎖、關連接
    closeResources();
}

執行流程圖(含 return/throw 情況)

try {
    A(); B(); // B 拋異常
    return 1; // 不執行
} catch (Exception e) {
    C();      // 執行捕獲塊
    return 2; // 設置返回值 2
} finally {
    D();      // **必執行**(極少數除外)
    // return 3; // ⚠️ 覆蓋外層返回值,極度不推薦
}
// 實際返回:2
// 若 finally 拋異常:外層異常被覆蓋,finally 異常傳播

二、多重捕獲順序鐵律與 JDK 7+ 語法糖

子類先捕獲,父類後捕獲,否則編譯錯誤 "unreachable catch block"。
// 錯誤示範
catch (Exception e) { }
catch (IOException e) { } // 編譯錯誤:IOException 是 Exception 子類,永不可達

// 正確
catch (FileNotFoundException e) { } // 最具體
catch (IOException e) { }           // 父類
catch (Exception e) { }             // 更上層

// JDK 7+ 合併捕獲(同層級、無繼承關係)
catch (IOException | SQLException | TimeoutException e) {
    // e 類型為公共父類(Exception),不可重新拋出具體子類
}

三、異常轉換鏈:保留原始堆棧是鐵律

try {
    jdbcTemplate.update(sql);
} catch (DataAccessException e) {
    // ❌ 錯誤:丟失原始堆棧
    throw new BusinessException("數據庫錯誤");

    // ✅ 正確:構造器傳入 cause
    throw new BusinessException("用戶保存失敗", e);

    // ✅ 正確:initCause(異常無帶 cause 構造器時)
    BusinessException be = new BusinessException("用戶保存失敗");
    be.initCause(e);
    throw be;
}

日誌記錄鐵律:log.error("業務含義描述", e) —— 參數順序:信息在前,異常對象在後,框架自動打印完整堆棧。

四、資源管理:try-with-resources (JDK 7+) 完勝手工 finally

// 手工 finally 痛點:代碼冗長、關閉順序易錯、異常抑制機制缺失
try (Connection conn = getConnection();
     PreparedStatement ps = conn.prepareStatement(sql);
     ResultSet rs = ps.executeQuery()) {

    while (rs.next()) { process(rs); }
} // 自動按**逆序**關閉:rs → ps → conn
// 若關閉過程拋異常,被**抑制**附加到主異常(getSuppressed() 可獲取)

自定義資源實現 AutoCloseable

class TempFileManager implements AutoCloseable {
    private final File tempDir;
    public TempFileManager() { tempDir = Files.createTempDirectory("app").toFile(); }
    public File newTempFile() throws IOException { return File.createTempFile("tmp", ".dat", tempDir); }
    @Override public void close() { // 刪除整個臨時目錄
        FileUtils.deleteQuietly(tempDir);
    }
}

// 使用
try (TempFileManager tm = new TempFileManager()) {
    File f1 = tm.newTempFile();
    File f2 = tm.newTempFile();
    // 退出 try 自動清理
}

五、受檢 vs 非受檢異常:架構層面的選型哲學

維度受檢異常 (Checked)非受檢異常 (Unchecked)
繼承Exception (非 RuntimeException)RuntimeException 子類
編譯器強制必須 catch 或 throws無強制
語義**可預期、可恢復**的業務錯誤**編程錯誤、不可恢復**的系統故障
典型例子IOException、SQLException、TimeoutException、自定義 BizExceptionNullPointerException、IllegalArgumentException、IndexOutOfBounds、自定義 SystemException
架構層面Service 層拋出,Controller 捕獲轉友好提示框架全局攔截器捕獲,記錄日誌、返回 500/錯誤碼

六、生產級最佳實踐清單

  1. 異常分層:DAO 拋 SQLException → Service 包裝為 BizException → Controller 統一處理返回錯誤碼/頁面。
  2. 不吞沒異常:catch (Exception e) { } 嚴禁;最小動作 log.error("", e)。
  3. 異常信息業務化:throw new BizException("訂單號 {} 不存在", orderId),而非 "ERROR"。
  4. 保留異常鏈:永遠傳入 cause,initCause 兜底。
  5. finally 只做清理:無 return、無 throw、無耗時邏輯。
  6. 優先 try-with-resources:管理連接、流、鎖、臨時文件、網絡端口。
  7. 全局異常處理器:Spring @ControllerAdvice、Servlet error-page、Thread setDefaultUncaughtExceptionHandler。
  8. 避免用異常控制流程:如 try { map.get(key) } catch { ... } 判斷 key 存在 —— 用 containsKey。
  9. 文檔化拋出異常:Javadoc @throws BizException 當訂單不存在時。

異常處理是代碼健壯性的護城河。內化本文流程圖、轉換鏈、資源管理模式與最佳實踐,能讓你的系統在錯誤面前依然優雅、可追蹤、可恢復。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved