return类型定义
private static String returnType() {
String str = "start";
try {
return str = "try ruturn"; //这里定义为try块return
} catch (Exception e) {
return str = "catch ruturn"; //这里定义为catch块return
}finally {
return str = "finally ruturn"; //这里定义为finally块return
}
return "method return"; //这里定义为方法体return
}
try-catch-finally逻辑
- try必须搭配catch或finally一起使用,反正不能单独使用。
- 由于JVM会自动处理运行时异常,因此try-finally结构会隐式地增加异常处理,执行异常跳转。
- try-catch-finally最终要实现,避免多余的不可达的return,也避免本应存在却未出现的return。
- 如果存在finally块,编译器会将try块和catch块return调整成方法体return,为了确保整个代码顺序执行且在各种情况下都存在返回,也没有多余的返回,finally块return和方法体return必定互斥。
- try块代码执行时触发异常进入catch块,不会再返回try块执行异常点后的代码,包括return。要是try块也没有return,整个方法存在无返回的情况,编译时直接报错。
方法中代码的执行次序:
try -> catch -> finally -> 方法体
- 首先运行try块中的代码,如果正常执行,遇到return,返回相应的值,否则跳到catch块;
- 如果捕获异常,进入catch块,执行其代码,遇到return,返回相应的值;
- 其次执行finally块中代码,存在return,返回相应的值,否则执行完块内所有代码再跳出finally块;
- 最后执行方法体剩余代码,存在return,返回相应的值;
try-catch-finally返回情况流程图
代码示例
java文件 | 字节码文件 |
---|---|
ReturnTest.java | ReturnTest.class |
1. try-finally 返回int类型
public int rtnIntNoCatch(int n){
try {
n += 20;
return n;
} finally {
n += 30;
System.out.printf("n=%d\n", n);
}
}
执行结果
入参: n=10
n=60
rtnIntNoCatch: 30
字节码文件:
编译器对代码结构做了2处调整:
- 将try块return转换成方法体return
- 在try块return之前复制返回变量,并将复制变量作为最终的返回
public int rtnIntNoCatch(int n) {
int var2;
try {
n += 20;
var2 = n;
} finally {
n += 30;
System.out.printf("n=%d\n", n);
}
return var2;
}
字节码指令
可以在IDEA中安装jclasslib或者命令行中使用javap查看程序字节码,对其执行过程会有清楚具体的认识。
0 iinc 1 by 20
3 iload_1
4 istore_2
5 iinc 1 by 30
8 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
11 ldc #18 <n=%d>
13 iconst_1
14 anewarray #10 <java/lang/Object>
17 dup
18 iconst_0
19 iload_1
20 invokestatic #12 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
23 aastore
24 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
27 pop
28 iload_2
29 ireturn
30 astore_3
31 iinc 1 by 30
34 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
37 ldc #18 <n=%d>
39 iconst_1
40 anewarray #10 <java/lang/Object>
43 dup
44 iconst_0
45 iload_1
46 invokestatic #12 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
49 aastore
50 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
53 pop
54 aload_3
55 athrow
2. try-catch-finally 返回int类型
public int rtnIntWithCatch(int n){
try {
n += 20;
int i = 1/0;
return n;
} catch (Exception e){
n += 10;
return n;
} finally {
n += 30;
System.out.printf("n=%d\n", n);
}
}
执行结果
入参: n=10
n=70
rtnIntWithCatch: 40
字节码文件
编译器对代码结构做了2处调整:
- 将catch块return转换成方法体return,而try块return保留,因为无冲突。
- 由于finally块的存在,在catch块return之前复制返回变量,并将复制变量
var3
作为最终的返回
public int rtnIntWithCatch(int n) {
int var3;
try {
n += 20;
int i = 1 / 0;
var3 = n;
return var3;
} catch (Exception var7) {
n += 10;
var3 = n;
} finally {
n += 30;
System.out.printf("n=%d\n", n);
}
return var3;
}
字节码指令
0 iinc 1 by 20
3 iconst_1
4 iconst_0
5 idiv
6 istore_2
7 iload_1
8 istore_3
9 iinc 1 by 30
12 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
15 ldc #18 <n=%d>
17 iconst_1
18 anewarray #10 <java/lang/Object>
21 dup
22 iconst_0
23 iload_1
24 invokestatic #12 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
27 aastore
28 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
31 pop
32 iload_3
33 ireturn
34 astore_2
35 iinc 1 by 10
38 iload_1
39 istore_3
40 iinc 1 by 30
43 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
46 ldc #18 <n=%d>
48 iconst_1
49 anewarray #10 <java/lang/Object>
52 dup
53 iconst_0
54 iload_1
55 invokestatic #12 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
58 aastore
59 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
62 pop
63 iload_3
64 ireturn
65 astore 4
67 iinc 1 by 30
70 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
73 ldc #18 <n=%d>
75 iconst_1
76 anewarray #10 <java/lang/Object>
79 dup
80 iconst_0
81 iload_1
82 invokestatic #12 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
85 aastore
86 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
89 pop
90 aload 4
92 athrow
3. try-finally 返回User对象
public User rtnUserNoCatch(User user){
try {
user.setId("try-id");
user.setName("try-name");
return user;
} finally {
user.setId("try-finally-id");
user.setName("try-finally-name");;
System.out.printf("user=%s\n", user.toString());
}
}
执行结果
入参: User user = new User("1","Tale");
user=User{id='try-finally-id', name='try-finally-name'}
rtnUserNoCatch: User{id='try-finally-id', name='try-finally-name'}
字节码文件
编译器对代码结构调整的总结:
- 编译器对引用变量和基础类型变量的调整没有区别。
- 由于返回变量是引用类型,复制变量
var2
和入参user
都指向堆中的同一个User对象
,其引用地址相同,因此printf
输出和return
返回的结果是相同的。
public User rtnUserNoCatch(User user) {
User var2;
try {
user.setId("try-id");
user.setName("try-name");
var2 = user;
} finally {
user.setId("try-finally-id");
user.setName("try-finally-name");
System.out.printf("user=%s\n", user.toString());
}
return var2;
}
字节码指令
0 aload_1
1 ldc #20 <try-id>
3 invokevirtual #21 <basic/trycatch/User.setId : (Ljava/lang/String;)V>
6 aload_1
7 ldc #22 <try-name>
9 invokevirtual #23 <basic/trycatch/User.setName : (Ljava/lang/String;)V>
12 aload_1
13 astore_2
14 aload_1
15 ldc #24 <try-finally-id>
17 invokevirtual #21 <basic/trycatch/User.setId : (Ljava/lang/String;)V>
20 aload_1
21 ldc #25 <try-finally-name>
23 invokevirtual #23 <basic/trycatch/User.setName : (Ljava/lang/String;)V>
26 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
29 ldc #26 <user=%s>
31 iconst_1
32 anewarray #10 <java/lang/Object>
35 dup
36 iconst_0
37 aload_1
38 invokevirtual #27 <basic/trycatch/User.toString : ()Ljava/lang/String;>
41 aastore
42 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
45 pop
46 aload_2
47 areturn
48 astore_3
49 aload_1
50 ldc #24 <try-finally-id>
52 invokevirtual #21 <basic/trycatch/User.setId : (Ljava/lang/String;)V>
55 aload_1
56 ldc #25 <try-finally-name>
58 invokevirtual #23 <basic/trycatch/User.setName : (Ljava/lang/String;)V>
61 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
64 ldc #26 <user=%s>
66 iconst_1
67 anewarray #10 <java/lang/Object>
70 dup
71 iconst_0
72 aload_1
73 invokevirtual #27 <basic/trycatch/User.toString : ()Ljava/lang/String;>
76 aastore
77 invokevirtual #13 <java/io/PrintStream.printf : (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;>
80 pop
81 aload_3
82 athrow