前言
??前一段時間校招閱卷,發(fā)現(xiàn)一道有關(guān)try-catch-finally&return執(zhí)行順序題目,其實之前也有遇到,一直沒太在意,現(xiàn)總結(jié)記錄,并記錄有該題目引發(fā)有關(guān)基礎(chǔ)的思考——為何說java是值調(diào)用,而非引用調(diào)用。
具體題目
public class tryCatch {
public static void main(String[] args) {
int a = 1, b = 1;
System.out.println(add(a++, ++b));
}
public static int add(int a, int b) {
int c = a + b;
try {
return c;
} finally {
System.out.println("finally語句塊");
c = 0;
}
}
}
??上述的執(zhí)行結(jié)果如下:

執(zhí)行結(jié)果.png
結(jié)果分析
??執(zhí)行順序想必都是沒有疑問的,因為我們都知道finally中的代碼總會被執(zhí)行,即:任何執(zhí)行try 或者catch中的return語句之前,都會先執(zhí)行finally語句,如果finally存在的話。如果finally中有return語句,那么程序就return了,所以finally中的return是一定會被return的,編譯器把finally中的return視為為一個warning【所以釋放資源的代碼一般放在finally中】。問題在于:為何最終結(jié)果是3?
??在思考這個問題前,我又陸續(xù)做了以下幾個實驗:
- 1、 try中帶有return
public static void main(String[] args) {
//int a = 1, b = 1;
//System.out.println(add(a++, ++b));
int i=testReturn1() ;
System.out.println("i:"+i);
}
private static int testReturn1() {
int i = 1;
try {
i++;
System.out.println("try:" + i);
return i;
} catch (Exception e) {
i++;
System.out.println("catch:" + i);
return i;
} finally {
i++;
System.out.println("finally:" + i);
}
}
??上述的執(zhí)行結(jié)果如下:

執(zhí)行結(jié)果.png
- 2、非基本類型
public static void main(String[] args) {
//int a = 1, b = 1;
//System.out.println(add(a++, ++b));
//int i=testReturn1() ;
//System.out.println("i:"+i);
List<Integer> list=testReturn2();
System.out.println("list:"+list);
}
private static List<Integer> testReturn2() {
List<Integer> list = new ArrayList<>();
try {
list.add(1);
System.out.println("try:" + list);
return list;
} catch (Exception e) {
list.add(2);
System.out.println("catch:" + list);
return list;
} finally {
list.add(3);
System.out.println("finally:" + list);
}
}
??上述的執(zhí)行結(jié)果如下:

執(zhí)行結(jié)果.png
- 3、catch中帶有return
public static void main(String[] args) {
//int a = 1, b = 1;
//System.out.println(add(a++, ++b));
int i=testReturn3() ;
System.out.println("i:"+i);
//List<Integer> list=testReturn2();
//System.out.println("list:"+list);
}
private static int testReturn3() {
int i = 1;
try {
i++;
System.out.println("try:" + i);
int x = i / 0 ;
} catch (Exception e) {
i++;
System.out.println("catch:" + i);
return i;
} finally {
i++;
System.out.println("finally:" + i);
}
return i;
}
??上述的執(zhí)行結(jié)果如下:

執(zhí)行結(jié)果.png
- 4、finally中帶有return
public static void main(String[] args) {
//int a = 1, b = 1;
//System.out.println(add(a++, ++b));
int i=testReturn4() ;
System.out.println("i:"+i);
//List<Integer> list=testReturn2();
//System.out.println("list:"+list);
}
private static int testReturn4() {
int i = 1;
try {
i++;
System.out.println("try:" + i);
return i;
} catch (Exception e) {
i++;
System.out.println("catch:" + i);
return i;
} finally {
i++;
System.out.println("finally:" + i);
return i;
}
}
??上述的執(zhí)行結(jié)果如下:

執(zhí)行結(jié)果.png
基于上述實驗的總結(jié)與思考
- finally中的代碼總會被執(zhí)行。
- 當try、catch中有return時,也會執(zhí)行finally。return的時候,要注意返回值的類型,是否受到finally中代碼的影響。
- finally中有return時,會直接在finally中退出,導(dǎo)致try、catch中的return失效。
-
重點來了:讓我們仔細對比 上述 案例中的 1 & 2.
??我們都知道:java方法參數(shù)共有2種,即:基本數(shù)據(jù)類型與對象引用。根據(jù)《Java核心技術(shù)》一書中,我們知道,Java是按照值調(diào)用的,即:方法得到的所有參數(shù)值都是一根拷貝,特別是:方法不能修改傳遞給它的任何參數(shù)變量的內(nèi)容。這時有人會反駁:那么參數(shù)是集合類型的,內(nèi)容為何會被修改?注意了:因為傳入的是該集合的引用,通過該引用可以改變該引用指向的內(nèi)容,但是該引用是沒有被修改的。
??有了上面的共同認知,我們繼續(xù)。對于1 & 2都是在一個方法里,為何基本數(shù)據(jù)類型和引用數(shù)據(jù)類型結(jié)果不那么一致呢?【從結(jié)果上看,基本數(shù)據(jù)類型,finally對于局部變量的操作無效,】這里大膽猜測,注意是猜測。有木有發(fā)現(xiàn):try & finally分別擁有一個語句塊:{ }。
??上述的運行順序應(yīng)該如下:
fun():
var x=0
var y=fun1(x)
fun2(y)
return y
fun1(var x):
x++
return x
fun2(var y):
y++
??其中,fun1相當于try代碼塊的執(zhí)行,fun2相當于finally代碼塊的執(zhí)行。你品,仔細品。如果是引用類型是不是fun2就有效,基本數(shù)據(jù)類型就不會生效呢?[切記:Java是值調(diào)用!也就是說fun2中對于y的修改其實沒有修改y,是自己的棧內(nèi)有一新的變量,其值=y++,但是fun1的y不受其影響]