今天看到有几个帖子是讲try/finally语句中try语句体包含return语句的执行顺序问题,我做了一下深入的了解,下面进行分析
首先,来看一段代码: - public class TestTryFinally {
- public static void main(String[] args) {
- test();
- }
- public static void test() {
- int x = 5;
- try{
- System.out.println("try 语句-----x值:"+x);
- }finally{
- x++;
- System.out.println("finally 语句-----x值:"+x);
- }
- }
- }
复制代码想必大家都很清楚这段代码的输出结果,对的,这段代码的输出结果是: try 语句-----x值:5
finally 语句-----x值:6
先执行try语句体,再执行finally语句体,现在,代码稍微改动一下 - public class TestTryFinally {
- public static void main(String[] args) {
- System.out.println(test());
- }
- public static int test() {
- int x = 5;
- try{
- return x;
- }finally{
- x++;
- System.out.println("finally 语句-----x值:"+x);
- }
- }
- }
复制代码大家再看输出结果是什么呢?是不是大多数人认为结果如下呢: finally 语句-----x值:6
6
实际上,结果如下: finally 语句-----x值:6
5
这该怎么解释呢,既然先执行了finally里的语句,那么x的值应该改变了的,可是为什么try里面返回的值还是原来的那个呢,这个问题可以通过反编译查看字节码指令来解释。(为了更加简洁字节码,这里把上面代码中的finally区域里的打印语句省掉)
然后通过如下指令得到字节码:javap -c TestTryFinally
得到的字节码如下:
直接看test方法的字节码
0:iconst_5 //将int型的5入栈
1:istore_0 //将int类型值存入局部变量0
2:iload_0 //从局部变量0中装载int类型值(装入5)
3:istore_1 //将int类型值存入局部变量1(这一句等于就是把5又放入到索引为1的变量中去)
4:iinc 0,1 //把局部变量区索引为0的变量加1(在这里就是执行finally语句体中的x++)
5:iload_1 //从局部变量1中装载int类型值(装入局部变量1中的值,这个值为5)
6:ireturn //从方法中返回int类型数据(返回5)
7://后面的就是抛出异常时的代码,这里就不进行分析
从分析字节码可以看出,在try语句返回之前先将x存入到另一个变量中,再进行finally里面的语句(x++)
在finally里面语句执行完成后,再从之前的变量里取出值5,所以不管你在finally里怎么改变x的值,try里返回的
已经不会改变了。
现在,就已经可以对这段代码有一个比较深的理解了。
总结:如果try中有return语句时(注:finally语句体中没有return语句)当执行到return语句时,先把要return的值
存放在另一个新建的变量中,在finally里执行完后,再从刚才新建的变量中取出值进行返回。
另:如果try语句和finally语句中都含有return 语句时,try语句体中的return会被隐藏掉。
|