前言
最近遇到一个try->catch->finally->return
问题的变式,由于只知道是这么个顺序,但不知其原理,所以问题稍微变下就答不上来了。现在从字节码角度来分析这个问题。
代码
Sample1
public class TryCatchTest {
public static void main(String[] args) {
System.out.println(finallyTest());
}
public static int finallyTest() {
int a;
try {
a = 1;
if (a == 1) {
throw new RuntimeException("deliberate...");
}
return a;
} catch (RuntimeException e) {
a = 2;
if (a == 2) {
throw new RuntimeException("deliberate1...");
}
return a;
} finally {
a = 3;
return a;
}
}
}
Sample2
public class TryCatchTest {
public static void main(String[] args) {
System.out.println(finallyTest());
}
public static int finallyTest() {
int a;
try {
a = 1;
if (a == 1) {
throw new RuntimeException("deliberate...");
}
return a;
} catch (RuntimeException e) {
a = 2;
if (a == 2) {
throw new RuntimeException("deliberate1...");
}
} finally {
a = 3;
}
return a;
}
}
上述两段代码的区别就在于finally
代码块内是否有return
语句;猜下两段代码执行的效果。
这里给出结论:
Sample1
打印3
;Sample2
抛出异常;
字节码
我们现在从字节码入手开始分析, ##
后为笔者注释内容
Sample1
public static int finallyTest();
Code:
0: iconst_1 ## 从常量池里获取整数1
1: istore_0 ## 将值存入第一个整型变量
2: iload_0 ## 加载第一个整型变量的值
3: iconst_1 ## 从常量池里获取整数1
4: if_icmpne 17 ## 比较 如果不符合条件 跳到17行
7: new #5 // class java/lang/RuntimeException
10: dup
11: ldc #6 // String deliberate...
13: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
16: athrow ## 抛异常,到下方的异常表去查询目标命令行,这里是跳到23行
17: iload_0 ## 加载第一个整型变量的值
18: istore_1 ## 将值存入第二个整型变量 这里很重要,这是一个匿名变量
19: iconst_3 ## 从常量池里获取整数3 这里是finally的代码块
20: istore_0 ## 将值存入第一个整型变量
21: iload_0 ## 加载第一个整型变量的值
22: ireturn ## 返回
23: astore_1 ## 将引用值存入第一个引用变量,这里是异常对象
24: iconst_2 ## 从常量池里获取整数2 这里是catch的代码块
25: istore_0 ## 将值存入第一个整型变量
26: iload_0 ## 加载第一个整型变量的值
27: iconst_2 ## 从常量池里获取整数2
28: if_icmpne 41 ## 比较 如果不符合条件 跳到41行
31: new #5 // class java/lang/RuntimeException
34: dup
35: ldc #8 // String deliberate1...
37: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
40: athrow ## 抛异常,到下方的异常表去查询目标命令行,这里是跳到47行
41: iload_0 ## 加载第一个整型变量的值
42: istore_2 ## 将值存入第三个整型变量 这里很重要,这是一个匿名变量
43: iconst_3 ## 从常量池里获取整数3 这里是finally的代码块
44: istore_0 ## 将值存入第一个整型变量
45: iload_0 ## 加载第一个整型变量的值
46: ireturn ## 返回
47: astore_3 ## 将引用值存入第三个引用变量,这里是异常对象
48: iconst_3 ## 从常量池里获取整数3 这里是finally的代码块
49: istore_0
50: iload_0
51: ireturn
Exception table:
from to target type
0 19 23 Class java/lang/RuntimeException
0 19 47 any
23 43 47 any
我们带入Sample1
的逻辑,执行的顺序应该是0~16->23->24~40->47
,
最后的字节码是
48: iconst_3
49: istore_0
50: iload_0
51: ireturn
因为前面的return
其实都是新建了一个匿名对象,所以不影响iload_0
;
Sample2
这块字节码的解析就不进行了,直接说调用链0~16->23->24~40->46
最后的字节码是
47: iconst_3
48: istore_0
49: aload_2
50: athrow
public static int finallyTest();
Code:
0: iconst_1
1: istore_0
2: iload_0
3: iconst_1
4: if_icmpne 17
7: new #5 // class java/lang/RuntimeException
10: dup
11: ldc #6 // String deliberate...
13: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
16: athrow
17: iload_0
18: istore_1
19: iconst_3
20: istore_0
21: iload_1
22: ireturn
23: astore_1
24: iconst_2
25: istore_0
26: iload_0
27: iconst_2
28: if_icmpne 41
31: new #5 // class java/lang/RuntimeException
34: dup
35: ldc #8 // String deliberate1...
37: invokespecial #7 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
40: athrow
41: iconst_3
42: istore_0
43: goto 51
46: astore_2
47: iconst_3
48: istore_0
49: aload_2
50: athrow
51: iload_0
52: ireturn
Exception table:
from to target type
0 19 23 Class java/lang/RuntimeException
0 19 46 any
23 41 46 any
思考
根据try->catch->finally->return规则,思考以下输出结果:
public class TryCatchTest {
public static void main(String[] args) {
System.out.println(finallyTest());
}
public static int finallyTest() {
int a;
try {
a = 1;
if (a == 1) {
throw new RuntimeException("deliberate...");
}
return a;
} catch (RuntimeException e) {
a = 2;
return a;
} finally {
a = 3;
}
}
}
从上面的字节码可以知道,
return
一个基础变量的时候,其实是会将该变量值暂存在一个匿名变量里,return
的时候,return
的是匿名变量,而不是原来的变量;