什么是空指针异常(java.lang.NullPointerException),是什么原因导致的?

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


当前回答

当应用程序试图在需要对象的情况下使用null时,将引发空指针异常。这些包括:

调用空对象的实例方法。 访问或修改空对象的字段。 取null的长度,就好像它是一个数组一样。 访问或修改null的槽位,就像它是一个数组一样。 抛出null,就好像它是一个Throwable值。

应用程序应该抛出该类的实例,以指示null对象的其他非法使用。

参考:http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

其他回答

什么是NullPointerException?

JavaDocs是一个很好的开始。他们涵盖了以下内容:

类的情况下,当应用程序试图使用null时引发 对象是必需的。这些包括: 调用空对象的实例方法。 访问或修改空对象的字段。 取null的长度,就好像它是一个数组一样。 访问或修改null的槽位,就像它是一个数组一样。 抛出null,就好像它是一个Throwable值。 应用程序应该抛出该类的实例来指示其他类 null对象的非法使用。

同样的情况是,如果你试图使用synchronized的空引用,也会抛出这个异常,根据JLS:

SynchronizedStatement: synchronized(表达式)块 否则,如果表达式的值为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方法中)。查看这一行并检查哪些值为空 添加日志语句或使用调试器。我们发现s为空,对它调用length方法会引发异常。我们可以看到,当s.length()从方法中移除时,程序停止抛出异常。

跟踪这些值的来源

接下来检查这个值的来源。通过跟踪该方法的调用者,我们看到s在print()方法中与printString(name)一起传入,this.name为空。

跟踪应该在哪里设置这些值

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

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

其他修复

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

private String name = "";

print或printString方法都可以检查null,例如:

printString((name == null) ? "" : 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();
    }
}

参见:

避免”!= null "语句在Java?

我还是找不到问题

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

另一个NullPointerException发生在声明一个对象数组时,然后立即尝试解引用其中的元素。

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

如果比较顺序颠倒,则可以避免这种特定的NPE;也就是说,在一个有保证的非空对象上使用.equals。

数组中的所有元素都被初始化为它们共同的初始值;对于任何类型的对象数组,这意味着所有元素都是空的。

在访问或取消引用数组中的元素之前,必须初始化它们。

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

在Java中,您声明的所有变量实际上都是对象(或原语)的“引用”,而不是对象本身。

当您尝试执行一个对象方法时,引用会要求活动对象执行该方法。但是如果引用引用的是NULL (nothing, zero, void, nada),那么就没有办法执行方法。然后运行时通过抛出NullPointerException让你知道这一点。

你的引用是“指向”空,因此“空->指针”。

对象存在于VM内存空间中,访问它的唯一方法是使用该引用。举个例子:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

在代码的另一个地方:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

这是一件很重要的事情——当一个对象没有更多的引用时(在上面的例子中,当reference和otherReference都指向null时),那么这个对象是“不可达的”。我们无法使用它,因此该对象已准备好被垃圾收集,在某个时刻,VM将释放该对象使用的内存,并分配另一个内存。

空指针异常表示您正在使用一个对象而没有初始化它。

例如,下面是一个学生类,将在我们的代码中使用它。

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

下面的代码给出了一个空指针异常。

public class School {

    Student student;

    public School() {
        try {
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

因为你用的是student,但你忘了初始化它 正确代码如下所示:

public class School {

    Student student;

    public School() {
        try {
            student = new Student();
            student.setId(12);
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

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

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

public class Example {

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

}

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

(这是一个技术细节,但我认为值得一提:指向null的引用与指向无效内存位置的C指针不同。空指针字面上不指向任何地方,这与指向一个恰好无效的位置有微妙的不同。)