以上代码的执行结果如下:
当程序执行完成之后,我们发现在项目的根目录出现了一个新的 log.txt 文件,打开此文件看到如下结果:
从以上结果可以看出标准输出流和标准错误输出流是彼此独立执行的,且 JVM 为了高效的执行会让二者并行运行,所以最终我们看到的结果是 finally 在 catch 之前执行了。
③ 解决方案
知道了原因,那么问题就好处理,我们只需要将 try-catch-finally 中的输出对象,改为统一的输出流对象就可以解决此问题了。
④ 正例代码
private static void execErr() { try { throw new RuntimeException(); } catch (RuntimeException e) { System.out.println(e); } finally { System.out.println("执行 finally."); } }
改成了统一的输出流对象之后,我手工执行了 n 次,并没有发现任何问题。
坑4:finally中的代码“不执行”
finally 中的代码一定会执行吗?如果是之前我会毫不犹豫的说“是的”,但在遭受了社会的毒打之后,我可能会这样回答:正常情况下 finally 中的代码一定会执行的,但如果遇到特殊情况 finally 中的代码就不一定会执行了,比如下面这些情况:
- 在 try-catch 语句中执行了 System.exit;
- 在 try-catch 语句中出现了死循环;
- 在 finally 执行之前掉电或者 JVM 崩溃了。
如果发生了以上任意一种情况,finally 中的代码就不会执行了。虽然感觉这一条有点“抬杠”的嫌疑,但墨菲定律告诉我们,如果一件事有可能会发生,那么他就一定会发生。所以从严谨的角度来说,这个观点还是成立的,尤其是对于新手来说,神不知鬼不觉的写出一个自己发现不了的死循环是一件很容易的事,不是嘛?
① 反例代码
public static void main(String[] args) { noFinally(); } private static void noFinally() { try { System.out.println("我是 try~"); System.exit(0); } catch (Exception e) { // do something } finally { System.out.println("我是 fially~"); } }
以上代码的执行结果如下:
从以上结果可以看出 finally 中的代码并没有执行。