06 finally 中调整返回对象, 但是最终返回值未改变

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了06 finally 中调整返回对象, 但是最终返回值未改变相关的知识,希望对你有一定的参考价值。

前言

// 回个家, 还什么都没有做, 各种各样的问题 ... 

呵呵呵 很久以前的一个问题了, 当时写了 case, 但是 没有时间来看 

并且 当时找了一下 jls 的相关说明, 也没有找到具体的 和 当前问题有关系的地方, 还是 理解能力不够 

问题来自于 hllvmgroup [讨论] 关于try-finally编译, R 大也是做了比较详尽的说明  

我们这里主要是 看一下 jls 中的说明, 以及 javac 中的一些具体的体现 

引用 R大的一部分回复  

RednaxelaFX 2013-06-21

直接在finally里用return语句可以干涉返回值,但只是改变显式的局部变量则不改变返回值。finally在执行时,语义上说那个try块里的return语句已经执行完了,只是控制流临返回之前进入finally块里转一圈而已。因而这个例子在实际javac实现中,try块里的return语句要返回的值被缓存在了一个编译器生成的局部变量里(slot 3),等实际返回的时候再拿出来。

slot 2则被用于临时存放try块里可能抛出的异常,用于在finally末尾重新抛出。

以下测试用例效果, 以及截图基于 jdk8

测试用例 

测试用例来自于 原文章 

/**
 * FInallyUpdateReturnVal
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2020-02-15 15:24
 */
public class Test20FinallyUdpateReturnVal 

    // Test20FinallyUdpateReturnVal
    // https://hllvm-group.iteye.com/group/topic/38101
    public static void main(String[] args) 

        System.out.println(get());

    

    // get
    public static String get() 
        String ret = "";
        try 
            ret = "aa";
            return ret;
         finally 
            ret = "bb";
        
    


反编译的 class 的相关信息 

master:21_javac jerry$ javap -c Test20FinallyUdpateReturnVal
Compiled from "Test20FinallyUdpateReturnVal.java"
public class Test20FinallyUdpateReturnVal 
  public Test20FinallyUdpateReturnVal();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #3                  // Method get:()Ljava/lang/String;
       6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       9: return

  public static java.lang.String get();
    Code:
       0: ldc           #5                  // String
       2: astore_0
       3: ldc           #6                  // String aa
       5: astore_0
       6: aload_0
       7: astore_1
       8: ldc           #7                  // String bb
      10: astore_0
      11: aload_1
      12: areturn
      13: astore_2
      14: ldc           #7                  // String bb
      16: astore_0
      17: aload_2
      18: athrow
    Exception table:
       from    to  target type
           3     8    13   any

执行结果为, 呵呵 可能和我们常规的理解有一些差异, 为什么 finally 中的更新没有生效?  

jls 关于 try-finally, return 

14.20.2. Execution of try-finally and try-catch-finally

主要讲的是 try-fianlly & try-catch-finally 的相关约束, 和我们这里想要了解的说明是对不上的 

14.17. The return Statement

主要讲的是 return 的相关约束, 下面有一些和 try-finally 相关的说明, 但是 和我们这里想要了解的说明是对不上的 

javac 中的体现

可以看到这里在 "return ret" 生成 code 的时候, 发现 return 携带有 finally, 就生成了一个 临时变量来存储结果 

然后执行 finally, 然后 加载 临时变量 返回 

get 方法的 code 信息, code.cp 是 12, 表示 areturn 之前的内容有 12 字节  

  public static java.lang.String get();
    Code:
       0: ldc           #5                  // String
       2: astore_0
       3: ldc           #6                  // String aa
       5: astore_0
       6: aload_0
       7: astore_1
       8: ldc           #7                  // String bb
      10: astore_0
      11: aload_1
      12: areturn
      13: astore_2
      14: ldc           #7                  // String bb
      16: astore_0
      17: aload_2
      18: athrow
    Exception table:
       from    to  target type
           3     8    13   any

完 

参考

[讨论] 关于try-finally编译

以上是关于06 finally 中调整返回对象, 但是最终返回值未改变的主要内容,如果未能解决你的问题,请参考以下文章

06 finally 中调整返回对象, 但是最终返回值未改变

面对对象-final关键字

JMM 保证 final 作为字段和对对象的非最终引用

trycatchfinally 执行顺序

final

面向对象 “下”