我一直想知道为什么当抛出NullPointerException
时,JVM没有告诉你哪个指针(或更确切地说,哪个变量)为空。
行号不够具体,因为违规行通常可能包含许多可能导致错误的变量。
是否有任何编译器或JVM标志可以使这些异常消息更有用?
答案 0 :(得分:49)
这是因为当没有可用的名称时,始终会发生取消引用。该值将加载到操作数堆栈上,然后传递给解除引用的JRE操作码之一。但是,操作数堆栈没有与空值关联的名称。它只是'null'。使用一些聪明的运行时跟踪代码,可以派生一个名称,但这会增加有限值的开销。
因此,没有JRE选项可以打开空指针异常的额外信息。
在此示例中,引用存储在本地插槽1中,该插槽映射到本地变量名称。但是取消引用发生在invokevirtual指令中,它只在堆栈上看到'null'值,然后抛出异常:
15 aload_1
16 invokevirtual #5
同样有效的是数组加载后跟一个取消引用,但在这种情况下,没有名称可以映射到'null'值,只是一个索引偏离另一个值。
76 aload 5
78 iconst_0
79 aaload
80 invokevirtual #5
你不能静态地为每条指令分配名称 - 这个例子产生了很多字节码,但你可以看到解除引用指令将接收到objA或objB,你需要动态地跟踪它以报告右边一个,因为两个变量都流向相同的解引用指令:
(myflag ? objA : objB).toString()
答案 1 :(得分:12)
一旦你JIT代码,它只是本机指针数学,如果本机代码中的任何指针为null,它会抛出异常。将组件反转回原始变量会产生破坏性的性能影响,并且考虑到JIT将生成的代码优化到不同的级别,通常甚至都不可能。
答案 2 :(得分:1)
自Java 15起,它终于可以告诉您!请参见此JEP:https://openjdk.java.net/jeps/358(实际上是在Java 14中添加的,但默认情况下已禁用,现在在Java 15中默认情况下已启用)。
答案 3 :(得分:0)
如果将行分成多行而不是在一行上进行多次方法调用,或者如果在该行上设置断点并使用调试器逐步执行该行,则可以非常容易地确定哪个引用为null。
答案 4 :(得分:0)
如果
行号不够具体 因为违规行可以经常 包含许多可能的变量 导致错误。
然后我建议:
NullPointerException
- 生成值分配给临时变量。答案 5 :(得分:0)
遗憾的是,这正是Java的工作方式。
如果这是“你的”代码,那么只需添加像
这样的代码段if (foo == null) {
throw new NullPointerException("foo == null");
}
分配foo后立即。如果foo是一个参数,那么立即在方法体的开头检查,并改为抛出IllegalArgumentException。
这可以帮助你澄清问题。
答案 6 :(得分:0)
在调试时,可以在Eclipse中的空指针异常上添加断点,以获得异常的确切原因。