读:《java中的finally语句块的深度辨析》
《java中的finally语句块的深度辨析》满有意思的文章。作下笔记。
作者第一句提出一个问题:finally 语句块一定会执行吗?(这里是辩术吧...)
他的说法是不一定,被interrupted或者killed的情况不会被执行,并提出一个例子。
(我觉得不太算强词夺理,以后多线程的时候可以长个心眼)
public static int test() {
int i = 1;
try {
System.out.println("try block");
System.exit(0);
return i;
} finally {
System.out.println("finally block");
}
}
当然上面这点,称不上有意思。人家继续举了一个举例
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
public static int getValue() {
int i = 1;
try {
return i;
} finally {
i++;
}
}
}
这个例子的结果是:1。为什么不是2呢? finally语句没有执行么?(这就是上面我说的辩术的误导效果吧... )
真实情况要看下对应的字节码
public static int getValue();
Code:
0: iconst_1 //Pushint
constant
1: istore_0 // Storeint
into local variable
2: iload_0 // Loadint
from local variable
3: istore_1 // 结果存在本地变量表1
4: iinc 0, 1 // Increment local variable by constant
7: iload_1 // 从本地变量表1取回结果
8: ireturn //Returnint
from method
9: astore_2
10: iinc 0, 1
13: aload_2
14: athrow
Exception table:
from to target type
2 4 9 any
9 10 9 any
}
这里牵涉到两部分类容:
1、The JavaTM Virtual Machine Specification, Second Edition 》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译 finally 语句块。实际上,Java 虚拟机会把 finally 语句块作为 subroutine直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。
2、但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。请注意,前文中我们曾经提到过 return、throw 和 break、continue 的区别,对于这条规则(保留返回值),只适用于 return 和 throw 语句,不适用于 break 和 continue 语句,因为它们根本就没有返回值。