JAVA之try{}catch{}finally{}

本文从字节码的角度分析JAVA中try{}catch{}finally{}的执行顺序和原理,通过Sample1和Sample2的代码示例,探讨在不同情况下的字节码生成及其对变量的影响。最后提出了try->catch->finally->return的执行规则,并根据字节码解释了基础类型变量在finally块中的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

最近遇到一个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的是匿名变量,而不是原来的变量;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值