JDK14引入、JDK15默认开启的详细NullPointerException消息(JEP 358)常被认为"开箱即用能精确定位到字段名"——这篇真实对比默认javac编译和加-g编译两种情况下,同一段代码抛出的NPE消息到底有什么区别。
构造四种典型的空指针场景,逐一捕获异常并打印getMessage()。
HelpfulNpeDemo.java User user = new User(); // address字段是null user.address.city.length(); // 真实哪一步空了?
真实捕获到的NPE消息(单层): Cannot read field "city" because "user.address" is null 真实捕获到的NPE消息(数组元素): Cannot read field "address" because "users[1]" is null 真实捕获到的NPE消息(数组.length): Cannot read the array length because "arr" is null 真实捕获到的NPE消息(方法参数链式调用): Cannot read field "address" because "u" is null
真实结果证明JEP 358的详细NPE消息确实强大:它不只是告诉你"哪一行"抛了空指针,而是真实精确说出链式调用user.address.city.length()中,是user.address这一段为null(不是city或者更后面),数组访问也能精确到users[1]这个具体下标。这背后是JVM在字节码层面真实追踪了每一步getfield/aaload操作的调用链,而不是简单的行号定位。
上面的结果是用javac -g(保留完整调试信息)编译得到的。如果用最普通的javac(不带任何参数)编译,结果还一样吗?
编译对比 javac HelpfulNpeDemo.java # 默认参数 javac -g HelpfulNpeDemo.java # 显式加上-g
真实捕获到的NPE消息(单层): Cannot read field "city" because "<local1>.address" is null 真实捕获到的NPE消息(数组元素): Cannot read field "address" because "<local2>[1]" is null 真实捕获到的NPE消息(数组.length): Cannot read the array length because "<local3>" is null 真实捕获到的NPE消息(方法参数链式调用): Cannot read field "address" because "<parameter1>" is null
每一行都是真实运行这段代码触发异常后得到的真实消息文本。