try-catch-finally中的return

try-catch-finally

try: 正常执行的语句

catch: try中抛出相应的异常时执行的语句

finally: 不管执行的是try还是catch, 最终都会执行的语句

先回顾以上基础

AutoClose

从jdk1.7开始, 可以在try后面加一对圆括号, 把要关闭的资源放到圆括号里声明, 可以自动关闭

File f = new File("test");
try (
InputStream is = new FileInputStream(f);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
) {
br.readLine();
} catch (Exception e) {
}

自动关闭的资源需要实现AutoCloseable接口

实际上AutoCloseable接口是jdk1.7新增的, 而Closeable接口继承自AutoCloseable, 所以只要实现Closeable接口即可

带有return语句的try-catch-finally

public static int tryCatchFinally() {
int a = 1, b = 2;
try {
return ++a + b;
} catch(Exception e) {
return a;
} finally {
return b;
}
}

使用javap反编译字节码, 部分如下

Code:
stack=1, locals=3, args_size=0
0: iconst_1
1: istore_0
2: iconst_2
3: istore_1
4: iinc 0, 1
7: goto 15
10: astore_2
11: goto 15
14: pop
15: iload_1
16: ireturn
Exception table:
from to target type
4 10 10 Class java/lang/Exception
4 14 14 any

大致意思:

如果代码4到10(try中的语句)发生异常(catch中捕获的异常)就跳转到10继续运行

如果代码4到14(try和catch)发生异常(任何异常)就跳转到14继续运行

如果不发生异常, 会在7执行goto, 跳转至15

或者try发生异常被catch捕获, 那就会在11执行goto, 跳转至15

所以最终执行的return语句是finally中的return

try-catch中有return, finally中无return

int a = 1, b = 2;
try {
return a;
} catch(Exception e) {
return b;
} finally {
a = 10;
b = 20;
}

在finally中对a和b赋值, 在try和catch中返回a和b

结果return的a和b是1和2, 赋值语句也确实执行了, 只是未能对返回结果造成影响

具体什么情况呢? 还是看字节码

Code:
stack=1, locals=5, args_size=0
0: iconst_1
1: istore_0
2: iconst_2
3: istore_1
4: iload_0
5: istore 4
7: bipush 10
9: istore_0
10: bipush 20
12: istore_1
13: iload 4
15: ireturn
16: astore_2
17: iload_1
18: istore 4
20: bipush 10
22: istore_0
23: bipush 20
25: istore_1
26: iload 4
28: ireturn
29: astore_3
30: bipush 10
32: istore_0
33: bipush 20
35: istore_1
36: aload_3
37: athrow
Exception table:
from to target type
4 7 16 Class java/lang/Exception
4 7 29 any
16 20 29 any

与上次不同的是: 这次的字节码没有goto指令

两个ireturn指令把把这段逻辑分成了三部分, 分别对应try, catch, 和未catch的异常情况

finally中的语句都在ireturnathrow之前被执行

至于为什么finally中的语句未对返回值造成影响, 三段也都一样, 这里挑try中的指令来看一下

4: iload_0 // 把0位置的变量(a)值推入栈顶
5: istore 4 // 把栈顶值存入4位置变量
7: bipush 10 // 把单字节常量值10推入栈顶
9: istore_0 // 把栈顶值赋给0位置变量(a)
10: bipush 20
12: istore_1 // 同上
13: iload 4 // 把4位置变量值推入栈顶
15: ireturn // 返回栈顶值

总结一下就是: 当在try或catch中返回将在finally中要修改的基本类型变量时, 虚拟机会缓存将要返回的值, 再执行finally中的语句修改变量

为什么要这样设计呢

感觉这个有点类似上一篇内部类访问局部变量为什么必须要用final修饰中的原因

为了消除代码的不确定性, 保持代码的简单易懂

posted @   树是树非树  阅读(506)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示