您现在的位置是:亿华云 > 数据库
Java中return和finally到底哪个先执行
亿华云2025-10-09 15:29:57【数据库】1人已围观
简介本章节我们从字节码的角度来探究下return和finally到底哪个先执行。下面先来看一段简单地源码:publicclassReturnFinallyDemo{publicstaticvoidmain
本章节我们从字节码的到底角度来探究下return和finally到底哪个先执行。下面先来看一段简单地源码:
public class ReturnFinallyDemo { public static void main(String[] args) { System.out.println(case1()); } public static int case1() { int x; try { x = 1; return x; } finally { x = 3; } } } # 输出上述代码的先执行输出可以简单地得出结论:return在finally之前执行,我们来看下字节码层面上发生了什么事情。到底下面截取case1方法的先执行部分字节码,并且对照源码,到底将每个指令的先执行含义注释在后面:
iconst_1 // 将常量1推入操作数栈顶 istore_0 // 弹出栈顶元素(1),保存到局部变量表slot[0],到底此时slot[0]=1。先执行这两条指令对应源码:x = 1; iload_0 // 将局部变量表slot[0]的到底值推入操作数栈顶,也就是先执行说把上面x的值推入栈顶 istore_1 // 弹出栈顶元素(1),保存到局部变量表slot[1],到底此时slot[1]=1。先执行其实,到底此时就已经把要return的先执行值准备好了 iconst_3 // 将常量3推入操作数栈顶,这一条指令开始,到底其实是开始执行finally中的代码了 istore_0 // 弹出栈顶元素(3),保存到局部变量表slot[0],此时slot[0]=3。这两个指令对应源码:x = 3;这里要注意的是,虽然都是更新了x的值,服务器租用但是finally中的x和try中x的赋值,保存在了不同的局部变量表中 iload_1 // 将局部变量表slot[1]的值推入操作数栈顶,此时栈顶元素的值为1,是第3行指令保存的值 ireturn // 将操作数栈顶的值返回给调用方从字节码来看,似乎又是finally的代码先执行了,因为ireturn指令确实是在最后执行的,所以返回什么样的值不在于谁先执行,而在于ireturn指令返回的操作数栈顶的元素是何时保存的。在上述代码环境中,是try代码块中給x赋值的版本,也就是紧接着return语句后面的x所保存的免费信息发布网版本。
下面再来看一个稍微复杂点的场景:
public static int case2() { int x; try { x = 1; return ++x; } finally { x = 3; } } # 输出有了上面的分析,这个就很好理解了,我们还是来看下字节码:
iconst_1 // 将常量1推入操作数栈顶 istore_0 // 弹出栈顶元素(1),保存到局部变量表slot[0],此时slot[0]=1。这两条指令对应源码:x = 1; iinc 0, 1 // 对局部变量表slot[0]进行自增(+1)操作,此时slot[0]=2,对应源码:++x;所以,可以看出return后面的表达式先执行 iload_0 // 将局部变量表slot[0]的值推入操作数栈顶,也就是说把上面x的值(2)推入栈顶 istore_1 // 弹出栈顶元素(2),保存到局部变量表slot[1],此时slot[1]=2。其实,此时就已经把要return的值准备好了 iconst_3 // 将常量3推入操作数栈顶,这一条指令开始,其实是开始执行finally中的代码了 istore_0 // 弹出栈顶元素(3),保存到局部变量表slot[0],此时slot[0]=3。这两个指令对应源码:x = 3;这里要注意的是,虽然都是亿华云计算更新了x的值,但是finally中的x和try中x的赋值,保存在了不同的局部变量表中 iload_1 // 将局部变量表slot[1]的值推入操作数栈顶,此时栈顶元素的值为2,是第6行指令保存的值,也就是经过++x之后的值 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; } } # 输出这是一个finally返回结果的示例,平时不建议这么写,我们同样从字节码的角度来分析下:
iconst_1 // 将常量1推入操作数栈顶 istore_0 // 弹出栈顶元素(1),保存到局部变量表slot[0],此时slot[0]=1。这两条指令对应源码:x = 1; iinc 0, 1 // 对局部变量表slot[0]进行自增(+1)操作,此时slot[0]=2,对应源码:++x;所以,可以看出return后面的表达式先执行 iload_0 // 将局部变量表slot[0]的值推入操作数栈顶,也就是说把上面x的值(2)推入栈顶 istore_1 // 弹出栈顶元素(2),保存到局部变量表slot[1],此时slot[1]=2。 iconst_3 // 将常量3推入操作数栈顶,这一条指令开始,其实是开始执行finally中的代码了 istore_0 // 弹出栈顶元素(3),保存到变量表slot[0],此时slot[0]=3。这两个指令对应源码:x = 3 iload_0 // 将局部变量表slot[0]的值(3)推入操作数栈,这是跟之前不一样的地方,ireturn返回的值选择的局部变量表不一样 ireturn从字节码以及解释来看,直接忽略了try语句块中的return指令,这样的代码会让人产生疑惑,所以平时不建议这么写。本章节就到这里了。
很赞哦!(44)
相关文章
- 在数以亿计的网站中,我们应该抓住每一个可能带来宣传的机会,域名可以带有企业的名字,一般可以使用汉语拼音或者英语单词或者是相关缩写的形式,只要用户记住了你企业的名字,就能很容易的打出你的网站域名,同样的,记住了网站域名也能很快的记住你公司的名字。
- 深入浅出谈 CSS 动画
- 选择域名注册平台的标准是什么?
- 超实用的三十个 Python 案例
- 互联网中的地址是数字的IP地址,域名解析的作用主要就是为了便于记忆。
- 深入理解 OpenFeign 的架构原理
- Go 分布式令牌桶限流 + 兜底保障
- 域名空间是一个什么结构?
- 其次,一般域名注册有一个获取密码的按钮,域名注册商点击后会向您发送密码。在得到域名注册商发送的密码后,将其传输到域名服务提供商网站,然后输入密码,此时域名呈现申请状态。提交申请后,原注册人通常会向您发送一封电子邮件,询问您是否同意转让。此时,您只需点击同意转移按钮,域名注册商就可以成功转移。
- 六千字详解!讲透 Vue3 响应式是如何实现的
热门文章
站长推荐
当投资者经过第二阶段的认真学习之后又充满了信心,认为自己可以在市场上叱咤风云地大干一场了。但没想到“看花容易绣花难”,由于对理论知识不会灵活运用.从而失去灵活应变的本能,就经常会出现小赢大亏的局面,结果往往仍以失败告终。这使投资者很是困惑和痛苦,不知该如何办,甚至开始怀疑这个市场是不是不适合自己。在这种情况下,有的人选择了放弃,但有的意志坚定者则决定做最后的尝试。
ACCESS 调用后台存储过程的实现方法
union这个连接是有什么用的和INNER JOIN有什么区别
Vue.js + Astro 比 Vue SPA 更好吗?
2、定期提交和投标域名注册。例如,益华网络点击“立即预订”后,平台会抢先为客户注册域名。当然,一个域名可能会被多个客户预订,所以出价最高的人中标。
域名空间是一个什么结构?
SQL Server 链接 Oracle 数据库实战
TS 类型体操:索引类型的映射再映射