try-catch-finally的return机制

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逻辑

  1. try必须搭配catch或finally一起使用,反正不能单独使用。
  2. 由于JVM会自动处理运行时异常,因此try-finally结构会隐式地增加异常处理,执行异常跳转。
  3. try-catch-finally最终要实现,避免多余的不可达的return,也避免本应存在却未出现的return。
  4. 如果存在finally块,编译器会将try块和catch块return调整成方法体return,为了确保整个代码顺序执行且在各种情况下都存在返回,也没有多余的返回,finally块return和方法体return必定互斥。
  5. try块代码执行时触发异常进入catch块,不会再返回try块执行异常点后的代码,包括return。要是try块也没有return,整个方法存在无返回的情况,编译时直接报错。

方法中代码的执行次序:

try -> catch -> finally -> 方法体
  1. 首先运行try块中的代码,如果正常执行,遇到return,返回相应的值,否则跳到catch块;
  2. 如果捕获异常,进入catch块,执行其代码,遇到return,返回相应的值;
  3. 其次执行finally块中代码,存在return,返回相应的值,否则执行完块内所有代码再跳出finally块;
  4. 最后执行方法体剩余代码,存在return,返回相应的值;

try-catch-finally返回情况流程图
try-catch-finally返回情况流程图

代码示例

java文件字节码文件
ReturnTest.javaReturnTest.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处调整:

  1. 将try块return转换成方法体return
  2. 在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处调整:

  1. 将catch块return转换成方法体return,而try块return保留,因为无冲突。
  2. 由于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'}

字节码文件

编译器对代码结构调整的总结:

  1. 编译器对引用变量和基础类型变量的调整没有区别。
  2. 由于返回变量是引用类型,复制变量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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值