finally子句与return语句调用先后问题及具体原理解析

分析和例子来源于《深入java虚拟机》一书。

finally语句一定会执行吗?

答案是不一定

虽然很多地方都强调一些释放资源的方法要在finally语句块中执行,因为finally语句块一定会执行,但是其实也是有条件的。

finally语句不执行的两个条件:

  • 在执行try{}catch语句之前已经退出程序

  • try{}catch语句中执行了System.exit()语句导致JVM退出并停止工作

只要是程序到达了try{}catch语句块,除非JVM停止工作,否则都会执行finally语句。那如果try{}catch语句或者finally语句中含有return语句,会不会结束程序呢?如果不,那又是怎样工作的呢?

finally子句的调用过程

首先看表面,JVM在每个try语句块和与其相关的catch子句的结尾处都会“调用”finally子句,finally子句结束后(这里结束指的是finally子句的最后一条语句正常执行完毕,不包括抛出异常,或者执行return、continue、break等情况),执行返回操作,程序在第一次调用finally语句的地方继续执行后面的语句。

然后介绍子例程,子例程就是主程序中的一段代码,在主程序中可以调用,执行特定的功能同时与主程序相对独立。书中将finally子句在方法内部的行为看作是一个“微型子例程”,执行用户设置的功能同时与外部程序相对独立。

接着介绍原理,当程序需要执行finally子例程的时候,会先将返回地址压入到栈中,然后从子例程的开始处继续执行。子例程执行完毕后,将调用ret指令,该指令的功能是执行子例程返回的操作,该指令中只有一个操作数,保存的是返回地址的局部变量的索引。

这里可能会疑惑,为什么前面将返回地址压入栈,而这里ret指令保存的是局部变量的索引,不应该是从栈中弹出返回地址,执行返回操作吗?这里就涉及到了return等命令。

不对称的调用和返回

不对称指的是前面压入栈,后面不通过出栈返回值。实际上,在每个子例程开始处,返回地址都从栈顶端弹出,并且保存在局部变量中。稍后的ret指令会从这个局部变量中取出返回地址。

这样的不对称调用是很必要的,因为finally子句本身会抛出异常或者含有return、break、continue等语句,如果在执行finally子句时遇到了退出语句,可能会导致栈帧被弹出或栈被清空,如果返回地址还被压入在栈中,会被清除,换句话说也就是finally无法正常执行完,ret指令也就不会调用,返回地址也就无法拿到,程序就无法从跳转的地方继续执行。所以需要保存在局部变量中。

下面代码包含了一个通过break语句退出的finally子句,执行的结果是,无论参数传入什么,该方法都将返回false

public static boolean test(boolean value){
    while(value){
        try{
            return true;
        }
        finally{
            break;
        }
    }
    return false;
}

虽然try语句中让方法返回了,但是因为后面紧跟着finally子句,所以将while语句终结处的地址压入栈中,然后开始子例程,在子例程一开始的时候,就将地址所在的栈帧弹出并将返回地址赋值给一个局部变量,然后执行break语句退出while循环,最后返回false。也就是说return在此例中并没有起到作用。

实际上,return还是执行了操作的,return将返回的值保存到一个局部变量中,如果后面finally子句中执行退出语句,那么此返回值无效;如果没有退出,分两种情况,一种是在finally子句中改变了需要返回的变量的值,但这种操作并不会影响前面已经保存了的返回值,程序依然会返回局部变量中的值,另一种情况是想要在finally子句中改变返回值,那么就必须在finally中加入return语句,用来返回被finally子句更新过的返回值。

上面的解释也可以说明为什么finally子句被称为“微型子例程”,因为它的运行相当独立,首先程序很大的几率会执行此语句,此外它的执行与主程序关系不大,逻辑上的联系不多,更重要的是,它对于主程序中的变量做出的改变大多没有落到实处,也就是没有起到作用,像是return语句,所以子例程很恰当。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值