由一道面试题想到的:Finally
2016-05-16 19:23
253 查看
找工作时,有这样一道题:
try{}里面有一条return语句,那么紧跟在这个try后的finally{}里的代码会不会执行,什么时候执行,在return之前还是之后?
我没有怎么思考,根据脑子里仅有的一点异常知识,给出了下面的解:
如果try{}没有抛出异常,那么finally{}里的代码不会执行。
如果try{}抛出异常,那么finally{}里的代码会执行,在return之前。
借助下面的代码验证一下:
毫无疑问,输出的结果显示上述解是草率的,是错的。那么,正解是什么?其实,读书用心点的话,可以注意到《Core Java》上的这段话:
Suppose you exit the middle of a try block with a return statement. Before the method returns, the finally block is executed. If the finally block also contains a return statement, then it masks the original return value.
——摘自《Core Java, Volume I: Fundamentals》
这段话就是正解。当方法中抛出异常,或包含break,continue,return语句时,方法就无法按照正常的方式终止。一般地,在try-finally结构中,当try部分意外终止时,并不意味着结束,方法仍然会继续执行finally部分。也就是说,上述代码返回1之前,会先打印“Finally!”。如果问原因,真说不出什么,没什么道理,Java就是这么干的。就和学英语遇到陌生的词组解释不了时,用“固定搭配”来安慰自己,是同一个道理。
进一步地,有没有某种情况,finally{}不会执行?另一个例子:
System.exit(0)可以阻止执行finally语句块。之所以可以做到,是因为System.exit()方法会立即终止当前正在运行的 Java 虚拟机。我们知道,在Java中,源码首先被编译成字节码,然后由虚拟机解释执行class文件。虚拟机进程被杀死后,程序当然无法接着执行下去。
众所周知,在需要关闭一些资源时,使用Finally语句是一种不错的选择。下面介绍一种结合try-catch和try-finally的方式:
在这种方式中,内存的try语句块只有一个职责,就是关闭输入流。外层的try语句块也只有一个职责,就是捕获可能出现的异常。职责相当明确,结构相当清晰。但是,这种方式也存在潜在的错误:如果[2]处在关闭输入流时也抛出了IOException,那么[3]处捕获的异常究竟是[1]处抛出的,还是[2]处抛出的。事实上,若出现上述情况,则[3]处实际捕获到[2]处抛出的异常,会忽略[1]处抛出的异常。这是需要小心的地方!
以上是对异常,尤其是Finally语句易错点的总结,仅供参考。
try{}里面有一条return语句,那么紧跟在这个try后的finally{}里的代码会不会执行,什么时候执行,在return之前还是之后?
我没有怎么思考,根据脑子里仅有的一点异常知识,给出了下面的解:
如果try{}没有抛出异常,那么finally{}里的代码不会执行。
如果try{}抛出异常,那么finally{}里的代码会执行,在return之前。
借助下面的代码验证一下:
private static int testFinally() { try { System.out.println("Try!"); return 1; } finally { System.out.println("Finally!"); } }
毫无疑问,输出的结果显示上述解是草率的,是错的。那么,正解是什么?其实,读书用心点的话,可以注意到《Core Java》上的这段话:
Suppose you exit the middle of a try block with a return statement. Before the method returns, the finally block is executed. If the finally block also contains a return statement, then it masks the original return value.
——摘自《Core Java, Volume I: Fundamentals》
这段话就是正解。当方法中抛出异常,或包含break,continue,return语句时,方法就无法按照正常的方式终止。一般地,在try-finally结构中,当try部分意外终止时,并不意味着结束,方法仍然会继续执行finally部分。也就是说,上述代码返回1之前,会先打印“Finally!”。如果问原因,真说不出什么,没什么道理,Java就是这么干的。就和学英语遇到陌生的词组解释不了时,用“固定搭配”来安慰自己,是同一个道理。
进一步地,有没有某种情况,finally{}不会执行?另一个例子:
private static void testFinally() { try { System.out.println("Try!"); System.exit(0); } finally { System.out.println("Finally!"); } }
System.exit(0)可以阻止执行finally语句块。之所以可以做到,是因为System.exit()方法会立即终止当前正在运行的 Java 虚拟机。我们知道,在Java中,源码首先被编译成字节码,然后由虚拟机解释执行class文件。虚拟机进程被杀死后,程序当然无法接着执行下去。
众所周知,在需要关闭一些资源时,使用Finally语句是一种不错的选择。下面介绍一种结合try-catch和try-finally的方式:
InputStream in = ...; try{ try{ // [1]Code may throw excption. }finally{ in.close(); //[2] } }catch(IOException e){ // [3]handle exception. }
在这种方式中,内存的try语句块只有一个职责,就是关闭输入流。外层的try语句块也只有一个职责,就是捕获可能出现的异常。职责相当明确,结构相当清晰。但是,这种方式也存在潜在的错误:如果[2]处在关闭输入流时也抛出了IOException,那么[3]处捕获的异常究竟是[1]处抛出的,还是[2]处抛出的。事实上,若出现上述情况,则[3]处实际捕获到[2]处抛出的异常,会忽略[1]处抛出的异常。这是需要小心的地方!
以上是对异常,尤其是Finally语句易错点的总结,仅供参考。
相关文章推荐
- 面试基础整理(一)---Java中==和equals的区别
- SegmentFault 巨献 1024 程序员护卫队#1红岸的呼唤
- iOS面试常见问题最全梳理
- 面试基本问题
- 自我总结初级程序员易错问题1—命令空间使用
- 2016年社招面试小结
- 奋斗吧,程序员——第三十七章.雄关漫道真如铁,而今迈步从头越
- 如何成为一名优秀的程序员
- JAVA多线程和并发基础面试问答
- 面试大总结:Java搞定面试中的二叉树题目
- 链表面试题Java实现【重要】
- 程序员必须掌握的8大排序算法(排序舞蹈)
- 数据库工程师职业规划
- [转]PHP程序员的技术成长规划
- 面试中的智力题及编程实践(二)
- 程序员如何承接软件外包项目
- 迄今见过的最好的职业规划文章
- 优秀程序员
- .net面试题
- 面试题 <数组中出现超过一半的数字>(7)