前言
??前一段時(shí)間校招閱卷,發(fā)現(xiàn)一道有關(guān)try-catch-finally&return執(zhí)行順序題目,其實(shí)之前也有遇到,一直沒太在意,現(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é)果如下:

結(jié)果分析
??執(zhí)行順序想必都是沒有疑問的,因?yàn)槲覀兌贾纅inally中的代碼總會(huì)被執(zhí)行,即:任何執(zhí)行try 或者catch中的return語句之前,都會(huì)先執(zhí)行finally語句,如果finally存在的話。如果finally中有return語句,那么程序就return了,所以finally中的return是一定會(huì)被return的,編譯器把finally中的return視為為一個(gè)warning【所以釋放資源的代碼一般放在finally中】。問題在于:為何最終結(jié)果是3?
??在思考這個(gè)問題前,我又陸續(xù)做了以下幾個(gè)實(shí)驗(yàn):
- 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é)果如下:

- 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é)果如下:

- 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é)果如下:

- 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é)果如下:

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