什么是nullpointerexception,如何修复它?

xmq68pz9  于 2021-08-25  发布在  Java
关注(0)|答案(4)|浏览(286)

这个问题的答案是社区的努力。编辑现有答案以改进此帖子。它目前不接受新的答案或互动。

什么是空指针异常( java.lang.NullPointerException )是什么导致了这些问题?
可以使用哪些方法/工具来确定原因,从而阻止异常导致程序过早终止?

bksxznpy

bksxznpy1#

声明引用变量(即对象)时,实际上是在创建指向对象的指针。考虑下面的代码,其中声明原始类型的变量 int :

int x;
x = 10;

在本例中,变量 x 是一个 int java会将其初始化为 0 为你。当您指定 10 在第二行,你的价值 10 被写入所引用的内存位置 x .
但是,当您试图声明引用类型时,会发生一些不同的情况。以下面的代码为例:

Integer num;
num = new Integer(10);

第一行声明了一个名为 num ,但它实际上还不包含基元值。相反,它包含一个指针(因为类型为 Integer 这是一种引用类型)。由于您还没有说要指向什么,java将其设置为 null ,意思是“我没有指向任何东西”。
在第二行中 new 关键字用于示例化(或创建)类型的对象 Integer ,以及指针变量 num 分配给那个 Integer 对象
这个 NullPointerException (npe)发生在声明变量但未创建对象并在尝试使用变量内容(称为取消引用)之前将其分配给变量时。所以你指的是一些实际上并不存在的东西。
取消引用通常发生在使用 . 访问方法或字段,或使用 [ 为数组编制索引。
如果您试图取消引用 num 在创建对象之前,您会得到一个 NullPointerException . 在最简单的情况下,编译器会发现问题并让您知道“ num may not have been initialized ,但有时您可能会编写不直接创建对象的代码。
例如,您可能有如下方法:

public void doSomething(SomeObject obj) {
   // Do something to obj, assumes obj is not null
   obj.myMethod();
}

在这种情况下,您不是在创建对象 obj ,而是假设它是在 doSomething() 方法被调用。注意,可以这样调用该方法:

doSomething(null);

那么,, objnull ,以及声明 obj.myMethod() 将抛出一个 NullPointerException .
如果该方法打算像上述方法那样对传入对象执行某些操作,则可以抛出 NullPointerException 因为这是程序员的错误,程序员需要这些信息进行调试。
除了 NullPointerException 如果由于方法的逻辑而抛出,您还可以检查方法参数的 null 值并通过在方法开头附近添加类似以下内容显式抛出NPE:

// Throws an NPE with a custom error message if obj is null
Objects.requireNonNull(obj, "obj must not be null");

请注意,在错误消息中清楚地指出哪个对象不能被删除是很有帮助的 null . 验证这一点的好处是1)您可以返回自己更清晰的错误消息,2)对于您知道的方法的其余部分,除非 obj 是重新分配的,它不是空的,可以安全地取消引用。
或者,在某些情况下,方法的目的不仅仅是对传入的对象进行操作,因此可以接受null参数。在这种情况下,您需要检查null参数并采取不同的行为。您还应该在文档中对此进行解释。例如 doSomething() 可以写成:

