Java嵌套try-finally块异常覆盖问题:为何NullPointerException未抛出反而被IOException掩盖?编译器如何判定执行的异常?
为什么finally中的异常会掩盖try里的NullPointerException?
这个问题其实是Java异常处理里一个很经典的“坑”,咱们一步步拆解代码的执行流程,就能明白为什么那个NullPointerException好像凭空消失了:
代码执行的完整流程
- 外层
try块成功创建Image对象,进入内层try块。 - 执行
image.print()后,抛出了NullPointerException。 - 这里是关键:只要对应的try块被执行过(没有调用
System.exit()这类直接终止JVM的操作),finally块一定会优先执行,不管try里有没有抛出异常。所以此时JVM会暂停处理try里的NPE,先跑去执行finally块。 - 在finally块中,先打印
FINALLY,执行image.close(),然后抛出了IOException。 - 当finally块抛出新的异常时,Java虚拟机的规则是:原来try块中未被处理的异常(也就是那个NPE)会被自动“压制”并丢弃,JVM只会把finally里抛出的异常传递给上层处理。
- 外层的
catch (IOException ex)块捕获到这个异常,打印对应的信息,之后程序正常执行最后的System.out.println("THREAD IS CONTINUE");。
关于“被压制的异常”补充
在Java 7及之后的版本里,其实被压制的异常并没有完全消失——你可以通过Throwable.getSuppressed()方法获取到它们。比如如果我们稍微修改代码,把finally的异常处理一下,就能看到被压制的NPE:
try { Image image = new Image("MyComputer/Documents/Photos/estambul.jpg"); try { image.print(); throw new NullPointerException("测试NPE"); } finally { System.out.println("FINALLY"); image.close(); try { throw new IOException("测试IO异常"); } catch (IOException e) { // 获取被压制的异常(也就是try里的NPE) for (Throwable suppressed : e.getSuppressed()) { System.out.println("被压制的异常:" + suppressed.getMessage()); } throw e; // 重新抛出IO异常 } } } catch (IOException ex){ System.out.println("IO_EXCEPTION"); System.out.println(ex.getMessage()); }
不过默认情况下,上层代码只会感知到finally抛出的异常,这就是你觉得NPE被“掩盖”的原因。
核心规则总结
finally块的执行优先级高于try块异常的处理流程- 当finally块抛出异常时,原try块中未处理的异常会被压制,默认只有finally的异常会被上层捕获处理
内容的提问来源于stack exchange,提问作者Backwindow
相关产品推荐
相关产品推荐

