关于java中 try和finally return的问题

 关于java中finally,以前只是用在try catch 之后,今天同学发来一个面试题给我详细一分析,立刻觉得java中的这部分设计有点繁琐。代码如下:
  
  
  1. public int test()  
  2. {  
  3.      int a = 1;  
  4.      try  
  5.      {  
  6.           return a;  
  7.      }catch  
  8.      {   
  9.           //todo:handle exception  
  10.      }finally  
  11.      {  
  12.           a++;  
  13.      }  
  14.      return a;  
  15. }
    要得出return a 为多少,是不是有点让人迷糊。
表现
    1.不是太明白try finally执行顺序的人做,很快就能得出答案,程序会执行到try代码块里面,直接返回a=1.这是正确答案,要讲出详细执行过程可能会讲不出来。
    2.明白try finally执行顺序的人做,可能会得出 a=2的答案,这是错误答案。
  分析
    知道try catch finally执行顺序的人都知道,上面的程序块中,首先会执行try中的代码,在执行try中的return前,程序会去执行finally中的a++,执行完finally中的a++ 然后再去执行try中的return。
    那么为什么执行了finally中的a++ 后,try中的return返回的仍然是a=1呢。这需要深入分析。
深入分析
   一些书上写明,1.try中return的是基本类型的数据,那么finally中的改动不会反映到try中。2.try中return的是引用,那么finally中对相同引用的改动会反映到try中。
    为什么会有上面两个结论,得从调用函数、参数栈的角度来解释。当调用一个函数时,将函数所需的参数依次压栈,函数里面直接取用这些参数,在函数返时将返回值压栈,函数返回后,栈顶即是返回值。当通过压栈传递参数时,参数的类型不同,压栈的内容也不同。如果是值类型,压栈就是经过复制的参数值,如果是引用类型,那么进栈的只是一个引用。这也是我们所熟悉的,传递值类型时,函数内修改参数值不会影响函数外,而引用类型的话则会影响。
通过上面的分析之后,那么下面的代码就很好得出答案:

public class JVMTest {  

public static void main(String[] args){  

System.out.println("aa:" + aa());  

}  

public static int aa(){  

int a = 1;  

int b = 10;  

try{  

System.out.println("abc");  

return a;  

}finally{  

a = 2;  

System.out.println("a: "+ a);  

}  

}  

}

abc

a: 2

aa: 1

    上面的输出结果很好分析,首先执行try中的println,然后执行return,在return前,去执行finally中的println,执行完finally中的代码后再去try中return,然后输出main函数中的结果。
-----------------------------------------------------割裂 -----------------------------------------------------------------
    上面的分析是最常规的情况,下面分析一下不常规的情况,虽然实际中可能很少能见到,但是对于理解java中finally的设计有相当大的帮助。 
    如果上面的代码进行变形,如下:

public class FinallyTest { public static void main(String[] args) { System.out.println("getValue()返回值为:" + getValue()); } public static int getValue() { try { return 0; } finally { return 1; } } }

那么,此时的输出结果是什么呢?
按照上面的分析:在try中return前,执行finally中的代码,return 1,然后return 0,所以最终的返回值为0.
这样的分析正确吗,总让人有种怪怪的感觉,那么执行过程又是怎样的呢?(正确答案是返回 1)
让java之父James Gosling来解释:

a finally clause is always entered with a reason. That reason may be that the trycode finished normally, that it executed a control flow statement such as return, or that an exception was thrown in code executed in the TRy block. The reason is remembered when the finally clause exits by falling out the bottom. However, ifthe finally block creates its own reason to leave by executing a control flow statement (such as break or return) or by throwing an exception, that reason supersedes the original one, and the original reason is forgotten. For example, consider the following code:

try {

    // ... do something ...

    return 1;

} finally {

    return 2;

}

When the TRy block executes its return, the finally block is entered with the "reason" of returning the value 1. However, inside the finally block the value 2 is returned, so the initial intention is forgotten. In fact, if any of the other code in the try block had thrown an exception, the result would still be to return 2. If the finally block did not return a value but simply fell out the bottom, the "return the value 1" reason would be remembered and carried out.

    上述红色关键字体的意思是,当try正常结束(code finished normally)包括return, or that an exception,程序就会跳转到finally块中去,如果finally中没有such as break or return or by throwing an exception这些发生,try中的return就会正常返回;如果finally中有break 或者 return 或者抛出异常,那么try中的return值就会被清除,按照finally中的结果来返回。

    按照上面的描述,是不是对finally的用法更加了解了呢。

    为什么会出现这种分情况的讨论,究其原因,都是java的设计规则引起的。结合上面的三段代码及运行过程分析,可以分析任何try 和finally的返回值问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值