先上源碼:
public class TestRef {
public static void main(String[] args) {
int c = count(0, 20);
System.out.println(c);
}
public static int count(int a, int b) {
try {
return add(a, b);
} catch (RuntimeException e) {
e.printStackTrace();
return 0;
} finally {
System.out.println("回收資源");
}
}
private static int add(int a, int b) {
if (a == 0) {
throw new RuntimeException();
}
return a + b;
}
}
再看字節(jié)碼
{
public static void main(java.lang.String[]);
Code:
stack=2, locals=2, args_size=1
0: iconst_0 //將常量0推到棧頂
1: bipush 20 //將直接操作數(shù)20 推到棧頂
3: invokestatic #2 // Method count:(II)I 使用前2行推入的參數(shù)調(diào)用count靜態(tài)方法
6: istore_1 //將count方法的結(jié)果存到局部變量表1號位
7: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #4 // Method java/io/PrintStream.println:(I)V 7、10、11三行是輸出count的結(jié)果
14: return
public static int count(int, int);
Code:
stack=2, locals=5, args_size=2
0: iload_0 //加載局部變量表0號位到棧頂 靜態(tài)方法沒有this 局部變量表從0開始存
1: iload_1 //加載局部變量表1號位到棧頂
2: invokestatic #5 // Method add:(II)I 調(diào)用靜態(tài)方法 add
5: istore_2 //將棧頂元素存到局部變量表2號位,這里是存add結(jié)果
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: ldc #6 // String 回收資源
11: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 6、9、11三行是finally塊
14: iload_2 //將局部變量2號位載入棧頂
15: ireturn //返回棧頂int類型元素
16: astore_2 //將對象引用存到局部變量表2號位
17: aload_2 //加載局部變量表2號位的對象引用到棧頂
18: invokevirtual #9 // Method java/lang/RuntimeException.printStackTrace:()V
21: iconst_0 //推送常量0到棧頂
22: istore_3 //存儲棧頂元素到局部變量表3號位
23: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
26: ldc #6 // String 回收資源 加載運行時常量池#6到棧頂
28: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
31: iload_3
32: ireturn
33: astore 4
35: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
38: ldc #6 // String 回收資源
40: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
43: aload 4
45: athrow //拋出異常
Exception table:
from to target type
0 6 16 Class java/lang/RuntimeException
0 6 33 any
16 23 33 any
33 35 33 any
public static int add(int, int);
Code:
stack=2, locals=2, args_size=2
0: iload_0
1: ifne 12 //如果棧頂元素小于0跳轉(zhuǎn)到12行
4: new #8 // class java/lang/RuntimeException
7: dup //復(fù)制上一個元素。new對象后一般都有這一步。因為初始化對象會消耗一個引用
8: invokespecial #10 // Method java/lang/RuntimeException."<init>":()V
11: athrow
12: iload_0
13: iload_1
14: iadd
15: ireturn
}
count方法相當?shù)膹?fù)雜 按順序看 是肯定不行的。 得結(jié)合Exception table異常表來看。異常表的順序是對應(yīng)catch的。from 0 to 6表示的是0-5這幾行,不含6的。
0-5行做的是add(a, b); 6-11行是finally塊 14,15行是返回add方法的值。這是沒有異常的時候的執(zhí)行流程。
異常表有2個from 0 to 6 的catch 第一個是我們定義的RuntimeException第二個是any 代表所有類型的異常。 這2個catch 的處理方法是不一樣的。target就是處理行開始。如果在0-5行發(fā)生運行時異常,就跳到16行處理。 16行 astore_2 將棧頂元素存儲到2號位,這里的棧頂元素是e 就是jvm傳入的異常對象的引用,16-32行 可以看做是一個塊,打印異常棧,調(diào)用finally返回元素0(0在調(diào)用finally 之前已經(jīng)存入了局部變量表,但是在執(zhí)行了finnaly之后才從局部變量表取出返回) 這里可以發(fā)現(xiàn)沒有使用任何的跳轉(zhuǎn)語句 比如 goto jsr ret 所以finally的代碼塊相當于重復(fù)了一次。
第三個catch from 16 to 23 這就是 catch (RuntimeException e) 里面的代碼,只是不含返回,如果這里發(fā)生了異常會跳轉(zhuǎn)到33行去執(zhí)行。33行是保存異常對象引用,然后35-40行又是finally的內(nèi)存,,然后43-45 就是把異常拋出到上層了。第三次重復(fù)finally
第四個catch是from 33 to 35,這中間只有一行代碼就是33的 astore 4 如果這行發(fā)生異常,會不斷的重試。不斷的跳回到33這行。成功后做的事情就和第三個catch一樣了。
第2、3、4 3個catch都是隱式的 幫我們做了 沒有用代碼寫出來的內(nèi)容:拋出未catch異常到上層