③ 解决方案
关于 Java 虚拟机是如何编译 finally 语句块的问题,有兴趣的读者可以参考《The JavaTM Virtual Machine Specification, Second Edition》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译 finally 语句块。
实际上,Java 虚拟机会把 finally 语句块作为 subroutine(对于这个 subroutine 不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解)直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中,待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。
因此如果在 try-catch-finally 中如果有 return 操作,**一定要确保 return 语句只在方法的尾部出现一次!**这样就能保证 try-catch-finally 中所有操作代码都会生效。
④ 正例代码
private static int getValueByAmend() { int num = 1; try { // do something } catch (Exception e) { // do something } finally { num++; } return num; }
坑3:finally中的代码“非最后”执行
① 反例代码
public static void main(String[] args) throws FileNotFoundException { execErr(); } private static void execErr() { try { throw new RuntimeException(); } catch (RuntimeException e) { e.printStackTrace(); } finally { System.out.println("执行 finally."); } }
以上代码的执行结果如下:
从以上结果可以看出 finally 中的代码并不是最后执行的,而是在 catch 打印异常之前执行的,这是为什么呢?
② 原因分析
产生以上问题的真实原因其实并不是因为 try-catch-finally,当我们打开 e.printStackTrace 的源码就能看出一些端倪了,源码如下: