什么是nullpointerexception,如何修复它?

js5cn81o  于 2021-06-29  发布在  Java
关注(0)|答案(4)|浏览(178)

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

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

gblwokeq

gblwokeq1#

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

您应该知道,java类型分为基本类型( boolean , int 等)和参考类型。java中的引用类型允许您使用特殊值 null 这是java中表示“无对象”的方式。
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的有:
将其赋给参考变量或从参考变量读取,
将其分配给数组元素或从数组元素中读取(前提是数组引用本身不为null!),
将其作为参数传递或作为结果返回,或者
使用 == 或者 != 操作员,或 instanceof .

问题:如何阅读npe stacktrace?

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

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

第一个观察:编译成功!程序中的问题不是编译错误。这是一个运行时错误(有些IDE可能会警告您的程序总是抛出异常。。。但是标准 javac 编译器不支持。)
第二个观察:当我运行程序时,它会输出两行“gobbledygook”。错!!那可不是胡说八道。这是一个stacktrace。。。它提供了重要的信息

rt4zxlrg

rt4zxlrg2#

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指针不同。空指针实际上不指向任何地方,这与指向恰好无效的位置有细微的区别。)

m0rkklqb

m0rkklqb3#

什么是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();
    }
}

标识空值
第一步是准确地确定哪些值导致异常。为此,我们需要进行一些调试。学会读一本书很重要。这将显示引发异常的位置:

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) 方法。通过更多的调试,我们可以看到这个方法根本没有被调用。如果调用了该方法,请确保检查调用这些方法的顺序,并且set方法不会在print方法之后调用。
这足以给我们提供一个解决方案:向 printer.setName() 打电话之前 printer.print() .

其他修复程序

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

private String name = "";

要么 print 或者 printString 方法可以检查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中的“空”语句?

我还是找不到问题

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

tv6aics1

tv6aics14#

声明引用变量(即对象)时,实际上是在创建指向对象的指针。考虑下面的代码,其中声明了一个基元类型的变量 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 在声明变量但未创建对象并在尝试使用变量内容(称为取消引用)之前将其赋给变量时发生。所以你指的是一些实际上并不存在的东西。
解引用通常发生在使用 . 访问方法或字段,或使用 [ 索引数组。
如果你试图取消引用 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参数并采取不同的行为。您还应该在文档中对此进行解释。例如, 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商业jvm的一部分。以下是2分钟的阅读,以了解这一惊人的语言功能。
https://jfeatures.com/blog/nullpointerexception
在java 14中,以下是nullpointerexception异常消息示例:
在线程“main”java.lang.nullpointerexception中:无法调用“java.util.list.size()”,因为“list”为null

相关问题