写点什么

Java 中 return 和 finally 到底哪个先执行,java 程序设计实用教程叶核亚第五版

用户头像
极客good
关注
发布于: 刚刚

上述代码的输出可以简单地得出结论:return 在 finally 之前执行,我们来看下字节码层面上发生了什么事情。下面截取 case1 方法的部分字节码,并且对照源码,将每个指令的含义注释在后面:


0:


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


iconst_1 // 将常量 1 推入操作数栈顶


1: istore_0 // 弹出栈顶元素(1),保存到局部变量表 slot[0],此时 slot[0]=1。这两条指令对应源码:x = 1;


2: iload_0 // 将局部变量表 slot[0]的值推入操作数栈顶,也就是说把上面 x 的值推入栈顶


3: istore_1 // 弹出栈顶元素(1),保存到局部变量表 slot[1],此时 slot[1]=1。其实,此时就已经把要 return 的值准备好了


4: iconst_3 // 将常量 3 推入操作数栈顶,这一条指令开始,其实是开始执行 finally 中的代码了


5: istore_0 // 弹出栈顶元素(3),保存到局部变量表 slot[0],此时 slot[0]=3。这两个指令对应源码:x = 3;这里要注意的是,虽然都是更新了 x 的值,但是 finally 中的 x 和 try 中 x 的赋值,保存在了不同的局部变量表中


6: iload_1 // 将局部变量表 slot[1]的值推入操作数栈顶,此时栈顶元素的值为 1,是第 3 行指令保存的值


7: ireturn // 将操作数栈顶的值返回给调用方


从字节码来看,似乎又是 finally 的代码先执行了,因为 ireturn 指令确实是在最后执行的,所以返回什么样的值不在于谁先执行,而在于 ireturn 指令返回的操作数栈顶的元素是何时保存的。在上述代码环境中,是 try 代码块中給 x 赋值的版本,也就是紧接着 return 语句后面的 x 所保存的版本。


下面再来看一个稍微复杂点的场景:


public static int case2() {


int x;


try {


x = 1;


return ++x;


} finally {


x = 3;


}


}

输出

2


有了上面的分析,这个就很好理解了,我们还是来看下字节码:


0: iconst_1 // 将常量 1 推入操作数栈顶


1: istore_0 // 弹出栈顶元素(1),保存到局部变量表 slot[0],此时 slot[0]=1。这两条指令对应源码:x = 1;


2: iinc 0, 1 // 对局部变量表 slot[0]进行自增(+1)操作,此时 slot[0]=2,对应源码:++x;所以,可以看出 return 后面的表达式先执行


5: iload_0 // 将局部变量表 slot[0]的值推入操作数栈顶,也就是说把上面 x 的值(2)推入栈顶


6: istore_1 // 弹出栈顶元素(2),保存到局部变量表 slot[1],此时 slot[1]=2。其实,此时就已经把要 return 的值准备好了


7: iconst_3 // 将常量 3 推入操作数栈顶,这一条指令开始,其实是开始执行 finally 中的代码了


8: istore_0 // 弹出栈顶元素(3),保存到局部变量表 slot[0],此时 slot[0]=3。这两个指令对应源码:x = 3;这里要注意的是,虽然都是更新了 x 的值,但是 finally 中的 x 和 try 中 x 的赋值,保存在了不同的局部变量表中


9: iload_1 // 将局部变量表 slot[1]的值推入操作数栈顶,此时栈顶元素的值为 2,是第 6 行指令保存的值,也就是经过++x 之后的值


10: ireturn // 将操作数栈顶的值返回给调用方


从上述代码可以看出,return 后面的指令先执行,然后保存到局部变量表,接着执行 finally 中的语句,最后执行 return 指令本身。


总结一下,return 指令是最后执行的,如果 return 后面有表达式,则执行完表达式之后就执行 finally 中的语句,最后再执行 return 指令。所以说 finally 和 return 到底哪个先执行:return 指令后面如果有表达式或方法调用的话,先执行,然后执行 finally,最后执行 return 指令。就像上面的程序演示的结果,不能光从 x 的赋值来看最终返回结果,从指令层面看,两次对 x 的赋值,保存在局部变量表的位置不一样。


最后,再来看一个平时不会这么去写的场景:


public static int case3() {


int x;


try {


x = 1;


return ++x;


} finally {


x = 3;


return x;


}

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Java中return和finally到底哪个先执行,java程序设计实用教程叶核亚第五版