先上源碼:
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號(hào)位
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號(hào)位到棧頂 靜態(tài)方法沒(méi)有this 局部變量表從0開(kāi)始存
1: iload_1 //加載局部變量表1號(hào)位到棧頂
2: invokestatic #5 // Method add:(II)I 調(diào)用靜態(tài)方法 add
5: istore_2 //將棧頂元素存到局部變量表2號(hào)位,這里是存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號(hào)位載入棧頂
15: ireturn //返回棧頂int類(lèi)型元素
16: astore_2 //將對(duì)象引用存到局部變量表2號(hào)位
17: aload_2 //加載局部變量表2號(hào)位的對(duì)象引用到棧頂
18: invokevirtual #9 // Method java/lang/RuntimeException.printStackTrace:()V
21: iconst_0 //推送常量0到棧頂
22: istore_3 //存儲(chǔ)棧頂元素到局部變量表3號(hào)位
23: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
26: ldc #6 // String 回收資源 加載運(yùn)行時(shí)常量池#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ù)制上一個(gè)元素。new對(duì)象后一般都有這一步品洛。因?yàn)槌跏蓟瘜?duì)象會(huì)消耗一個(gè)引用
8: invokespecial #10 // Method java/lang/RuntimeException."<init>":()V
11: athrow
12: iload_0
13: iload_1
14: iadd
15: ireturn
}
count方法相當(dāng)?shù)膹?fù)雜 按順序看 是肯定不行的树姨。 得結(jié)合Exception table異常表來(lái)看摩桶。異常表的順序是對(duì)應(yīng)catch的。from 0 to 6表示的是0-5這幾行帽揪,不含6的硝清。
0-5行做的是add(a, b); 6-11行是finally塊 14,15行是返回add方法的值转晰。這是沒(méi)有異常的時(shí)候的執(zhí)行流程芦拿。
異常表有2個(gè)from 0 to 6 的catch 第一個(gè)是我們定義的RuntimeException第二個(gè)是any 代表所有類(lèi)型的異常。 這2個(gè)catch 的處理方法是不一樣的查邢。target就是處理行開(kāi)始蔗崎。如果在0-5行發(fā)生運(yùn)行時(shí)異常,就跳到16行處理扰藕。 16行 astore_2 將棧頂元素存儲(chǔ)到2號(hào)位缓苛,這里的棧頂元素是e 就是jvm傳入的異常對(duì)象的引用,16-32行 可以看做是一個(gè)塊邓深,打印異常棧未桥,調(diào)用finally返回元素0(0在調(diào)用finally 之前已經(jīng)存入了局部變量表,但是在執(zhí)行了finnaly之后才從局部變量表取出返回) 這里可以發(fā)現(xiàn)沒(méi)有使用任何的跳轉(zhuǎn)語(yǔ)句 比如 goto jsr ret 所以finally的代碼塊相當(dāng)于重復(fù)了一次庐完。
第三個(gè)catch from 16 to 23 這就是 catch (RuntimeException e) 里面的代碼钢属,只是不含返回,如果這里發(fā)生了異常會(huì)跳轉(zhuǎn)到33行去執(zhí)行门躯。33行是保存異常對(duì)象引用淆党,然后35-40行又是finally的內(nèi)存,讶凉,然后43-45 就是把異常拋出到上層了染乌。第三次重復(fù)finally
第四個(gè)catch是from 33 to 35,這中間只有一行代碼就是33的 astore 4 如果這行發(fā)生異常懂讯,會(huì)不斷的重試荷憋。不斷的跳回到33這行。成功后做的事情就和第三個(gè)catch一樣了褐望。
第2勒庄、3、4 3個(gè)catch都是隱式的 幫我們做了 沒(méi)有用代碼寫(xiě)出來(lái)的內(nèi)容:拋出未catch異常到上層