【问题标题】:Java finally return, and strange bytecodeJava终于回来了,奇怪的字节码
【发布时间】:2014-05-17 13:39:57
【问题描述】:

我看到了一个关于finally的Java难题,然后返回

    int i = 0;
    try {
        return i;
    } finally {
        i++;
    }

这个函数的返回值是多少,我知道它会返回0,我测试另一个代码

    StringBuffer sb = new StringBuffer("11");
    try {
        return sb;
    } finally {
        sb.append("22");
    }

发生了奇怪的事情,它返回“1122” 这是我的第一个问题:为什么它返回“1122”?

我反编译了这两个java代码,

    0:   iconst_0 put 0 to the stack
1:   istore_0 store the stack top into index 0
2:   iload_0  put index 0 to the stack 
3:   istore_1 store the stack top into index 1
4:   iinc    0, 1 inc the index 0 with 1
7:   iload_1 put index 1 to the stack 
8:   ireturn return the top of stack
// what's the below bytecode mean? because the index 0 has incremented, why increment again?
9:   astore_2
10:  iinc    0, 1
13:  aload_2

这是我的第二个问题为什么 9 - 13 行字节码是什么意思?并且索引 0 增加了,wy 再次增加?

   0:   new     #2; //class java/lang/StringBuffer
   3:   dup
   4:   ldc     #3; //String 11
   6:   invokespecial   #4; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
   9:   astore_0
   10:  aload_0
   11:  astore_1
   12:  aload_0
   13:  ldc     #5; //String 22
   15:  invokevirtual   #6; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   18:  pop
   19:  aload_1
   // like the above, this seems should return the value in the index 1, it should be "11"??
   20:  areturn
   21:  astore_2
   22:  aload_0
   23:  ldc     #5; //String 22
   25:  invokevirtual   #6; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   28:  pop
   29:  aload_2
   30:  athrow

这是我的第三个问题,似乎应该是索引中的值,应该是“11”??

【问题讨论】:

  • docs.oracle.com/javase/tutorial/essential/exceptions/… - finally 块总是在 try 块退出时执行。
  • 它与引用与原始数据类型有关。第一种情况是原始数据类型,因此它将返回堆栈上的面值。第二种情况是一个引用,所以它会在finally块执行后返回引用指向的任何东西。
  • 所以从字节码的方式来说,astore_0,astore_1,存储引用,所以load_0,然后修改,那么index 1就会看到修改结果。对吗?
  • 以后,请每个问题问一个问题。这应该是三个独立的问题。
  • 我认为重复的代码路径用于处理“正常”和“异常”情况。

标签: java jvm return java-bytecode-asm try-catch-finally


【解决方案1】:

在第一个示例中,0 进入堆栈并返回,而不管 finally 块中发生什么。

在第二个示例中,对StringBuffer 的引用进入堆栈,然后StringBuffer 本身在finally 块中被修改。但是对StringBuffer 的引用没有改变,这就是返回的内容。

【讨论】:

  • 所以从字节码的方式来说,astore_0,astore_1,存储引用,所以load_0,然后修改,那么index 1就会看到修改结果。对吗?
  • "但是对 StringBuffer 的引用没有改变,这就是返回的内容。" “对 StringBuffer 的引用没有改变”是什么意思?您的意思是 sb 是对 StringBuffer 的引用,在 finally 块中,更改 StringBuffer 本身,而不是 sb?
  • 是的。引用仍然指向内存中的相同位置,但对象本身已更改。
【解决方案2】:

不看字节码就可以解释代码的行为和第一个问题:

代码返回一个StringBuffer 对象。该对象是可变的,它在finally 块中变异,该块在执行返回给调用者之前运行。 (它仍然返回 same StringBuffer 对象。)

尝试return sb.toString()(并更改返回类型以匹配) - 在这种情况下,StringBuffer 的副作用不会影响返回的字符串对象。

(我不知道关于实际字节码的问题。)

【讨论】:

    猜你喜欢
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多