2009-07-22 99 views
27

我一直想知道为什么JVM不会告诉你哪个指针(或者更确切地说哪个变量)在抛出NullPointerException时为空。为什么Java不告诉你哪个指针是空的?

行号不够具体,因为违规行通常可能包含许多可能导致错误的变量。

是否有任何编译器或JVM标志使这些异常消息更有用?

+9

作为一名专业的Java开发人员,这对于我来说从未成为一个问题。如果在一行中有很多引用可能为空,那么可能是时候将其分解为多行。 – MattC 2009-07-22 22:06:23

+6

@mattc,你从来没有与其他人的图书馆代码工作? – 2009-07-22 22:51:41

+0

@MATC:当你需要在长if-else-if块的中间条件下调用具有多个参数的函数时,经常会出现这种情况。将它分成多行意味着将会在if语句的顶部之前声明一堆虚拟变量。我会说在大多数情况下,这在代码可读性和风格方面会更成问题。 – kpozin 2009-07-23 14:28:05

回答

49

这是因为当没有可用的名称时总是会发生取消引用。该值被加载到操作数堆栈中,然后传递给其中一个解引用它的JRE操作码。但是,操作数堆栈没有与空值关联的名称。它只有'空'。通过一些聪明的运行时跟踪代码,可以派生出一个名称,但这会增加开销和限制值。

因此,没有JRE选项会打开空指针异常的额外信息。

在此示例中,引用存储在本地插槽1中,该插槽映射到本地变量名称。但解除引用发生在invokevirtual指令,该指令只看到堆栈上的“空”值,然后将引发异常:

15 aload_1 
16 invokevirtual #5 

同样有效。将数组负载后跟一个间接引用,但是在这种情况没有名称可以映射到'空'值,只是一个索引而不是另一个值。

76 aload 5 
78 iconst_0 
79 aaload 
80 invokevirtual #5 

不能静态分配的名称给每个指令会 - 这个例子中会产生大量的字节码的,但你可以看到,取消引用指令将接受或objA或objB,你需要跟踪此动态报告是正确的,因为这两个变量流向相同的取消引用指令:

(myflag ? objA : objB).toString() 
12

一旦你JIT的代码,它只是本地指针数学,并且如果本地代码中的任何指针为null它会引发异常。如果将该组件还原为原始变量,并且考虑到JIT将生成的代码优化为不同的级别,这往往不可能发生破坏性的性能影响。

0

如果您将行分成多行而不是在一行上进行多个方法调用,或者如果您在该行上设置断点并通过调试器遍历行,则可以很容易地确定哪些引用非常容易。

0

如果

行号是不够具体 ,因为出错的行往往能 包含可能 造成的错误很多的变化。

话,我建议:

  1. 该行分解成多个行和分配可能NullPointerException - 产生值的临时变量。
  2. 使用调试器并逐步调用每个方法调用,直到找到导致问题的调用者。
0

不幸的是,这只是Java的工作方式。

如果这是“你的”代码,然后简单地分配富之后添加片段喜欢

if (foo == null) { 
    throw new NullPointerException("foo == null"); 
} 

。如果foo是一个参数,那么在方法体的开始处立即检查,然后抛出IllegalArgumentException。

这应该可以帮助你澄清问题。

0

您可以在空指针异常在Eclipse中调试获得异常的具体原因时,添加一个断点。

相关问题