什么是NullPointerException,如何修复它?

2024-04-23 14:44:26 发布

您现在位置:Python中文网/ 问答频道 /正文

什么是空指针异常(java.lang.NullPointerException)以及什么导致它们?

可以使用哪些方法/工具来确定原因,从而阻止异常导致程序过早终止?


Tags: 工具方法程序lang原因java指针nullpointerexception
3条回答

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

int x;
x = 10;

在本例中,变量x是一个int,Java将为您初始化它为0。当您在第二行给它赋值为10时,您的10值将写入x所引用的内存位置。

但是,当您试图声明引用类型时,会发生一些不同的情况。获取以下代码:

Integer num;
num = new Integer(10);

第一行声明了一个名为num的变量,但它实际上还不包含基元值。相反,它包含一个指针(因为类型是一个引用类型Integer)。由于您还没有指出要指向什么,Java将其设置为null,这意味着“我指向的是nothing”。

在第二行中,new关键字用于实例化(或创建)类型为Integer的对象,指针变量num被分配给该Integer对象。

当您声明一个变量,但在尝试使用该变量的内容之前没有创建对象并分配给该变量时,就会发生NullPointerException(称为解引用)。所以你指的是不存在的东西。

当使用.访问方法或字段,或使用[索引数组时,通常会发生取消引用。

如果在创建对象之前试图取消对num的引用,则会得到NullPointerException。在最简单的情况下,编译器会捕捉到问题并让您知道“num may not have been initialized”,但有时您可能会编写不直接创建对象的代码。

例如,可以使用以下方法:

public void doSomething(SomeObject obj) {
   //do something to obj
}

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

doSomething(null);

在这种情况下,obj就是null。如果该方法旨在对传入对象执行某些操作,则抛出NullPointerException是适当的,因为这是程序员错误,程序员将需要该信息进行调试。请在异常消息中包含对象变量的名称,如

Objects.requireNonNull(a, "a");

或者,在某些情况下,方法的目的不仅仅是操作传入的对象,因此可以接受空参数。在这种情况下,您需要检查一个空参数并以不同的方式操作。您还应该在文档中对此进行解释。例如,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
    }
}

最后,How to pinpoint the exception & cause using Stack Trace

What methods/tools can be used to determine the cause so that you stop the exception from causing the program to terminate prematurely?

带有findbugs的声纳可以检测NPE。 Can sonar catch null pointer exceptions caused by JVM Dynamically

什么是NullPointerException?

一个好的开始是JavaDocs。他们包括:

Thrown when an application attempts to use null in a case where an object is required. These include:

  • Calling the instance method of a null object.
  • Accessing or modifying the field of a null object.
  • Taking the length of null as if it were an array.
  • Accessing or modifying the slots of null as if it were an array.
  • Throwing null as if it were a Throwable value.

Applications should throw instances of this class to indicate other illegal uses of the null object.

如果试图对synchronized使用空引用,也会引发此异常,per the JLS

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Otherwise, if the value of the Expression is null, a NullPointerException is thrown.

我该怎么修?

所以你有一个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方法中)抛出了异常。查看行并检查哪些值为空 添加日志语句或使用调试器。我们发现s为空,对其调用length方法会引发异常。我们可以看到,当s.length()从方法中移除时,程序停止抛出异常。

跟踪这些值的来源

下一步检查该值的来源。通过跟踪方法的调用方,我们可以看到sprint()方法中与printString(name)一起传入,并且this.name为空。

跟踪应设置这些值的位置

this.name集合在哪里?在setName(String)方法中。通过更多的调试,我们可以看到这个方法根本没有被调用。如果调用了方法,请确保检查调用这些方法的顺序,并且在打印方法之后不调用set方法

这足以给我们一个解决方案:在调用printer.print()之前添加对printer.setName()的调用。

其他修复

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

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

另请参见:

我还是找不到问题

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

NullPointerException是当您试图使用一个指向内存中任何位置(空)的引用时发生的异常,就像它在引用一个对象一样。对空引用调用方法或尝试访问空引用的字段将触发NullPointerException。这些是最常见的,但是其他方式列在^{}javadoc页面上。

可能我能想到的最快的示例代码是:

public class Example {

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

}

main内的第一行,我显式地将Object引用obj设置为null。这意味着我有一个引用,但它没有指向任何对象。之后,我试图通过调用对象上的方法来将引用视为指向该对象。这将导致一个NullPointerException,因为在引用所指向的位置没有要执行的代码。

(这是一个技术性问题,但我认为值得一提:指向null的引用与指向无效内存位置的C指针不同。空指针实际上不指向任何地方,这与指向碰巧无效的位置有细微的不同。)

相关问题 更多 >