Java異常機制--try catch finally 執行順序詳解
引言
關於try catch finally 執行順序的筆試面試題目非常的多,我曾經在牛客網刷題的時候不止一次的碰到過,而且不止一次的做錯過,這裡面需要涉及的細節如果不弄清楚每次做題就會產生似是而非的感覺。這次查閱了很多相關資料,關於try catch finally 執行順序各方面基本都講到了。不足之處歡迎指出。
try catch finally 執行順序
僅僅在下面4中情況下不會執行finally語句 :
①.如果在try 或catch語句中執行了System.exit(0)。
②.在執行finally之前jvm崩潰了。
③.try語句中執行死迴圈。
④.電源斷電。
除了以上的四種情況外,finally語句都會執行,finally語句執行時會有以下原則。
①、不管有沒有出現異常,finally塊中程式碼都會執行;
public void demo1(){
try {
System.out.println(result);
} catch (Exception e) {
System.out.println(e.getMessage());
}
finally {
System.out.println("finally trumps. " );
}
//輸出結果為:
result
finally trumps .
上面程式碼可知如果未出現異常是順序執行try和finally程式碼塊。
②、當try和catch中有return時,finally仍然會執行;
public static int demo2() {
try {
return 0;
}
finally {
System.out.println("finally trumps return.");
}
}
//輸出結果
finally trumps return.
0
當finally裡面沒有return語句是,執行try 和finally語句之後最後再執行return。
③、finally是在return後面的表示式運算後執行的(此時並沒有返回運算後的值,而是先把要返回的值儲存起來,管finally中的程式碼怎麼樣,返回的值都不會改變,任然是之前儲存的值),所以函式返回值是在finally執行前確定的;
public static int demo3()
{
int i = 0;
try {
i = 2;
return i;
} finally {
i = 12;
System.out.println("finally trumps return.");
}
}
//輸出結果
finally trumps return.
2
此處中finally中對i賦值12但是demo3的返回值仍然是2,也就是在finally中對i賦值並未改變i的返回值,這裡需要詳細的講一下,此處涉及到了jvm機制。先給出上面程式碼的位元組碼然後給出圖解:
public static demo3()I
TRYCATCHBLOCK L0 L1 L2
L3
LINENUMBER 12 L3
ICONST_0
ISTORE 0
L0
LINENUMBER 14 L0
ICONST_2
ISTORE 0
L4
LINENUMBER 15 L4
ILOAD 0
ISTORE 2
L1
LINENUMBER 17 L1
BIPUSH 12
ISTORE 0
L5
LINENUMBER 18 L5
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "finally trumps return."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
LINENUMBER 15 L6
ILOAD 2
IRETURN
L2
LINENUMBER 16 L2
FRAME FULL [I] [java/lang/Throwable]
ASTORE 1
L7
LINENUMBER 17 L7
BIPUSH 12
ISTORE 0
L8
LINENUMBER 18 L8
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "finally trumps return."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L9
LINENUMBER 19 L9
ALOAD 1
ATHROW
上面的位元組碼比較長,下面簡要的講一下,其實我在http://blog.csdn.net/u013309870/article/details/72935274這篇文章中詳細的講過方法中程式碼的執行過程,
在variable記憶體中有兩個變數區域一個是用來存放i的值,對應最上面的那個,另一個用於存放返回值。在上面程式碼執行到i = 2; return i;
先對i賦值2,然後執行return語句此時並不是將結果返回,而是將i=2的值儲存到返回值變數區域,在執行完i=12時,再返回variable中返回值地址變數區域的2。
④、finally中最好不要包含return,否則程式會提前退出,返回值不是try或catch中儲存的返回值。
public static int demo4() {
int i = 0;
try {
return i;
} finally {
i = 12;
System.out.println("finally trumps return.");
return i;
}
}
//輸出結果
finally trumps return.
12
上面為什麼會返回12呢?因為在程式還未執行try中的return語句時就先執行了finally裡面的return語句所以返回結果為12。
經典面試題
下面題目輸出什麼?
public static int demo5() {
try {
return printX();
}
finally {
System.out.println("finally trumps return... sort of");
}
}
public static int printX() {
System.out.println("X");
return 0;
}
輸出結果:
X
finally trumps return... sort of
0
上面這道題目含金量很高,程式順序執行時先執行printX()函式,此時得到返回值0並且將0儲存到variable中對應的用於儲存返回值的區域,此時程式在執行finally語句因為finally語句中沒有return語句,所以程式將返回值區域的0返回給上一級函式。