先看一段代码
public static int test() {
int i = 0;
try {
System.out.println("业务执行"); // 标记1
i = 1 / 0;
System.out.println("业务执行完成");
i++; // 标记2
return i;
} catch (Exception e) {
System.out.println("捕捉异常"); // 标记3
i++; // 标记4
return i;
} finally {
System.out.println("释放资源"); // 标记5
i++; // 标记6
return i;
}
}
带着问题找答案
问题1:try {} 里面的代码没有抛出异常,正确的执行了 try {} 代码中的 return 指令,finally {} 中的代码会被执行,那底部的执行原理是怎样的呢。
问题2:try {} 里面的代码抛出了异常,接着会执行 catch {} 中的代码且 catch {} 中的代码没有抛出异常正确的执行了 return 指令,执行 finally 中的代码也会被执行,那底部的执行原理是怎样的呢。
问题3:try {} 里面的代码抛出了异常,接着会执行 catch {} 中的代码且 catch {} 中的代码也抛出了异常,执行 finally 中的代码也会被执行,那底部的执行原理是怎样的呢。
以上三个问题总结如下图
通过javap查看class文件字节码,命令为javap -c -s -v -l TestMain.class
这里只是帖出了 test 方法有关的字节码,
重点(看懂这三点,也就明白了 try catch finally 的底层原理)
- 绿色表示 try {} 中的代码,红色表示 catch {} 中的代码,蓝色表示 finally {} 中的代码。
- finally {} 中代码的字节码别编织到了三个地方(看懂这一点,也就明白了 try catch finally 的底层原理)
- Exception table,列表中记录了从字节码 from 到字节码 to,如果执行期间出现异常 代码分支应该如何走
字节码如下
public static int test();
descriptor: ()
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=0
0: iconst_0
1: istore_0
------实际运行中 try {} 中的字节码 begin --------
2: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
5: ldc #3 // String 业务执行
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: iconst_1
11: iconst_0
12: idiv
13: istore_0
14: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
17: ldc #5 // String 业务执行完成
19: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: iinc 0, 1
25: iload_0
26: istore_1
27: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
30: ldc #6 // String 释放资源
32: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: iinc 0, 1
38: iload_0
39: ireturn // 对应着代码中的标记 2
------实际运行中 try {} 中的字节码 end----------
------实际运行中 catch {} 中的字节码 begin --------
40: astore_1
41: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
44: ldc #8 // String 捕捉异常
46: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
49: iinc 0, 1
52: iload_0
53: istore_2
54: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
57: ldc #6 // String 释放资源
59: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
62: iinc 0, 1
65: iload_0
66: ireturn // 对应着代码中的标记 4
------实际运行中 catch {} 中的字节码 end --------
------实际运行中 finally {} 中的字节码 begin --------
67: astore_3
68: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
71: ldc #6 // String 释放资源
73: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
76: iinc 0, 1
79: iload_0
80: ireturn // 对应着代码中的标记 6
------实际运行中 finally {} 中的字节码 end --------
Exception table:
from to target type
2 27 40 Class java/lang/Exception // 2 到 26 之间的代码是 try {} 中的代码,运行期间如果抛出 Exception 则跳转到 40,也就是 catch {} 中代码的第一行
2 27 67 any // 2 到 26 之间的代码是 try {} 中的代码,运行期间没有抛出异常则跳转到 67 也就是 finally {} 中代码的第一行
40 54 67 any // 40 到 53 之间的代码是 catch {} 中的代码,运行期间如果抛出异常则跳转到 67 也就是 finally {} 中代码的第一行
参考文章