浅析finally关键字如何一定执行

finally关键字通常和try/catch搭配使用,当程序执行到try中时,finally中的代码一定会执行。

finally为什么会一定执行?

    public static void main(String[] args) {
        try {
            System.out.println("try");
        } catch (Exception e){
            System.out.println("catch");
        } finally {
            System.out.println("finally");
        }
    }

将上面程序运行后反编译得到字节码指令如下(摘取了一部分,先扫一遍,耐心看完会有收获的^_^):

      stack=2, locals=3, args_size=1                                                                                           ①
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String try
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: ldc           #5                  // String finally
        13: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        16: goto          50


        19: astore_1                                                                                                                           
        20: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        23: ldc           #7                  // String catch
        25: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        31: ldc           #5                  // String finally
        33: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        36: goto          50


        39: astore_2                                                                                                                       
        40: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        43: ldc           #5                  // String finally
        45: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        48: aload_2
        49: athrow
        50: return


      Exception table:                                                                                                                     
         from    to  target type
             0     8    19   Class java/lang/Exception
             0     8    39   any
            19    28    39   any

Exception table的解释:④中Exception table就是异常表,当[from,to)中出现type类型的异常时就跳转到target继续执行。例如在[0,8)中发生了Exception的异常就跳转到19(astore_1)继续执行。至于这个异常表为什么还有后两个?且埋下一个伏笔。

①中执行了try和finally的代码②中执行了catch和finally的代码在try和catch后面都执行了finally的代码,这是finally初步实现的方式,保证try和catch执行完后一定会执行finally。


在③中,return操作前finally代码又被执行了,这是为什么?

在这个问题之前有两个问题需要思考:

1:如果catch捕获的是空指针异常,而try中出现了by zero异常,由于type不一样,catch捕获不到,此时程序会由于异常而终止,finally会不会执行不了?不妨运行一下:

    public static void main(String[] args) {
        try {
            System.out.println("try");
            throw new ArithmeticException();
        } catch (NullPointerException e){
            System.out.println("catch");
        } finally {
            System.out.println("finally");
        }
    }

这样的运行情况说明了异常表中的第二项存在的原因:finally会捕获在try中catch捕获不到的异常,执行完finally的代码然后将这个异常抛出。

2:如果catch中出现异常finally还会执行吗?再运行一下:

    public static void main(String[] args) {
        try {
            System.out.println("try");
            throw new NullPointerException();
        } catch (NullPointerException e){
            System.out.println("catch");
            throw new ArithmeticException();
        } finally {
            System.out.println("finally");
        }
    }

这进一步说明了异常表中的第三项存在的原因:finally会捕获在catch代码中的异常,执行完finally的代码然后将这个异常抛出。

此时看③中finally代码的执行是不是有了新感悟?当捕获到try中catch捕获不到的或catch中的异常时,将异常存储在slot 2中(astore_2:将操作数栈栈顶元素存储在局部变量表的slot 2中并在栈中弹出此元素),然后执行finally代码,最后加载异常并抛出后返回。

finally捕获try中catch捕获不到的或catch中的异常又给它的"一定执行"上了一层保险。

注意:在多个catch或multi-catch的情况下,finally代码在每个catch后都执行,异常表也会加上相应的项。


当try中出现return时,finally会执行吗?

    public static int test2(){
        try {
            return 10;
        } finally {
            System.out.println("finally");
        }
    }
    public static void main(String[] args) {
        System.out.println(test2());
    }

如图:finally依然会执行,继续看看它的字节码指令:

      stack=2, locals=2, args_size=0                                                                                       
         0: bipush        10
         2: istore_0
         3: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: ldc           #5                  // String finally
         8: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        11: iload_0
        12: ireturn


        13: astore_1                                                                                                                   
        14: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        17: ldc           #5                  // String finally
        19: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        22: aload_1
        23: athrow


      Exception table:
         from    to  target type
             0     3    13   any

①中是try的代码,首先bipush 10将return的10压入操作数栈,然后将其弹出存放在局部变量表的一号单元,随后执行finally代码,在最后加载存放在局部变量表的一号单元到操作数栈,然后return,保证了finally的执行。

所以在try中出现return,如果有finally关键字,它需要先将返回信息暂存一下,等到finally代码执行完后再进行返回,这给finally的"一定执行"加了一层保险。

总结一下:

①:try和catch代码后面都执行了finally的代码;

②:finally可以捕获并抛出在try中catch捕获不到的或catch中的异常;

③:在try中出现return,如果有finally关键字,它需要先将返回信息暂存一下,等到finally代码执行完后再进行返回。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值