java.lang.NullPointerException为什么不设计成显示null对象的名字或类型?

软件工程师、主攻高级编程语言虚拟机的设计与实现

35 👍 / 13 💬

问题描述

大部分时候java.lang.NullPointerException出现的时候你都不会看到一个错误信息提示你哪个对象是null,给debug带来很多麻烦。为什么这个Exception不设计的友好些呢?比如告诉你null对象的名字或者类型?比如在丢出异常的同时带一个消息:“Object named a is null" 或者"Object named a with type b is null"。
有人会说可以根据异常产生的行数来判断,但是在某些情况下还是麻烦,比如一行代码中有a.getB().getC(), 那么a是null呢还是a.getB()是null呢?
还有一种情况就是在异常封装的情况下,你根本就不知道NPE在哪一行出现的。

首先吐个槽:Java里“对象”没有名字,“引用”才有可能有名字(也可能没名字)。

以前就有好些人给Sun JDK报过bug希望能改善NullPointerException的信息,但全部都被标记为“will not fix”。例如这个bug:

[#JDK-4834738] RFE: NullPointerException: Better info

相同主题的讨论有好多。就拿最近在OpenJDK邮件列表上的说:

JDK-4834738 NullPointerException: Better infoUseful message about NullPointerException

这俩讨论帖,第一帖是去年有人给HotSpot VM提的一个patch,给NullPointerException添加信息,效果是

这样

((String)null).length();

显示:

java.lang.NullPointerException: can not invoke method
java.lang.String.length()I on null object
        at Test.testNPE(Test.java:14)
        at Test.main(Test.java:8)

这跟题主想要的效果已经有点接近。不过提交patch的人只实现了解释器部分。最终这个patch不够好没有被接受。

第二帖就是个基本讨论,跟题主想讨论的内容一样。不过没啥特别有营养的东西在里面…毕竟这个话题已经是月经帖了。

其实与其给出“引用的名字”,最精确而又容易在JVM里实现的是在Java异常对象携带的stack trace信息里带上bci(bytecode index,字节码位置)。本来JVM在抛异常时最先拿到的就是bci,然后才通过查询LineNumberTable将其转换为行号;只要稍微修改JVM和StackTraceElement就可以在stack trace中显示bci。

就算有链式调用 a.x().y() ,x()与y()虽然在同一行上但bci必然不同,通过bci对照字节码很容易就知道到底是哪个解引用遇到了空指针。

有需要的话我可以做个patch…但是许多Java程序员对于 bci 方案并不很感兴趣。归根到底还是懒,懒得去学习Java字节码的知识,也懒得知道一个Java类到底编译成了怎样的字节码。

在另一个问题里回答了如何从bci找回对应的源码:

如何理解ByteCode、IL、汇编等底层语言与上层语言的对应关系? - RednaxelaFX 的回答

有兴趣的同学请耐心读到最后⋯