finally一定会执行吗?回答当然是否定的,假如在try里执行了System.exit(0)就不会再去执行finally了,守护线程中的finally碰到非守护线程全部退出的时候也可能执行不到。
又如下面的代码,会打印什么内容?
public class Test { public static void main(String... args) { System.out.println(getValue1()); System.out.println(getValue2()); } public static int getValue1() { int i1 = 0 ; int i2 = 1 ; try { return i1; } finally { return i2; } } public static int getValue2() { int i = 1 ; try { return i; } finally { i++; } } } |
这个问题可以通过反编译查看字节码指令来解释,编译后再运行javap -c Test即可得到方法要执行的指令,接下来分别对两个方法做个分析
以下对字节码的解释中【】表示栈,左边表示栈顶
public static int getValue1();
Code:
0: iconst_0 //将0入栈,栈内容【0】
1: istore_0 //将栈顶元素弹出,也就是0,存到局部变量区索引为0的变量中(也就是i1),栈内容【】,(0,1)这两个指令是由int i1 = 0生成的
2: iconst_1 //将1入栈,栈内容【1】
3: istore_1 //将栈顶元素弹出,也就是1,存到局部变量区索引为1的变量中(也就是i2),栈内容【】,(2,3)这两个指令是由int i2 = 1生成的
4: iload_0 //将局部变量区索引为0的变量(也就是i1)值入栈,栈内容【0】
5: istore_2 //将栈顶元素弹出,也就是0,存到局部变量区索引为2的变量中(代码中没有声明,这是javac生成的临时变量,再此记为tmp1),栈内容【】
6: iload_1 //将局部变量区索引为1的变量(也就是i2)值入栈,栈内容【1】
7: ireturn //将栈顶元素弹出,也就是1,并返回
8: astore_3 //(8,9,10)属异常处理部分,这段代码不会出现异常,故执行不到下面的指令
9: iload_1
10: ireturn
Exception table:
from to target type
4 6 8 any
8 9 8 any
可见如果finally和try里都有执行了return,try里的return的值会被废弃。
public static int getValue2();
Code:
0: iconst_1 //将1入栈,栈内容【1】
1: istore_0 //将栈顶元素弹出,也就是1,存到局部变量区索引为0的变量中(也就是i),栈内容【】,(0,1)这两个指令是由int i = 1生成的
2: iload_0 //将局部变量区索引为0的变量(也就是i)的值入栈,栈内容【1】
3: istore_1 //将栈顶元素保存到局部变量区索引为1的变量中(代码中未声明此变量,在此记为tmp1),栈内容【】
4: iinc 0, 1 //将局部变量区索引为0的变量加1,栈内容【】
7: iload_1 //将局部变量区索引为1的变量(即tmp1)的值入栈,栈内容【1】
8: ireturn //弹出栈顶值并返回,即返回1
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
由此可见,在try里返回值会先存到一个临时变量中,finally里改变的是原始变量,改完之后再将临时变量的值返回,也就是说在finally里改变返回值变量并不影响返回值本身。