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 代碼塊是類似的。
總結
執行順序:
- try 代碼塊中 return 前面的部分
- catch 代碼塊中 return 前面的部分
- finally 代碼塊中 return 前面的部分
- finally 的 return 或 catch 的 return 或 try 的 return。若前面的 return 被執行,會導致提前返回,同時後面的 return 被忽略。
- 方法的其他部分
變量:
注意 Java 的按值傳遞規則
特殊情況:
注意 finally 不會被執行的情況
“不積跬步,無以至千里”,希望未來的你能:有夢爲馬 隨處可棲!加油,少年!
關注公衆號:「Java 知己」,每天更新Java知識哦,期待你的到來!
- 發送「1024」,免費領取 30 本經典編程書籍。
- 發送「Group」,與 10 萬程序員一起進步。
- 發送「面試」,領取BATJ面試資料、面試視頻攻略。
- 發送「JavaEE 實戰」,領取《JavaEE 實戰》系列視頻教程。
- 發送「玩轉算法」,領取《玩轉算法》系列視頻教程。