论坛首页 入门技术论坛

finally 和 return

浏览 6057 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-09-01  
JVM从概念上说有两种栈,一个是用于记录方法调用信息的“方法调用栈”,还有一个是用于完成表达式运算的“求值栈”或者叫“操作数栈”。后者是每个方法被调用的时候都会有一个的
0 请登录后投票
   发表时间:2009-09-01  
还是看不太明白栈是怎么回事
既然finally在return j之前执行了 那返回j的值怎么没有改变呢
哪位大虾能说的详细点   谢先
0 请登录后投票
   发表时间:2009-09-01   最后修改:2009-09-01
yidao620c 写道
第二个可以理解,第一个不太懂。
为什么我debug的时候两次到达try语句中的return j; 这句话上面,而且j的值也改变了(第一次是1,第二次是2),最后居然还是返回了1。不懂。

而且jiyanliang说"这里要理解的是return操作是一个压栈的操作(这里是方法的调用,就是把一个方法的栈顶值拷贝到调用方法的栈顶。注:每个方法都有自己的栈)"。但是这个try/catch不是在同一个方法里面吗,怎么说每个方法都有自己的栈呢?

这个我是按照1.4.2以前来理解的,在1.4.2以前,finally是作为一个单独的微型子例程来使用的,和方法有些类似。java中,每一个方法都有自己的栈的。当然JDK后来版本由于对编译器进行了优化,我想应该不存在微型子例程这么个说法了。一方面可能是效率问题,也可能是bug。

引用
还是看不太明白栈是怎么回事
既然finally在return j之前执行了 那返回j的值怎么没有改变呢

那是因为每一个变量都对应一个槽(不知道用的合不合适,当然要是引用类型的话,这里存储的应该是指向局部变量区或者堆中的地址),你使用javap查看字节码就可以发现,在finally中对j进行操作的槽和在try中进行操作的槽不一样的,所以你在finally中改变不j的值也没用,因为以后返回的是在finally执行之前使用的那个槽的值,除非你在finally中直接return j直接返回。但是我还是说的,我这个理解也是基于老版本的。

下面我们来看看新版编译器到底怎么处理finally,其中有部分不解,还请 RednaxelaFX 帮忙解释一下(我的jdk版本1.6.0)。为了方便 我去掉了输出语句。
0:	iconst_0
   1:	istore_0

   2:	iconst_1
   3:	istore_0
//开始finally
   4:	iload_0

   5:	istore_1//保存到另一个地方
   6:	iconst_2
   7:	istore_0//把2放到1位置
   8:	iload_1//加载的是刚才保存在1位置的 j为1
   9:	ireturn

//这里不解 为什么还要保存一次
   10:	astore_1
   11:	iconst_2
   12:	istore_0

   13:	goto	21//不进行异常处理

   16:	astore_2//异常处理
   17:	iconst_2//inline again
   18:	istore_0
   19:	aload_2
   20:	athrow

   21:	iload_0
   22:	ireturn
  Exception table:
   from   to  target type
     2     6    10   Class java/lang/Exception

     2     6    16   any
    10    11    16   any
    16    17    16   any



偶是初学者,不当地方还请指出。。
0 请登录后投票
   发表时间:2009-11-06   最后修改:2009-11-06

第二次保存做的就是inline的工作。编译器把finally里的代码粘贴到了每个可能的执行路径的后面
0 请登录后投票
   发表时间:2009-11-06  
jiyanliang 写道
yidao620c 写道
第二个可以理解,第一个不太懂。
为什么我debug的时候两次到达try语句中的return j; 这句话上面,而且j的值也改变了(第一次是1,第二次是2),最后居然还是返回了1。不懂。

而且jiyanliang说"这里要理解的是return操作是一个压栈的操作(这里是方法的调用,就是把一个方法的栈顶值拷贝到调用方法的栈顶。注:每个方法都有自己的栈)"。但是这个try/catch不是在同一个方法里面吗,怎么说每个方法都有自己的栈呢?

这个我是按照1.4.2以前来理解的,在1.4.2以前,finally是作为一个单独的微型子例程来使用的,和方法有些类似。java中,每一个方法都有自己的栈的。当然JDK后来版本由于对编译器进行了优化,我想应该不存在微型子例程这么个说法了。一方面可能是效率问题,也可能是bug。

引用
还是看不太明白栈是怎么回事
既然finally在return j之前执行了 那返回j的值怎么没有改变呢

那是因为每一个变量都对应一个槽(不知道用的合不合适,当然要是引用类型的话,这里存储的应该是指向局部变量区或者堆中的地址),你使用javap查看字节码就可以发现,在finally中对j进行操作的槽和在try中进行操作的槽不一样的,所以你在finally中改变不j的值也没用,因为以后返回的是在finally执行之前使用的那个槽的值,除非你在finally中直接return j直接返回。但是我还是说的,我这个理解也是基于老版本的。

下面我们来看看新版编译器到底怎么处理finally,其中有部分不解,还请 RednaxelaFX 帮忙解释一下(我的jdk版本1.6.0)。为了方便 我去掉了输出语句。
0:	iconst_0
   1:	istore_0

   2:	iconst_1
   3:	istore_0
//开始finally
   4:	iload_0

   5:	istore_1//保存到另一个地方
   6:	iconst_2
   7:	istore_0//把2放到1位置
   8:	iload_1//加载的是刚才保存在1位置的 j为1
   9:	ireturn

//这里不解 为什么还要保存一次
   10:	astore_1
   11:	iconst_2
   12:	istore_0

   13:	goto	21//不进行异常处理

   16:	astore_2//异常处理
   17:	iconst_2//inline again
   18:	istore_0
   19:	aload_2
   20:	athrow

   21:	iload_0
   22:	ireturn
  Exception table:
   from   to  target type
     2     6    10   Class java/lang/Exception

     2     6    16   any
    10    11    16   any
    16    17    16   any



偶是初学者,不当地方还请指出。。

关于1.4.2后try...finally的字节码生成 http://ow.ly/zKnl 。比较难以理解的是最后一个异常表,似乎是为了对付在finally 抛出异常的情况,但根据 http://ow.ly/zKoc 和我的实验,在finally里返回或者throw都是直接返回
0 请登录后投票
   发表时间:2009-11-17  
为什么我的结果是
try:1
finally:2

也就是说finally 改变了返回值,我的JDK是1.6
0 请登录后投票
   发表时间:2009-11-21   最后修改:2009-11-21
很有意思的一个题目,我做了测试,也看了上面高手的解答,结合他们的理论,谈谈我的看法,我不了解jvm的运行机制。所以用大白话说,可能大家更容易理解。如果有不对的地方,请指正。

我的理解是,一个方法里如果有了finally,则相当于在jvm里并行运行了两个机制。一个是方法本身,是一个finally。

结合第一个例子,
1,在try里有了return,相当于方法运行到这里就结束了,方法里return下面的所有语句都不会再运行。这时j的值是1,
2,因为有了finally,所以另一路也在同时运行,他改变了j的值,但是没有返回,所以j的值是1,如果修改代码,在finally里也写return j,我试过,值会是2。
由此,我认为return就相当于上面那们高手说的,是一个压栈的过程,在这个例子里,如果finally也有return,相当行了两次压栈,一前一后,得到的值肯定是以后面的为准。

第二个例子,
运行原理是和第一例子一样的,但这里的值是引用类型,所以不管finally有不有return,都直接改变了对象本身的值。所以最后的结果肯定也就是finally的值。

以上就是我的理解,不知道对不对,如果对,希望对其它人理解有帮助,如果不对,请指正。
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics