Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么';Java告诉你哪个指针是空的吗?_Java_Debugging_Nullpointerexception - Fatal编程技术网

为什么';Java告诉你哪个指针是空的吗?

为什么';Java告诉你哪个指针是空的吗?,java,debugging,nullpointerexception,Java,Debugging,Nullpointerexception,我一直想知道,当抛出NullPointerException时,为什么JVM不告诉您哪个指针(或者更准确地说,哪个变量)为null 行号不够具体,因为有问题的行通常包含许多可能导致错误的变量 是否有任何编译器或JVM标志可以使这些异常消息更有用?一旦您对代码进行JIT处理,它就是本机指针数学,如果本机代码中的任何指针为null,它将抛出异常。将程序集还原为原始变量会对性能产生毁灭性的影响,而且考虑到JIT会将生成的代码优化到不同的级别,这通常是不可能的。如果您将该行拆分为多行,而不是在一行上进行

我一直想知道,当抛出
NullPointerException
时,为什么JVM不告诉您哪个指针(或者更准确地说,哪个变量)为null

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


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

一旦您对代码进行JIT处理,它就是本机指针数学,如果本机代码中的任何指针为null,它将抛出异常。将程序集还原为原始变量会对性能产生毁灭性的影响,而且考虑到JIT会将生成的代码优化到不同的级别,这通常是不可能的。

如果您将该行拆分为多行,而不是在一行上进行多个方法调用,或者,如果您在该行上设置断点并使用调试器逐步完成该行,则可以非常容易地确定哪个引用为null。

行号不够具体 因为冒犯的界线经常会 包含许多可以 造成错误的原因

那么,我建议:

  • 将该行拆分为多行,并将可能的
    NullPointerException
    -生成值分配给临时变量
  • 使用调试器并逐步检查每个方法调用,直到找到导致问题的方法

  • 不幸的是,这正是Java的工作方式

    如果这是“您的”代码,那么只需添加如下代码片段

    if (foo == null) {
      throw new NullPointerException("foo == null");
    }
    
    就在分配foo之后。如果foo是一个参数,那么立即在方法体的开头进行检查,并抛出IllegalArgumentException


    这将有助于您澄清问题。

    这是因为取消引用总是在没有可用名称时发生。该值被加载到操作数堆栈上,然后传递给对其进行解引用的一个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()
    


    调试时,您可以在Eclipse中的空指针异常上添加断点,以获得异常的确切原因。

    因为Java 15,它最终会告诉您!请参阅此JEP:(它实际上是在Java14中添加的,但在默认情况下被禁用,现在在Java15中被默认启用)。

    @Michael:JIT在本机代码生成过程中跟踪行号。它的跟踪分辨率不高于此分辨率(子表达式等)。@Michael:抱歉,我是从CLI JIT发言的:请参阅此处“默认”成员的描述:@Michael,early Sun JITs丢失了行号信息。Hotspot甚至可以为jitted代码显示它。这个小线程为我澄清了它,所以我添加了我的答案。例如,当使用?:运算符时,跟踪子表达式无论如何也不能解决问题。在对代码进行JIT后,许多信息仍然可用,如行号。我认为这种解释还停留在一半,这对我作为一名专业Java开发人员的8年多时间里从来都不是一个问题。如果一行中有很多引用可能为空,那么可能是时候将其拆分为多行了。@mattc,您从未使用过其他人的库代码?@mattc:当您需要在一个长If-else-If块中间的条件下调用具有多个参数的函数时,通常会出现这种情况。将其拆分为多行意味着在
    if
    的顶部之前向上并声明一组虚拟变量。我想说,在大多数情况下,这在代码可读性和风格方面会有更大的问题。我就同一个问题问了一个类似的“如何调试”问题,但最终说服我以一种不会抛出NPE的方式编写代码。请参阅+1以获得实际解释,而不是“如果您以“正确的方式”编写代码,则不会发生这种情况”。不过,JVM应该能够告诉我们是哪条指令导致了NPE,我认为这仍然非常有用,例如,如果表达式“a.b.c.d.e.f”NPE中的结果。我认为这里的
    assert
    语句比
    if
    语句更可取。我会谋杀任何使用类似代码进行代码审查的人。@Thorbjorn我相信用户代码使用NullPointerException是不受欢迎的。“这是由JRE抛出的。”迈克尔,我相信这仍然是有争议的。就我个人而言,我试图编写代码以避免永远返回null(对我来说是null对象),我可能只会抛出一个RuntimeException。在这个特定的设置中,我试图在提供更多信息的同时给出保留当前行为的建议(其他异常可能被捕获)。@MichaelDonohue没有。JDK中充满了故意抛出NPE的代码,例如任何合理编写的
    AddListNet()
    方法,在这种情况下,非常需要在源代码处捕获错误,而不是让它在可能数小时后在其他线程中以其他方法跳闸。