Java 的 try-catch 寫法咱們常常用到,按照固定的方式咱們能夠很容易捕獲和處理異常:java
try {
....
} catch( e ) {
...
} finally {}
複製代碼
可是,咱們會在某些寫法上出現誤區,這裏將這些誤區稱爲陷阱,本文會具體分析這些陷阱的產生原理,避免你們未來入坑。bash
//demo1
public static int testTryCatch() {
try {
int i = 1/0;// step1
} catch(Exception e) { //2
System.out.println("hello!");
return 1;
}finally{
return 2;
}
}
複製代碼
在 demo1
代碼的 step1
位置,發生了個除以 0
的異常,按照代碼的邏輯將走到 catch
代碼塊。catch
代碼塊執行了一個 return
指令返回了字面量 1
。可是在 finally
語句塊中也執行了一個 return
指令,那麼最後函數將返回那個值呢?函數
咱們執行該函數將獲得結果:ui
>"hello!"
>2
複製代碼
也就是執行finally
語句塊中的指令,這是爲何呢?咱們來看下這段代碼編譯出來的字節碼:spa
Code:
stack=2, locals=1, args_size=0
0: iconst_1
1: iconst_0
2: idiv
3: istore_0
4: goto 20
7: astore_0
8: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc #22 // String hello!
13: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: goto 20
19: pop
20: iconst_2
21: ireturn
Exception table:
from to target type
0 4 7 Class java/lang/Exception
0 19 19 any
複製代碼
上面 JVM
字節碼中 0~2
行表明 try
語句塊, 當執行到 idiv
除法指令的時候,出現了異常。這時候咱們須要查一下咱們的異常向量表,異常向量表包含兩條數據:code
Exception
類型數據,跳轉到第 7 條指令Throwable
,跳轉到 19 語句塊try
語句塊中的 idiv
指令位於第 2 行,屬於區間 [0,4] ,所以,當該語句發生異常的時候將執行向量表的第一條記錄,跳轉到代碼 7 。astore_0
,將一個引用對象(其實是一個 Exception
對象)存入局部變量表,而後執行 System.out.println
指令。以後,並無按照咱們的代碼寫法直接執行 return 1
, return
指令被捨棄,而是跳轉到 代碼行 20 的位置,也就是 finally
代碼塊ireturn
指令返回。所以,實際上編譯器在編譯咱們的
try-catch
語法代碼的時候,當發現咱們的finally
中有return
語句的時候,將捨棄掉catch
代碼塊中的ireturn
指令對象
//code 2
public static int testFunc(MyObj obj) {
try {
int i = 1/0;
return 10;
} catch(Exception e) { //2
obj.v= 2;
return obj.v;
}finally{
obj.v= 3;
}
}
複製代碼
經過咱們對 例子1 的分析,咱們知道 finally
語句將會插入到 catch
語句的 return
代碼以前。那麼按照這種結論咱們的 例子2 就能夠轉化爲:索引
catch(Exception e) {
obj.v = 2;
obj.v= 3;//finally 語句塊
return obj.v;
}
複製代碼
也就是說 例子2 中函數的結果應該是: 3 。 可是,結論老是事與願違的,實際上返回的是 2。咱們仍是看下它編譯的字節碼:ip
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: iconst_0
2: idiv //異常
3: istore_1
4: aload_0
5: iconst_3
6: putfield #16 // Field david/support/Demos$MyObj.v:I
9: bipush 10
11: ireturn //catch 代碼塊
12: astore_1
13: aload_0
14: iconst_2
15: putfield #16 // Field david/support/Demos$MyObj.v:I
18: aload_0
19: getfield #16 // Field david/support/Demos$MyObj.v:I
22: istore_3
23: aload_0
24: iconst_3
25: putfield #16 // Field david/support/Demos$MyObj.v:I
28: iload_3
29: ireturn
30: astore_2
31: aload_0
32: iconst_3
33: putfield #16 // Field david/support/Demos$MyObj.v:I
36: aload_2
37: athrow
Exception table:
from to target type
0 4 12 Class java/lang/Exception
0 4 30 any
12 23 30 any
複製代碼
try
語句塊中的除法指令,對應代碼第 2 行的時候發生的異常。 對應第 0 條向量表,跳轉到 12 行 catch
語句塊Demos$MyObj
對象的 v
屬性中Demos$MyObj
對象的 v
屬性,存入到索引 [3] 的局部變量中finally
指令塊,將對象的 v
屬性變成 3從上面的編譯的代碼咱們能夠看到,
例子2
中,finally
語句塊中並無return
指令的時候,編譯器將會先把結果存入一個臨時變量(上面的索引 [3] 局部變量)中,而後執行完finally
語句後,讀取臨時變量中的值,執行return
語句,而且,此時對象中的屬性也由於執行了finally語句,而變成了 3 。get