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

Java 異常處理陷阱:try-catch-finally 中異常傳播與返回值行為詳解

     編輯:Java 相關知識

核心代碼演示

public class ExceptionTrapDemo {
    public static void main(String[] args) {
        try {
            int a = 1 / 0; // ArithmeticException
        } catch (Exception e) {
            System.out.println("catch 塊");
            String s = null;
            System.out.println(s.length()); // NullPointerException
        } finally {
            System.out.println("finally 塊");
            int[] arr = {1, 2, 3};
            System.out.println(arr[6]); // ArrayIndexOutOfBoundsException
        }
    }
}

執行結果分析

  1. try 拋出 ArithmeticException。
  2. 進入 catch,打印 "catch 塊",隨後拋出 NullPointerException。
  3. 關鍵點:catch 中的異常暫存,執行 finally。
  4. finally 打印 "finally 塊",拋出 ArrayIndexOutOfBoundsException。
  5. 最終結果:ArrayIndexOutOfBoundsException 向外傳播,**NullPointerException 被吞噬(丟失)**。

若移除 finally 塊,則拋出 NullPointerException。

異常掩蓋規則總結

場景最終拋出異常被吞噬異常
try 拋 A,catch 拋 B,finally 正常BA
try 拋 A,catch 正常,finally 拋 CCA
try 正常,catch 拋 B,finally 拋 CCB
try 拋 A,catch 拋 B,finally 拋 CCA, B
try return,finally returnfinally 的 return 值try 的 return 值

鐵律:finally 中的 拋出異常 或 return/break/continue 會**完全覆蓋** try/catch 的異常或返回值。

正確處理模式

1. finally 內部捕獲異常,防止掩蓋

try {
    riskyOperation();
} catch (Exception e) {
    log.error("業務異常", e);
    throw new BusinessException(e);
} finally {
    try {
        resource.close(); // 可能拋 IOException
    } catch (IOException e) {
        log.warn("資源關閉失敗", e); // 僅記錄,不拋出
        // 或添加到 Suppressed:primary.addSuppressed(e) (Java 7+)
    }
}

2. Java 7+ try-with-resources 自動處理

// 自動調用 close(),異常自動添加為 Suppressed
try (Resource res = new Resource()) {
    res.riskyOperation();
} catch (Exception e) {
    // e.getSuppressed() 可獲取 close() 拋出的異常
    throw new BusinessException(e);
}

3. 保留原始異常鏈

try {
    // ...
} catch (SQLException e) {
    throw new DataAccessException("數據庫訪問失敗", e); // 構造函數傳入 cause
}

返回值陷阱

public int trap() {
    try {
        return 1;
    } finally {
        return 2; // 編譯器警告:finally 塊不正常完成
    }
}
// 調用結果:2,try 的 return 1 被丟棄

最佳實踐:finally **絕不**包含 return/throw/break/continue,僅用於資源清理、狀態重置、日誌記錄。

Suppressed Exceptions (Java 7+)

try (AutoCloseable res = () -> { throw new IOException("close 失敗"); }) {
    throw new SQLException("業務失敗");
} catch (Exception e) {
    System.out.println("主異常: " + e); // SQLException
    for (Throwable t : e.getSuppressed()) {
        System.out.println("被壓制: " + t); // IOException
    }
}

try-with-resources 自動將資源關閉異常作為 Suppressed 附加到主異常,避免異常丟失,便於診斷。

總結

  1. finally 中拋出異常/返回會吞噬 try/catch 的異常/返回值。
  2. 資源清理代碼放入 finally 時,**必須內部捕獲異常**,或使用 try-with-resources。
  3. 異常鏈保持完整:throw new XxxException("msg", cause)。
  4. 使用 Java 7+ try-with-resources 替代手動 finally 關閉資源。
  5. 日誌記錄異常時,務必打印完整堆疊:log.error("msg", e),而非 log.error(e.getMessage())。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved