在
finally塊中使用return、continue、break等跳轉語句,會導致try/catch中拋出的異常被「吞噬」,外層調用者無法感知異常發生。
public class TryTest {
public static boolean test() throws Exception {
try {
throw new Exception("Something error"); // ① 拋出異常
} catch (Exception e) { // ② 捕獲異常
System.out.println("Exception from e");
return false; // ⑤ 本應返回 false
} finally {
return true; // ④ 直接返回 true,「吃掉」異常
}
}
public static void main(String[] args) {
System.out.println(TryTest.test()); // 輸出:true —— 異常無聲無息消失!
}
}
try 拋出異常catch 匹配,進入塊體return false,暫存返回值,控制權轉移至 finallyfinally 執行 return true,**直接覆蓋**暫存值並終止方法public static void doThings(int i) {
try {
if (i == 0) {
return; // ① 遇到 return,先暫存,執行 finally
}
int t = 100 / i;
System.out.println(t);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
System.out.println("finally"); // ② 僅做資源釋放、日誌記錄,絕不 return/throw
}
}
| 場景 | 正確做法 | 錯誤示範 |
|---|---|---|
| 資源關閉(流、連接、鎖) | finally { if (res != null) res.close(); } | finally { return; } |
| 事務回滾 | finally { if (tx != null) tx.rollback(); } | finally { throw new RuntimeException(); } |
| 需返回計算結果 | 在 try/catch 中賦值變量,finally 後統一 return result | 在 finally 中直接 return |
實現 AutoCloseable 的資源,優先使用 try-with-resources,編譯器自動生成安全的 finally 邏輯,且異常不會被吞噬:
// 自動關閉,異常保留(被抑制異常可通過 getSuppressed() 獲取)
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
// 業務邏輯
} catch (SQLException e) {
// 異常正常拋出
throw new RuntimeException(e);
}
finally 的設計初衷是保證清理代碼必執行,而非改變方法控制流。牢記:「finally 不 return,不 throw,不 continue/break」,配合 try-with-resources,將異常處理變得既安全又優雅。