關(guān)于Java中try finally return語(yǔ)句的執(zhí)行順序淺析
問(wèn)題分析
finally語(yǔ)句塊一定會(huì)執(zhí)行嗎?
可能很多人第一反應(yīng)是肯定要執(zhí)行的,但仔細(xì)一想,如果一定會(huì)執(zhí)行的話 也就不會(huì)這么SB的問(wèn)了。
Demo1
public class Test {
public static void main(String[] args) {
System.out.println("return value of test(): " + test());
}
public static int test() {
int i = 1;
// if (i == 1) {
// return 0;
// }
System.out.println("the previous statement of try block");
i = i / 0;
try {
System.out.println("try block");
return i;
} finally {
System.out.println("finally block");
}
}
}
Demo1的執(zhí)行結(jié)果如下:
the previous statement of try block Exception in thread "main" java.lang.ArithmeticException: / by zero at com.becoda.bkms.bus.basics.web.Test2.test(Test2.java:15) at com.becoda.bkms.bus.basics.web.Test2.main(Test2.java:5)
另外,如果去掉上例中的注釋?zhuān)瑘?zhí)行結(jié)果則是:
return value of test(): 0
以上兩種情況,finally語(yǔ)句塊都沒(méi)有執(zhí)行,說(shuō)明什么問(wèn)題?只有與finally相對(duì)應(yīng)的try語(yǔ)句塊得到執(zhí)行的情況下,finally語(yǔ)句塊才會(huì)執(zhí)行,而上面都是在try語(yǔ)句塊之前返回(return)或者拋出異常,所以try對(duì)應(yīng)的finally語(yǔ)句塊沒(méi)有執(zhí)行。那么,即使與finally相對(duì)應(yīng)的try語(yǔ)句塊得到執(zhí)行的情況下,finally語(yǔ)句塊一定會(huì)執(zhí)行嗎?但下面例子
Demo2
public class Test {
public static void main(String[] args) {
System.out.println("return value of test(): " + test());
}
public static int test() {
int i = 1;
try {
System.out.println("try block");
System.exit(0);
return i;
} finally {
System.out.println("finally block");
}
}
}
Demo2的執(zhí)行結(jié)果如下:
try block
finally語(yǔ)句塊還是沒(méi)有執(zhí)行,為什么呢?因?yàn)槲覀冊(cè)趖ry語(yǔ)句塊中執(zhí)行了System.exit(0)語(yǔ)句,終止了Java虛擬機(jī)的運(yùn)行,雖然一般情況下我們不會(huì)這么干。還有情況是當(dāng)一個(gè)線程在執(zhí)行try語(yǔ)句塊或者catch語(yǔ)句塊時(shí)被打斷(interrupted)或者被終止(killed),與其對(duì)應(yīng)的finally語(yǔ)句塊可能不會(huì)執(zhí)行。還有更極端的情況,就是在線程運(yùn)行 try 語(yǔ)句塊或者 catch 語(yǔ)句塊時(shí),突然死機(jī)或者斷電,finally 語(yǔ)句塊肯定不會(huì)執(zhí)行了。
finally 語(yǔ)句示例說(shuō)明
下面看一個(gè)簡(jiǎn)單的例子
Demo3
public class Test {
public static void main(String[] args) {
try {
System.out.println("try block");
return;
} finally {
System.out.println("finally block");
}
}
}
Demo3的執(zhí)行結(jié)果為:
try block finally block
Demo3說(shuō)明 finally 語(yǔ)句塊在 try 語(yǔ)句塊中的 return 語(yǔ)句之前執(zhí)行。我們?cè)賮?lái)看另一個(gè)例子。
Demo4
public class Test {
public static void main(String[] args) {
System.out.println("reture value of test() : " + test());
}
public static int test() {
int i = 1;
try {
System.out.println("try block");
i = 1 / 0;
return 1;
} catch (Exception e) {
System.out.println("exception block");
return 2;
} finally {
System.out.println("finally block");
}
}
}
Demo4的執(zhí)行結(jié)果為:
try block exception block finally block reture value of test() : 2
Demo4說(shuō)明了 finally 語(yǔ)句塊在 catch 語(yǔ)句塊中的 return 語(yǔ)句之前執(zhí)行。
從上面的Demo3和Demo4,我們可以看出,其實(shí)finally語(yǔ)句塊時(shí)在try或者catch中的return語(yǔ)句之前執(zhí)行的,更加一般的說(shuō)法是,finally語(yǔ)句塊應(yīng)該是在控制轉(zhuǎn)移語(yǔ)句之前執(zhí)行,控制轉(zhuǎn)移語(yǔ)句除了return外,還有break和continue。
再來(lái)看下面兩個(gè)例子
Demo5
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}
Demo5的執(zhí)行結(jié)果為:
return value of getValue(): 1
Demo6
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
public static int getValue() {
int i = 1;
try {
return i;
} finally {
i++;
}
}
}
Demo6的執(zhí)行結(jié)果為:
return value of getValue(): 1
利用我們上面分析得出的結(jié)論:finally 語(yǔ)句塊是在 try 或者 catch 中的 return 語(yǔ)句之前執(zhí)行的。 由此,可以輕松的理解Demo5 的執(zhí)行結(jié)果是 1。因?yàn)?finally 中的 return 1;語(yǔ)句要在 try 中的 return 0;語(yǔ)句之前執(zhí)行,那么 finally 中的 return 1;語(yǔ)句執(zhí)行后,把程序的控制權(quán)轉(zhuǎn)交給了它的調(diào)用者 main()函數(shù),并且返回值為 1。那為什么Demo6 的返回值不是 2,而是 1 呢?按照Demo5 的分析邏輯,finally 中的 i++;語(yǔ)句應(yīng)該在 try 中的 return i;之前執(zhí)行啊?i 的初始值為 1,那么執(zhí)行 i++;之后為 2,再執(zhí)行 return i;那不就應(yīng)該是 2 嗎?怎么變成 1 了呢?
說(shuō)明這個(gè)問(wèn)題需要了解Java虛擬機(jī)是如何編譯finally語(yǔ)句塊的。
Java方法是在棧幀中執(zhí)行,棧幀是線程私有棧的單位,執(zhí)行方法的線程會(huì)為每一個(gè)方法分配一小塊空間來(lái)作為該方法執(zhí)行時(shí)的內(nèi)存空間,棧幀分為三個(gè)區(qū)域:
1、操作數(shù)棧,用來(lái)保存正在執(zhí)行的表達(dá)式中的操作數(shù)
2、局部變量區(qū),用來(lái)保存方法中使用的變量,包括方法參數(shù),方法內(nèi)部聲明的變量,以及方法中使用到的對(duì)象的成員變量或類(lèi)的成員變量(靜態(tài)變量),最后兩種變量會(huì)復(fù)制到局部變量區(qū),因此在多線程環(huán)境下,這種變量需要根據(jù)需要聲明為volatile類(lèi)型
3、字節(jié)碼指令區(qū)
例如下面這段代碼
try{
return expression;
}finally{
do some work;
}
首先我們知道,finally語(yǔ)句是一定會(huì)執(zhí)行,但他們的執(zhí)行順序是怎么樣的呢?他們的執(zhí)行順序如下:
1、執(zhí)行:expression,計(jì)算該表達(dá)式,結(jié)果保存在操作數(shù)棧頂;
2、執(zhí)行:操作數(shù)棧頂值(expression的結(jié)果)復(fù)制到局部變量區(qū)作為返回值;
3、執(zhí)行:finally語(yǔ)句塊中的代碼;
4、執(zhí)行:將第2步復(fù)制到局部變量區(qū)的返回值又復(fù)制回操作數(shù)棧頂;
5、執(zhí)行:return指令,返回操作數(shù)棧頂?shù)闹担?/p>
我們可以看到,在第一步執(zhí)行完畢后,整個(gè)方法的返回值就已經(jīng)確定了,由于還要執(zhí)行finally代碼塊,因此程序會(huì)將返回值暫存在局部變量區(qū),騰出操作數(shù)棧用來(lái)執(zhí)行finally語(yǔ)句塊中代碼,等f(wàn)inally執(zhí)行完畢,再將暫存的返回值又復(fù)制回操作數(shù)棧頂。所以無(wú)論finally語(yǔ)句塊中執(zhí)行了什么操作,都無(wú)法影響返回值,所以試圖在finally語(yǔ)句塊中修改返回值是徒勞的。因此,finally語(yǔ)句塊設(shè)計(jì)出來(lái)的目的只是為了讓方法執(zhí)行一些重要的收尾工作,而不是用來(lái)計(jì)算返回值的。
這樣就能解釋Demo6的問(wèn)題了
讓我們?cè)賮?lái)看以下 3 個(gè)例子。
Demo7
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
@SuppressWarnings("finally")
public static int getValue() {
int i = 1;
try {
i = 4;
} finally {
i++;
return i;
}
}
}
Demo7的執(zhí)行結(jié)果為:
return value of getValue(): 5
Demo8
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
public static int getValue() {
int i = 1;
try {
i = 4;
} finally {
i++;
}
return i;
}
}
Demo8的執(zhí)行結(jié)果為:
return value of getValue(): 5
Demo9
public class Test {
public static void main(String[] args) {
System.out.println(test());
}
public static String test() {
try {
System.out.println("try block");
return test1();
} finally {
System.out.println("finally block");
}
}
public static String test1() {
System.out.println("return statement");
return "after return";
}
}
Demo9的執(zhí)行結(jié)果為:
try block return statement finally block after return
總結(jié):
1、finally 語(yǔ)句塊不一定會(huì)被執(zhí)行
2、finally 語(yǔ)句塊在 try 語(yǔ)句塊中的 return 語(yǔ)句之前執(zhí)行
3、finally 語(yǔ)句塊在 catch 語(yǔ)句塊中的 return 語(yǔ)句之前執(zhí)行
4、finally 語(yǔ)句塊中的 return 語(yǔ)句會(huì)覆蓋 try 塊中的 return 返回
5、試圖在 finally 語(yǔ)句塊中修改返回值不一定會(huì)被改變
- 淺談Java finally語(yǔ)句到底是在return之前還是之后執(zhí)行(必看篇)
- 談?wù)凧ava中try-catch-finally中的return語(yǔ)句
- Java異常處理中同時(shí)有finally和return語(yǔ)句的執(zhí)行問(wèn)題
- 淺談Java中return和finally的問(wèn)題
- java異常處理執(zhí)行順序詳解try catch finally
- 詳解Java Web項(xiàng)目啟動(dòng)執(zhí)行順序
- java中靜態(tài)代碼塊與構(gòu)造方法的執(zhí)行順序判斷
- 詳解Java中finally和return的執(zhí)行順序
相關(guān)文章
eclipse啟動(dòng)出現(xiàn)“failed to load the jni shared library”問(wèn)題解決
這篇文章主要介紹了eclipse啟動(dòng)出現(xiàn)“failed to load the jni shared library”問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Java使用TCP實(shí)現(xiàn)數(shù)據(jù)傳輸實(shí)例詳解
這篇文章主要介紹了Java使用TCP實(shí)現(xiàn)數(shù)據(jù)傳輸實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06
利用Java實(shí)現(xiàn)網(wǎng)站聚合工具
互聯(lián)網(wǎng)上有數(shù)以萬(wàn)億計(jì)的網(wǎng)站,每個(gè)網(wǎng)站大都具有一定的功能。搜索引擎雖然對(duì)互聯(lián)網(wǎng)上的部分網(wǎng)站建立了索引,但是其作為一個(gè)大而全的搜索系統(tǒng),無(wú)法很好的定位到一些特殊的需求。因此本文將介紹一個(gè)用java實(shí)現(xiàn)的網(wǎng)站數(shù)據(jù)聚合工具,需要的可以參考一下2022-01-01
MyBatis動(dòng)態(tài)SQL中的trim標(biāo)簽的使用方法
這篇文章主要介紹了MyBatis動(dòng)態(tài)SQL中的trim標(biāo)簽的使用方法,需要的朋友可以參考下2017-05-05
出現(xiàn)java.lang.NoSuchMethodException異常的解決(靠譜)
這篇文章主要介紹了出現(xiàn)java.lang.NoSuchMethodException異常的解決方案(靠譜),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
Intellij IDEA 2018配置Java運(yùn)行環(huán)境的方法步驟
這篇文章主要介紹了Intellij IDEA 2018配置Java運(yùn)行環(huán)境的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

