finally语句块与return
运行下面的代码,想想在程序正常运行时输出什么,出现异常时输出什么?
public class Test {
public static void main(String[] args) {
System.out.println("return value of test() : " + test());
}
public static int test() {
int x;
try {
x = 1;
return x;
} catch (Exception e) {
x = 2;
return x;
} finally {
x = 3;
}
}
}
运行上面的代码,在test()方法正常运行时返回1,出现异常时返回2;可能和你想的不太一样。在下面,使用javap -v 查看java在字节码层面对test()方法的实现:
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=4, args_size=0
0: iconst_1
1: istore_0
2: iload_0
3: istore_1
4: iconst_3
5: istore_0
6: iload_1
7: ireturn
8: astore_1
9: iconst_2
10: istore_0
11: iload_0
12: istore_2
13: iconst_3
14: istore_0
15: iload_2
16: ireturn
17: astore_3
18: iconst_3
19: istore_0
20: aload_3
21: athrow
Exception table:
from to target type
0 4 8 Class java/lang/Exception
0 4 17 any
8 13 17 any
如果test方法执行中没有发生异常:
0:1 入栈
1:1 出栈存入Slot0 中
2:装载slot0 中的 1 入栈
3:1 出栈存入Slot1 中
4: 3 入栈
5: 3 出栈存入Slot0中
6: 装载slot1中 1 入栈
7: 返回栈顶元素 1 , 方法结束
如果test方法在执行0 ~ 4(不包含第4条) 条字节码指令时发生Exception异常,跳转到第8条指令开始执行:
8: 把栈顶异常存入Slot1
9: 2 入栈
10:2 出栈存入 Slot0
11: 装载Slot0中的 2 入栈
12:2 出栈存入 Slot2
13:3 入栈
14:3 出栈存入Slot0
15:装载Slot2中 2 入栈
16:返回栈顶元素2 ,返回结束
如果发生Exception以外的异常跳到第17条指令执行,方法非正常退出,无返回值。
无论程序在运行中,发生不发生Exception异常,在执行i = 3 前,总会复制 i 的值到最后一个本地变量表的Slot 中(暂时把这个Slot称为returnValue),在执行ireturn前,总会把returnValue压入栈顶,作为方法返回值使用。
finally语句块什么时候执行?
由上面问题中的字节码指令可以看出:无论有无异常发生,finally语句块在try 和 catch 语句 return之前执行。
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}
所以下面代码的执行结果为: return value of getValue(): 1
finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。《阿里巴巴Java开发手册》中【强制】不能在 finally 块中使用 return。
- 参考《深入理解 Java 虚拟机 第2版》 第三章
- 参考[ 关于 Java 中 finally 语句块的深度辨析 ]
- 参考《阿里巴巴Java开发手册》