大家好,我是烤鸭:
今天说下 finally 这个关键字。
1. 认识finally
finally 总是跟 try、catch一起出现,finally是执行方法结束一定要执行的代码,比如流关闭等等。
finally是如何实现在异常捕捉之后保证执行 finally 代码块里的内容。
其实不管是普通的代码,还是 try、catch ,JVM都是根据字节码文件中的指令来执行,
也就是 finally的时候,字节码指令覆盖了这一种情况。
而异常之后的操作指令是有专门的异常表来存储,在字节码指令之后(不是一定存在的,如果代码显示的try catch的话会有),结构如下图(图来自《深入理解java虚拟机》)
2. 代码实践
我们看一下 下面的代码,其实代码特别简单,输出3或4或者抛出非Exception的异常(finally会先执行)。
public class TestCatchFinally {public static void main(String[] args) throws Throwable{int x = 0;try {x = 1;// throw new Throwable();} catch (Exception e) {x = 2;} finally {try {x = 3;} catch (Exception e) {x = 4;}}System.out.println(x);}
}
看一下生成的字节码指令
javap -verbose TestCatchFinally.class
Classfile / ... 无关内容省略
Constant pool:// 常量池内容省略
{public src.bytecode.TestCatchFinally();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 10: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lsrc/bytecode/TestCatchFinally;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=5, args_size=10: iconst_01: istore_12: iconst_13: istore_14: iconst_35: istore_16: goto 419: astore_210: iconst_411: istore_112: goto 4115: astore_216: iconst_217: istore_118: iconst_319: istore_120: goto 4123: astore_224: iconst_425: istore_126: goto 4129: astore_330: iconst_331: istore_132: goto 3935: astore 437: iconst_438: istore_139: aload_340: athrow41: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;44: iload_145: invokevirtual #4 // Method java/io/PrintStream.println:(I)V48: returnException table:from to target type4 6 9 Class java/lang/Exception2 4 15 Class java/lang/Exception18 20 23 Class java/lang/Exception2 4 29 any15 18 29 any30 32 35 Class java/lang/Exception// ,,,
}
SourceFile: "TestCatchFinally.java"
字节码指令比较简单了,没什么可说的,我们看下异常表。
第1行指的是字节码指令 4-6行,对应的代码内容是 try {x = 3;}catch (Exception e) {x = 4;}。如果触发了这个异常,就会执行 9行指令,变量赋值为 4。
第2行指的是字节码指令 2-4行,对应的代码内容是 try {x = 1;}catch (Exception e) {x = 2;}。如果触发了这个异常,就会执行 15行指令,变量赋值为 2。
第3行指的是字节码指令 18-20行,对应的代码内容是 try {x = 3;}catch (Exception e) {x = 4;}。如果触发了这个异常,就会执行 23行指令,变量赋值为 4。
关于指令重复,比如 iconst_3 执行了三次,第一次 是正常执行后执行 finally内容,第二次是 异常后执行finally内容,第三次是遇到 非 Exception异常 执行的finally
两个any 指的是 catch不到的异常,比如 throwable ,如果触发,程序会执行finally后退出。
3. 总结
字节码指令已经把显示异常和 finally 代码块都包含了,剩下的只是按指令执行而已了。