经常在面试题中会看到:exit、finally、return的提问,这也是考察个人的基础知识。
一般(注意我的措辞‘一般’)我们都知道exit()是退出虚拟机,那么一旦调用了exit,则不管什么
return 、finally统统忽略不计,但前段时间看到了一篇博文“关于java的10个谎言”(搜了一下,好多
地方都有,也不知道谁是"首发"),这里要小记一下第一个问题(System.exit(0)会跳过finally的执
行?)的思考。
1、三者一般执行顺序
a、return,表示方法执行返回或者结束方法运行,其实就是方法退出栈执行
b、exit(0) ,退出虚拟机(一般认为就立马结束虚拟机生命)
c、finally ,一般直接跟在try块或者catch块后面,在try或catch语句块中控制转移语句之前执行。
eixt,很好理解,那return与finally呢?可以这样通俗的理解:
在面向对象程序执行过程其实就是各种对象之间通信,对象之间的通信不就是方法调用吗,是的,所以程序执行就是一个个方法的运行,所有方法运行完了,程序也就结束了,而finally只是一个语句,那么他势必是属于某个方法的(事实是,编译器在编译finally块的时候也只是这么做的),而return是方法的结束语句,这样一来,二者就转化为顺序执行了——先finally后return(方法都要退出栈了,还不执行finally,等待何时 ?)。
其实上面的只是帮助理解,本质上,finally块在虚拟机编译处理的时候,会将finally块的语句插入到try
或者catch语句块中的控制转移语句之前,而return就是控制转移语句之一,所以,当二者同时出现
时,finally语句块会被编译器插如到return之前 。
2、System.exit(0)会跳过finally的执行? 真的吗?
上面对三者的执行顺序做了简单总结,但是运行如下代码试试,看exit后为啥还执行finally?
public static void main(String[] args) { System.setSecurityManager(new SecurityManager(){ @Override public void checkExit(int status) { throw new ThreadDeath(); } }); try { System.exit(0); } finally{ System.out.println("in finally block "); } }
执行结果可能颠覆了前面说的:exit后其他都不执行,其实,前面说的是一般情况,而且99%的情况都
是那样的顺序,只是上面的代码耍了小心机(或者说我们没仔细探究exit方法)。
上面代码之所以会输出“in finally block”,是因为前面设置的SecurityManager中覆盖了checkExit方法,导致虚拟机并没有退出,为什么呢,请看分析:
其实,这一切都是因为exit方法的实现过程,以下片段为Runtime 类中exit方法源码:
public void exit(int status) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkExit(status); } Shutdown.exit(status); }
main方法开始自上而下执行,首先设置了一个安全管理器实例,接着调用System.exit(0) ,调用时执行上面的exit方法,运行环境获取到了我们自定义安全管理器,并开始调用覆盖的方法,checkExit ,但checkExit方法抛出了error(不是exception,请看ThreadDeath类),导致exit的中Shutdown.exit()调用失败,从而导致虚拟机并没有退出,那么finally当然要执行了。
经此,看来我们以后在学习过程中一定要看一手资料,多看细节实现。