提问者:小点点

什么是NullPointerException,如何修复它?


什么是空指针异常(java.lang.NullPointerException),是什么导致这些异常?

可以使用什么方法/工具来确定原因,以便您停止异常导致程序过早终止?


共3个答案

匿名用户

当您声明一个引用变量(即一个对象)时,您实际上是在创建一个指向一个对象的指针。 请考虑以下代码,其中声明了一个基本类型为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可能尚未初始化”,但有时您可能会编写不直接创建对象的代码。

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

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

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

doSomething(null);

在这种情况下,objnull。 如果该方法旨在对传入的对象执行某些操作,则抛出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
    }
}

最后,如何找出异常&; 使用堆栈跟踪的原因

可以使用什么方法/工具来确定原因,以便您停止异常导致程序过早终止?

带有findbugs的声纳可以探测到NPE。 sonar能动态捕捉JVM引起的空指针异常吗

匿名用户

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

我能想出的用来说明NullPointerException的最快示例代码可能是:

public class Example {

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

}

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

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

匿名用户

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()时,程序停止抛出异常。

跟踪这些值的来源

接下来检查这个值来自哪里。 通过跟随该方法的调用方,我们可以看到sprintString(name)一起在print(name)方法中传入,并且this.name为空。

跟踪应设置这些值的位置

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

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

该变量可以具有默认值(并且setname可以防止将其设置为null):

private String name = "";

PrintPrintString方法可以检查空值,例如:

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)。

相关问题