先上源码:
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;
}
}
再看字节码
{
public static void main(java.lang.String[]);
Code:
stack=2, locals=2, args_size=1
0: iconst_0 //将常量0推到栈顶
1: bipush 20 //将直接操作数20 推到栈顶
3: invokestatic #2 // Method count:(II)I 使用前2行推入的参数调用count静态方法
6: istore_1 //将count方法的结果存到局部变量表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的结果
14: return
public static int count(int, int);
Code:
stack=2, locals=5, args_size=2
0: iload_0 //加载局部变量表0号位到栈顶 静态方法没有this 局部变量表从0开始存
1: iload_1 //加载局部变量表1号位到栈顶
2: invokestatic #5 // Method add:(II)I 调用静态方法 add
5: istore_2 //将栈顶元素存到局部变量表2号位,这里是存add结果
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跳转到12行
4: new #8 // class java/lang/RuntimeException
7: dup //复制上一个元素。new对象后一般都有这一步。因为初始化对象会消耗一个引用
8: invokespecial #10 // Method java/lang/RuntimeException."<init>":()V
11: athrow
12: iload_0
13: iload_1
14: iadd
15: ireturn
}
count方法相当的复杂 按顺序看 是肯定不行的。 得结合Exception table异常表来看。异常表的顺序是对应catch的。from 0 to 6表示的是0-5这几行,不含6的。
0-5行做的是add(a, b); 6-11行是finally块 14,15行是返回add方法的值。这是没有异常的时候的执行流程。
异常表有2个from 0 to 6 的catch 第一个是我们定义的RuntimeException第二个是any 代表所有类型的异常。 这2个catch 的处理方法是不一样的。target就是处理行开始。如果在0-5行发生运行时异常,就跳到16行处理。 16行 astore_2 将栈顶元素存储到2号位,这里的栈顶元素是e 就是jvm传入的异常对象的引用,16-32行 可以看做是一个块,打印异常栈,调用finally返回元素0(0在调用finally 之前已经存入了局部变量表,但是在执行了finnaly之后才从局部变量表取出返回) 这里可以发现没有使用任何的跳转语句 比如 goto jsr ret 所以finally的代码块相当于重复了一次。
第三个catch from 16 to 23 这就是 catch (RuntimeException e) 里面的代码,只是不含返回,如果这里发生了异常会跳转到33行去执行。33行是保存异常对象引用,然后35-40行又是finally的内存,,然后43-45 就是把异常抛出到上层了。第三次重复finally
第四个catch是from 33 to 35,这中间只有一行代码就是33的 astore 4 如果这行发生异常,会不断的重试。不断的跳回到33这行。成功后做的事情就和第三个catch一样了。
第2、3、4 3个catch都是隐式的 帮我们做了 没有用代码写出来的内容:抛出未catch异常到上层