/**
  * @param obj An optional foo for ____. May be null, in which case
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj == null) {
       // Do something
    } else {
       // Do something else
    }
}

最后,如何使用堆栈跟踪查明异常和原因
可以使用哪些方法/工具来确定原因,从而阻止异常导致程序过早终止?
带有“发现虫子”的声纳可以探测npe。sonar能否动态捕获jvm引起的空指针异常
现在Java14添加了一个新的语言特性来显示nullpointerexception的根本原因。自2006年以来,此语言功能一直是sap commercial jvm的一部分。
在java 14中,以下是nullpointerexception异常消息示例:
线程“main”中的java.lang.nullpointerexception:无法调用“java.util.list.size()”,因为“list”为null

j0pj023g

j0pj023g2#

NullPointerException 当您试图使用一个指向内存中任何位置(null)的引用,就好像它在引用一个对象一样时,会出现异常。对空引用调用方法或尝试访问空引用的字段将触发 NullPointerException . 这些是最常见的,但其他方法也列在 NullPointerException javadoc页面。
可能是我能想出的最快的示例代码来说明 NullPointerException 将是:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

在里面的第一行 main ,我显式地设置 Object 参考文献 obj 等于 null . 这意味着我有一个引用,但它没有指向任何对象。之后,我尝试通过调用对象上的方法,将引用视为指向对象。这导致了 NullPointerException 因为在引用所指向的位置没有要执行的代码。
(这是一个技术问题,但我认为值得一提:指向null的引用与指向无效内存位置的c指针不同。空指针实际上不指向任何地方,这与指向碰巧无效的位置有细微的区别。)

u4vypkhs

u4vypkhs3#

什么是nullpointerexception?

javadocs是一个很好的起点。它们包括:
当应用程序在需要对象的情况下尝试使用null时引发。这些措施包括:
调用空对象的示例方法。
访问或修改空对象的字段。
将null的长度视为数组。
像数组一样访问或修改null的插槽。
将null作为可丢弃的值抛出。
应用程序应该抛出此类的示例,以指示null对象的其他非法使用。
如果您尝试将空引用用于 synchronized ,这也将引发此异常,根据jls:

SynchronizedStatement:
    synchronized ( Expression ) Block

否则,如果表达式的值为null,则 NullPointerException 被抛出。

我怎么修理它?

所以你有一个 NullPointerException . 你怎么修理它?让我们举一个简单的例子 NullPointerException :

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

识别空值
第一步是准确识别导致异常的值。为此,我们需要进行一些调试。学习阅读stacktrace很重要。这将显示引发异常的位置:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

在这里,我们看到异常被抛出到第13行(在 printString 方法)。查看该行,通过添加日志语句或使用调试器检查哪些值为null。我们发现 s 为null,并调用 length 方法引发异常。我们可以看到,当 s.length() 从方法中删除。
跟踪这些值的来源
下一步检查该值的来源。通过跟踪方法的调用者,我们可以看到 s 与…一起传递 printString(name)print() 方法,以及 this.name 是空的。
跟踪应设置这些值的位置
在哪里 this.name 设置在 setName(String) 方法。通过更多的调试,我们可以看到这个方法根本没有被调用。如果调用了该方法,请确保检查调用这些方法的顺序,并且在print方法之后不调用set方法。
这足以给我们提供一个解决方案:向 printer.setName() 打电话之前 printer.print() .

其他修复

变量可以有一个默认值(和 setName 可以防止将其设置为null):

private String name = "";

要么 printprintString 方法可以检查null,例如:

printString((name == null) ? "" : name);

或者您可以设计类,以便 name 始终具有非空值:

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

另见:
避免“!=java中的“null”语句?

我还是找不到问题

如果您试图调试问题,但仍然没有解决方案,您可以发布一个问题以获得更多帮助,但请确保包含您迄今为止尝试过的内容。至少在问题中包含stacktrace,并在代码中标记重要行号。此外,请先尝试简化代码(请参见sscce)。

8ftvxx2r

8ftvxx2r4#

问题:是什么导致nullpointerexception(npe)?

您应该知道,java类型分为基本类型( boolean , int 等)和参考类型。java中的引用类型允许您使用特殊值 null 这是java表示“无对象”的方式。
A. NullPointerException 每当程序尝试使用 null 好像这是一个真正的参考。例如,如果您编写以下内容:

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

标记为“here”的语句将尝试运行 length() 方法 null 引用,这将抛出一个 NullPointerException .
有很多方法可以使用 null 值,该值将导致 NullPointerException . 事实上,你唯一能做的就是 null 在不引起npe的情况下:
将其分配给参考变量或从参考变量读取,
将其分配给数组元素或从数组元素中读取(前提是数组引用本身非空!),
将其作为参数传递或作为结果返回,或
使用 ==!= 操作员,或 instanceof .

问题:我如何阅读npe stacktrace?

假设我编译并运行上面的程序:

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:4)
$

第一个观察:编译成功!程序中的问题不是编译错误。这是一个运行时错误(某些IDE可能会警告您的程序将始终引发异常。。。但是标准 javac 编译器不支持。)
第二个观察:当我运行程序时,它输出两行“gobbledy-gook”。错!!那不是狼吞虎咽的东西。这是一个堆栈跟踪。。。它提供了重要的信息,如果您花时间仔细阅读,这些信息将帮助您跟踪代码中的错误。
让我们看一看

相关问题