關於finally塊的運行順序,在學習階段不是很明確。查詢了一些資料後,大概可以得出以下結論:“finally塊是在下級函數向上級調用的函數跳轉前(無論是通過return還是throw exception的方式回到調用函數)執行”,具體參照以下兩個示例。
public static void main(String[] args) {
test t=new test();
System.out.println(t.f());
}
public String f() {
try{
System.out.println("try block");
return f2();
}
finally{
System.out.println("finally block");
return "finally";
}
}
public String f2(){
System.out.println("returning");
return "return";
}
此時的輸出結果爲
try block
returning
finally block
finally
分析:try塊執行,運行到f()中的return**這行,按慣例先執行return語句後半部分(用於計算return的返回值),輸出returning之後,開始正式執行return語句*,將返回值*return存在一個臨時區域裏,並在程序正式由f()跳轉回main()之前,執行finally塊的語句。若finally塊內有返回值,則將此返回值替換原來臨時變量區內的返回值。因此最後輸出的不是return而是finally。
類似的:
public static void main(String[] args) {
test t=new test();
try{
System.out.println(t.f());
}
catch(Exception e){}
}
public String f() {
try{
System.out.println("try block");
int a=5/0;
}
catch(Exception e){
System.out.println("exception block");
throw e;
//return "exception"
}
finally{
System.out.println("finally block");
return "finally";
}
輸出結果仍爲:
try block
exception block
finally block
finally
此時由catch塊捕獲並拋出異常,但finally塊仍把“finally”作爲返回值返回給了main函數,並由main函數打印出來。用註釋掉的return部分替換原有的catch模塊,結果也是一樣的。
但是在finally塊中修改返回值對應的變量(而不調用return語句),實際的返回值不會被修改。
public static void main(String[] args) {
test t=new test();
try{
System.out.println(t.f());
}
catch(Exception e){}
}
public String f() {
String s;
try{
System.out.println("try block");
return s="return";
}
finally{
System.out.println("finally block");
s="finally";
}
}
輸出爲
try block
finally block
return
可見最後被返回的變量s沒有被修改成finally。這裏要說道JAVA的值傳遞特性。JAVA沒有真正意義上(像C++)一樣的引用傳遞。而由於string類型的不可變性,對s的修改都會採用新建一個字符串並將s引用(指針)修改到新建字符串上。然而,try塊中調用的return已經反回了s的值(存在臨時區域內),即return字符串對應的地址,此時將s的值修改了也沒有實際作用。
考慮JAVA的值傳遞特性,我們可以推測,對於int之類的基礎類型的返回值,在finally塊修改而不return返回值變量,對函數返回值沒有影響(因爲return已經將變量內存儲的值存入臨時區域內)。但是對於class類型的變量返回值,如果在finally塊內修改類的成員變量等,真正的返回值中的成員變量也會對應被修改。