Fork me on GitHub
文章目录
  1. 1. 前言
  2. 2. 再谈Java中的Return-Finally问题
  3. 3. 先说说返回值的事
  4. 4. 字节码
  5. 5. 用图示来增进理解吧
  6. 6. 参考资料
  7. 7. 感谢

前言

也许你已经看过好几篇类似的文章了,但是还没搞定Return-Finally问题?那不妨给我一次机会,看看我的这篇文章,也许有收获呢(⊙o⊙)

再谈Java中的Return-Finally问题

先来看看下面这段代码:

1
2
3
4
5
6
7
8
public static int testReturnFinally() {
int x = 1;
try {
return x;
} finally {
x++;
}
}

你能马上回答出上述函数的返回值么?如果是,并且知其所以然,那么你就可以直接跳转到本文的后面部分了;如果不是,且听我娓娓道来。

先说说返回值的事

我们都知道,所谓finally,就是无论出现什么情况,在try结束或catch块结束之后执行的都是finally代码块。那么这是如何实现的呢?对!我们可以
trycatch代码块之后插入finally代码块,这样,无论是执行哪一个分支,最后一定会执行finally代码块!

字节码

那么,我们就来看看上述Java代码的字节码吧,如何查看?终端下使用指令javap -verbose TestReturnFinally.class,其中,TestReturnFinally.class是要查看的字节码文件。
上述Java代码的字节码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
 0: iconst_1                        //将int型1推送至Java虚拟机栈顶
1: istore_0 //将栈顶int型数值存入第一个本地变量(x=1)
2: iload_0 //将本地第一个int型本地变量推送至Java虚拟机栈顶
3: istore_1 //将栈顶int型数值存入第二个本地变量,方法返回前将返回值存在一个临时变量中
4: iinc 0, 1 //第4,7条指令是由编译器强行插入到第8条指令之前的,为了确保finally一定执行
7: iload_1 //指令4:将第一个本地变量自增1(x++) 指令7:将第2个本地变量推送至栈顶
8: ireturn //方法返回,返回值为第二个本地变量,因此返回值为1!
9: astore_2 //关于第9-14条指令,我其实不是特别清楚,我的猜想是因为我们没有在代码中明确进行
//catch操作,所以编译器插入了一个catch的操作,实际上可能没什么用......
10: iinc 0, 1
13: aload_2
14: athrow

编译出来之后共14条字节码指令,指令的相关含义已经在后面注明了。看完上述字节码,我们可以知道:

  1. try中的return之后,返回值其实已经确定了,真正的返回值被存在Java虚拟机栈的一个临时变量中,我们之后对x进行的操作并不会改变返回值的大小,只是改变的x的大小。
  2. 不过这里有一点需要注意,如果在finally中也有return语句,那么finally中的return会覆盖之前try块中的return语句,所以返回值会发生变化,你可以亲自写代码试验一下。

用图示来增进理解吧

还是上述那段代码,我们画个图来增强一下理解:

图解

对上图的一些说明:

  1. 在JVM中,Java虚拟机栈包含操作数栈和局部变量,但是在上图中,我将其画成了两列,这只是为了方便理解。

参考资料

深入理解Java虚拟机-周志明
你真的了解try{ return }finally{}中的return?

感谢

感谢访问我的个人博客的朋友,如果您感觉本站对您搜索的问题有所帮助,并感觉对本站还满意的话,顶一下吧,希望您把本站分享给您的朋友!在此对您表示由衷的谢意! :-)