三種退出代碼塊的方式
- 正常執行完畢:順序執行到右大括號
- return:直接返回調用者,
finally 仍會執行
- throw:拋出異常,沿調用棧向上尋找匹配的
catch,finally 仍會執行
實戰代碼演示
public class TryThrowDemo {
public static void test1() {
System.out.println("test1() 執行");
test2();
System.out.println("test1() 執行完 test2 之後");
}
public static void test2() {
System.out.println("test2() 進入");
try {
test3(true);
System.out.println("test2() try 後面的內容"); // 不會執行
} catch (Exception e) {
System.out.println("test2() catch (Exception e)");
e.printStackTrace();
}
System.out.println("test2() try-catch 後面的內容..."); // 會執行
}
public static void test3(boolean isThrow) throws Exception {
System.out.println("進入 test3()。。。");
try {
if (isThrow) {
throw new Exception("test3() 主動拋出的異常");
}
System.out.println("test3() 拋出異常以後的 try 內容..."); // 不會執行
} catch (Exception e) {
System.out.println("test3() catch (Exception e)");
throw e; // 重新拋出,繼續向上傳播
} finally {
System.out.println("test3() finally 執行"); // 必須執行
}
System.out.println("test3() try-catch-finally 後面的內容..."); // 不會執行(因為重拋異常未捕獲)
}
public static void main(String[] args) {
test1();
}
}
執行輸出流程分析
test1() 執行
test2() 進入
進入 test3()。。。
test3() catch (Exception e)
test3() finally 執行
test2() catch (Exception e)
java.lang.Exception: test3() 主動拋出的異常
at TryThrowDemo.test3(TryThrowDemo.java:29)
at TryThrowDemo.test2(TryThrowDemo.java:16)
at TryThrowDemo.test1(TryThrowDemo.java:9)
at TryThrowDemo.main(TryThrowDemo.java:44)
test2() try-catch 後面的內容...
test1() 執行完 test2 之後
關鍵知識點總結
| 場景 | finally 執行? | 後續代碼執行? | 異常傳播? |
| try 正常結束 | 是 | 是 | 否 |
| try 中 return | 是 (在 return 前) | 否 | 否 |
| try 中 throw,catch 未重拋 | 是 | 是 | 否 (被捕獲消化) |
| try 中 throw,catch 重拋 | 是 | 否 | 是 (向上傳播) |
| catch 中 return/throw | 是 (在 return/throw 前) | 否 | 視情況 |
| finally 中 return/throw | --- | --- | 覆蓋原有返回/異常,極度不推薦 |
最佳實踐原則
- 資源釋放放 finally:流關閉、鎖釋放、連接歸還
- catch 要么處理要么重拋:別吞掉異常(空 catch),至少打印堆疊
- 別在 finally 裡 return/throw:會掩蓋原始異常,導致排查極難
- try-with-resources (Java 7+):自動關閉實現
AutoCloseable 的資源,更簡潔安全
- 異常鏈保留:重拋時用
throw new RuntimeException("msg", e) 保留原因