黑马程序员技术交流社区

标题: finally语句的执行顺序问题 [打印本页]

作者: 周恺    时间: 2012-7-20 01:06
标题: finally语句的执行顺序问题
看了一本书,上面说在try...catch....finally结构中,如果try里面有return语句,那么finally里面的语句还是会执行的
并且会在return语句以前就执行.但是自己做了下验证,却发现结果并非如此.发下代码:
  1. public static void main(String[] args)
  2. {
  3.              System.out.println(returnTest());//打印出的还是haha.
  4. }
  5. public static String returnTest()
  6. {
  7.             String str="haha";
  8.   try
  9. {

  10.              return str;//如果finally里面的语句会在return前面执行,那么这里打印的应该是hehe才对.
  11. }
  12. catch(Exception e)
  13. {

  14. }
  15. finally
  16. {
  17.           str="hehe";//这里改变了str的值
  18. }
  19.           return null;

  20. }
复制代码
但是如果我在finally里面换成是打印语句
  1. finally
  2. {
  3.        System.out.println("我在finally里面");      
  4. }
复制代码
打印结果却是:
       我在finally里面
       haha

这也印证了书上的说法,我想请问一下这是问什么?


作者: 丁二跃    时间: 2012-7-20 01:33
实验了一下确实如此……

但是仔细想了一下,应该是这样的:

首先 如果try里面有return语句,那么finally里面的语句还是会执行的
并且会在return语句以前就执行  这是绝对没错的

咱们 来看程序 当执行到 try 里的 return str 时候,准备返回str,这时返回的是什么呢?不是 haha 而是haha的 地址,这个地址会被放入 该函数的栈中,这是程序不会反会,去执行finally 里面的代码,虽然str指向变了,但是由于返回值已经确定。所以 还是返回原来的值……
作者: 杨_扬    时间: 2012-7-20 01:38
好问题,不过导致这种现象本身的原因并不在于try...catch...finally的执行顺序有什么奇怪,书上说的也没错,的确finally的部分会在return之前被执行,出现你说的这种现象是Java中另一种东西导致的,叫不可变类
String就是一个不可变类,也就是说当一个String变量被改变的时候,原先的变量值仍然被保存在栈内存中,作为garbage等着被回收器回收,新的值JVM会从新在栈内存中分配一片空间来存储。

另外还要介绍一点是,return到底返回的是什么,return返回的并不是一个具体的值,而是指向某个在函数名前面定义的类型的指针,在你的例子中就是返回一个String类型的指针

那么,这个问题就可以解决了,首先,你给str初始化为"hehe", 此时,栈内存中,JVM就开辟了一块内存存放hehe

当程序执行到了return语句的时候,一个指向"hehe"这片内存的指针就形成了,并且等待被函数返回调用出,不过此时,还有finally没有被执行,程序继续执行finally部分,你在其中改变了str的值,实际上是在栈内存中又开辟了一片空间来存储"haha"
finally被执行完,开始return先前生成的那个String指针,注意,这个指针指向的内存地址是存放"hehe"的,因此,我们可以得出如下结论,如果return的是一个不可变类的实例,那么,在finally语句对该实例进行更新是不会对return的值发生影响的

为了进一步验证这个观点,我稍微改变了一下你的程序,将String改为StringBuffer, StringBuffer是一个可变类,改变其实例的值后并不会在内存中重新开辟空间,而是在原有空间上进行修改
  1. public static void main(String[] args) {
  2.                 System.out.println(returnTest());// 打印出的还是haha.
  3.         }

  4.         public static StringBuffer returnTest() {
  5.                 StringBuffer str = new StringBuffer("hehe");
  6.                 try {

  7.                         return str;
  8.                 } catch (Exception e) {

  9.                 } finally {
  10.                         System.out.println("Finally");
  11.                         str.append("haha");
  12.                 }
  13.                 return null;
  14.         }
复制代码
程序的执行结果是
Finally
hehehaha
看,str的值在return之后被finally中的语句改变了
作者: 王龙彪    时间: 2012-7-20 01:38
  1. class Test
  2. {
  3.         public static void main(String[] args)
  4.         {
  5.                      System.out.println(returnTest());//打印出的还是haha.
  6.         }
  7.        
  8.         public static String returnTest()
  9.         {
  10.             String str="haha";
  11.                 try
  12.                 {
  13.                         System.out.println("try...");
  14.                         return str;//如果finally里面的语句会在return前面执行,那么这里打印的应该是hehe才对.
  15.                 }
  16.                
  17.                 catch(Exception e)
  18.                 {
  19.                        
  20.                 }
  21.                
  22.                 finally
  23.                 {
  24.                           return str="x... ...";//
  25.                 }
  26.         }
  27. }
复制代码
在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。
如果在finally里也加个return的话,就会返回最后赋的值了
作者: 周坤    时间: 2012-7-20 02:01
  1. public class FinallyDemo{
  2.         public static void main(String[] args)
  3.         {
  4.                
  5.              System.out.println(returnTest());//打印出的还是haha.

  6.         }
  7.         public static String returnTest()
  8.         {

  9.             String str="haha";
  10.             try
  11.             {
  12.                     System.out.println(str);
  13.                     return str;//[color=Red]先执行了return str,即确定了此方法的返回值,而后才执行的finally[/color]。
  14.             }
  15.             catch(Exception e)

  16.             {}
  17.             finally
  18.             {
  19.                     System.out.println(str);
  20.                     str="hehe";//这里改变了str的值
  21.                     System.out.println(str);// [color=Red]这里输出一下str,发现为haha,说明的确改变了,但返回值依然指向“haha”
  22. [/color]                    return str;
  23.             }
  24.             //return null;


  25. }
  26. }
复制代码
输出结果为
haha
haha
hehe
hehe
可以finally一般是用来关闭资源的,必须执行,而return是结束方法。程序先执行了try中的treturn,即确定了返回值指向“haha”,而后执行finally,其中str的值已经被附到了haha上,但是没有返回,如果加上return str;那么返回值将重新确认,最终输出的结果为haha
作者: 中国移动    时间: 2012-7-20 02:17
本帖最后由 中国移动 于 2012-7-20 02:20 编辑

哥们,给你来个直接的
  1. try
  2. {
  3. //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容
  4. }
  5. catch
  6. {
  7. //除非try里面执行代码发生了异常,否则这里的代码不会执行
  8. }
  9. finally
  10. {
  11. //不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally
  12. }
复制代码
补充下:
try catch语句一般用于处理异常的,catch里一般在try异常成立的情况下才会执行






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2