从字节码的角度来看try-catch-finally和return的执行顺序

从字节码的角度来看try-catch-finally和return的执行顺序

全篇以一个例子来说明:

先看如下的例子代码:

public class ExceptionTest {
    public void  testException(){

        try{
            inside_try();
        }
        catch(Exception e){
            inside_catch(e);
        }
        finally{
            inside_finally();
        }
    }
    //分别为try块、catch块和finally块中被调用的函数
    public void inside_try(){   }
    public void inside_catch(Exception e){  }
    public void inside_finally(){}
}

通过javap -c ExceptionTest命令可以看到此类的字节码如下:

如果没有抛异常,那么它的执行顺序为:

 0: aload_0
 1: invokevirtual #2                  // Method inside_try:()V
 4: aload_0
 5: invokevirtual #3                  // Method inside_finally:()V
 8: goto          31
 31: return

即先执行try里面的代码块,然后执行finally里面的代码块。

如果try中抛了一个异常,那么JVM会在如下的异常表中寻找跳转位置。

 Exception table:
    from    to  target type
        0     4    11   Class java/lang/Exception
        0     4    24   any
       11    17    24   any

从异常表中,可以看到有三种异常情况发生导致执行的路径不同:
第一种:如果位于0到4字节之间的命令(即try块中的代码)抛出了Class java/lang/Exception类型的异常,则跳转到第11个字节开始执行catch中的代码;
第二种:如果位于0到4字节之间的命令(即try块中的代码)抛出了任何类型的异常,则跳转到第24个字节开始执行finally里面的代码。
第三种:如果位于11到17字节之间的命令(即catch块中的代码)跑出了任何类型的异常,则跳转到第24个字节开始执行finally里面的代码。

先看第一种情况:如果位于0到4字节之间的命令(即try块中的代码)抛出了Class java/lang/Exception类型的异常,则跳转到第11个字节开始执行catch中的代码

指令如下:

      11: astore_1
      12: aload_0
      13: aload_1
      14: invokevirtual #5                  // Method inside_catch:(Ljava/lang/E
xception;)V
      17: aload_0
      18: invokevirtual #3                  // Method inside_finally:()V
      21: goto          31
      31: return

astore_1会把抛出的异常对象保存到local variable数组的第二个元素。剩余的几行指令用来调用catch和finally块中的方法。

再看第二种情况:如果位于0到4字节之间的命令(即try块中的代码)抛出了任何类型的异常,则跳转到第24个字节开始执行finally中的代码

指令如下:

  24: astore_2
  25: aload_0
  26: invokevirtual #3                  // Method inside_finally:()V
  29: aload_2
  30: athrow
  31: return

astore_2会把抛出的异常对象保存到local variable数组的第二个元素。下面的两行指令用来调用finally块中的方法。

25: aload_0
26: invokevirtual #3                  // Method inside_finally:()V

最后通过如下的指令抛出异常

29: aload_2
  30: athrow

最后一种情况,如果位于11到17字节之间的命令(即catch块中的代码)跑出了任何类型的异常,则跳转到第24个字节开始执行finally里面的代码。

这种情况的代码与上面一样,直接执行finally块中的代码。

再来看下try和catch中有return语句的情形

例子代码如下,在catch块中有return语句:



public class ExceptionTest {
    public void  testException(){

        try{
            inside_try();

        }
        catch(Exception e){
            inside_catch(e);
            return;//catch块中有return语句
        }
        finally{
            inside_finally();
        }
    }
    //分别为try块、catch块和finally块中被调用的函数
    public void inside_try(){   }
    public void inside_catch(Exception e){  }
    public void inside_finally(){}
}

用javap -c ExceptionTest命令查看字节码如下:

从字节码可以看出,即使try块中发生了异常,catch块中的return语句也是在finally块后面执行。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值