你真的理解 Java 中的 try_catch_finally 嗎?
- 2019 年 10 月 5 日
- 筆記

try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗的「教訓」告訴我,這個東西可不是想像中的那麼簡單、聽話。不信?
看幾個例子,回顧一下執行順序
例子 1 無異常,finally 中的 return 會導致提前返回
public static String test() { try { System.out.println("try"); return "return in try"; } catch(Exception e) { System.out.println("catch"); return "return in catch"; } finally { System.out.println("finally"); return "return in finally"; } }
調用 test() 的結果:
try finally return in finally
例子 2 無異常,try 中的 return 會導致提前返回
public static String test() { try { System.out.println("try"); return "return in try"; } catch(Exception e) { System.out.println("catch"); } finally { System.out.println("finally"); } return "return in function"; }
調用 test() 的結果:
try finally return in try
例子 3 有異常,finally 中的 return 會導致提前返回
public static String test() { try { System.out.println("try"); throw new Exception(); } catch(Exception e) { System.out.println("catch"); return "return in catch"; } finally { System.out.println("finally"); return "return in finally"; } }
調用 test() 的結果:
try catch finally return in finally
例子 4 有異常,catch 中的 return 會導致提前返回
public static String test() { try { System.out.println("try"); throw new Exception(); } catch(Exception e) { System.out.println("catch"); return "return in catch"; } finally { System.out.println("finally"); } }
調用 test() 的結果:
try catch finally return in catch
例子 4 有異常,不會提前返回
public static String test() { try { System.out.println("try"); throw new Exception(); } catch(Exception e) { System.out.println("catch"); } finally { System.out.println("finally"); } return "return in function"; }
調用 test() 的結果:
try catch finally return in function
小結
上面這幾個例子,大多數人已經非常了解。同時也衍生出一些理論,比如不要在 finally 中 return 等,不再贅述。
再看幾個例子,返回值是否符合你的預期?
例子 1
public static int test() { try { return 1; } finally { return 2; } }
返回值:2
說明:與我們上面的例子一致,finally 中的 return 導致提前返回,try 中的 return1 不會被執行。
附編譯後的程式碼:
public static int test() { try { boolean var0 = true; return 2; } finally { ; } }
可以看到編譯器做過優化,同時驗證了 boolean 類型在底層是用 int 實現的,但注意你在源碼中直接給 int 行賦值 true 或 false 是不被允許的。
例子 2
public static int test() { int i; try { i = 3; } finally { i = 5; } return i; }
返回值:5
說明:執行 try 中的程式碼後,再執行 finally 中的程式碼,最終 i 被賦值為 5,最後返回
附編譯後的程式碼:
public static int test() { boolean var0 = true; byte i; try { var0 = true; } finally { i = 5; } return i; }
同樣可以看出,編譯器做了一些優化。
正是金九銀十跳槽季,為大家收集了2019年最新的面試資料,有文檔、有攻略、有影片。有需要的同學可以在公眾號【Java知己】,發送【面試】領取最新面試資料攻略!
例子 3
public static int test() { int i = 1; try { i = 3; return i; } finally { i = 5; } }
返回值:3
這個例子稍微有點意思,按我們通常的思維,應該還是返回 5,畢竟 finally 中把 i 賦值為 5 了嘛,然後由 try 中的 return 返回。然而很不幸,返回值是 3。
為什麼呢?先看一下編譯後的程式碼:
public static int test() { boolean var0 = true; byte var1; try { int i = 3; var1 = i; } finally { var0 = true; } return var1; }
我們會發現,finally 中的程式碼塊不起作用。不知你是否想起一點:Java 中是按值傳遞的,finally 中的 i 只是一個局部變數,finally 塊執行完畢後,局部變數便不復存在。
接著看例子:
例子 4
public static List test() { List<Integer> list = new ArrayList<>(); try { list.add(1); return list; } finally { list.add(2); } }
返回:包含 1 和 2 兩個元素的 List 對象。
說明:這個例子中,基本類型 int 被替換為引用類型 List,雖然 list 是按值傳遞,但它內部的狀態可變(體現在這裡,就是可以 add 元素)。擴展:finally 只能保證對象本身不可變,但無法保證對象內部狀態不可變。
附編譯後的程式碼:
public static List test() { ArrayList list = new ArrayList(); ArrayList var1; try { list.add(1); var1 = list; // 執行這一步操作後,var1和list指向同一個對象 } finally { list.add(2); } return var1; }
你現在應該覺得自己理解了,那麼再來看兩個例子:
例子 5
public static int test() { try { System.exit(0); } finally { return 2; } }
該函數沒有返回值。原因:jvm 提前退出了。
附編譯後的程式碼:
public static int test() { try { System.exit(0); return 2; } finally { ; } }
例子 6
public static int test() { try { while(true) { System.out.println("Infinite loop."); } } finally { return 2; } }
由於 try 中的無限循環阻塞,永遠執行不到 finally 中的程式碼塊。
附編譯後的程式碼:
public static int test() { try { while(true) { System.out.println("Infinite loop."); } } finally { ; } }
小結
為了方便說明,只舉了 finally 程式碼塊的例子,catch 程式碼塊是類似的。
總結
執行順序:
1. try 程式碼塊中 return 前面的部分 2. catch 程式碼塊中 return 前面的部分 3. finally 程式碼塊中 return 前面的部分 4. finally 的 return 或 catch 的 return 或 try 的 return。若前面的 return 被執行,會導致提前返回,同時後面的 return 被忽略。 5. 方法的其他部分
變數:
注意 Java 的按值傳遞規則
特殊情況:
注意 finally 不會被執行的情況
「不積跬步,無以至千里」,希望未來的你能:有夢為馬 隨處可棲!加油,少年!