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

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


当前回答

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指针不同。空指针字面上不指向任何地方,这与指向一个恰好无效的位置有微妙的不同。)

其他回答

什么是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)。

这就像你试图访问一个为空的对象。考虑下面的例子:

TypeA objA;

此时,您刚刚声明了该对象,但尚未初始化或实例化。无论何时你试图访问其中的任何属性或方法,它都会抛出NullPointerException,这是有意义的。

请看下面的例子:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown

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

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

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

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

在Java中有两种主要类型的变量:

Primitives: variables that contain data. If you want to manipulate the data in a primitive variable you can manipulate that variable directly. By convention primitive types start with a lowercase letter. For example variables of type int or char are primitives. References: variables that contain the memory address of an Object i.e. variables that refer to an Object. If you want to manipulate the Object that a reference variable refers to you must dereference it. Dereferencing usually entails using . to access a method or field, or using [ to index an array. By convention reference types are usually denoted with a type that starts in uppercase. For example variables of type Object are references.

考虑下面的代码,其中你声明了一个基本类型为int的变量,并且没有初始化它:

int x;
int y = x + x;

这两行会使程序崩溃,因为没有为x指定值,而我们正试图使用x的值来指定y。所有原语在被操作之前都必须初始化为可用的值。

现在事情变得有趣了。引用变量可以设置为null,这意味着“我没有引用任何东西”。如果您以这种方式显式地设置引用变量,或者引用变量未初始化且编译器没有捕获它,则可以在引用变量中获得空值(Java将自动将变量设置为空)。

如果一个引用变量被您显式地设置为null,或者通过Java自动设置为null,并且您试图解除对它的引用,则会得到一个NullPointerException。

NullPointerException (NPE)通常发生在声明一个变量,但在尝试使用变量的内容之前没有创建对象并将其赋值给变量的情况下。所以你得到了一个并不存在的东西。

取以下代码:

Integer num;
num = new Integer(10);

第一行声明了一个名为num的变量,但它实际上还没有包含一个引用值。因为您还没有说指向什么,所以Java将其设置为null。

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

如果你试图在创建对象之前解除对num的引用,你会得到一个NullPointerException。在大多数情况下,编译器会发现问题并告诉您“num可能没有初始化”,但有时您可能会编写不直接创建对象的代码。

例如,你可能有这样一个方法:

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

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

doSomething(null);

在这种情况下,obj为空,语句obj. mymethod()将抛出NullPointerException。

如果该方法打算像上面的方法那样对传入的对象做一些事情,那么抛出NullPointerException是合适的,因为这是一个程序员错误,程序员将需要该信息用于调试。

除了由方法逻辑引发的nullpointerexception异常,你还可以检查方法参数是否为空值,并通过在方法开头附近添加如下内容显式抛出npe:

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

注意,在错误消息中清楚地说明哪个对象不能为空是很有帮助的。验证这一点的好处是:1)您可以返回自己更清晰的错误消息;2)对于方法的其余部分,您知道除非obj被重新赋值,否则它不是空的,可以安全地解除引用。

或者,在某些情况下,方法的目的不仅仅是对传入的对象进行操作,因此空参数可能是可以接受的。在这种情况下,您将需要检查空参数并采取不同的行为。您还应该在文档中对此进行解释。例如,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。 声纳能捕捉JVM动态引起的空指针异常吗

现在Java 14添加了一个新的语言特性来显示NullPointerException的根本原因。该语言特性自2006年以来一直是SAP商业JVM的一部分。

在Java 14中,下面是一个示例NullPointerException异常消息:

java.lang.NullPointerException:不能调用"java.util.List.size()",因为"list"是空的

导致NullPointerException发生的情况列表

以下是Java语言规范中直接提到的所有NullPointerException发生的情况:

Accessing (i.e. getting or setting) an instance field of a null reference. (static fields don't count!) Calling an instance method of a null reference. (static methods don't count!) throw null; Accessing elements of a null array. Synchronising on null - synchronized (someNullReference) { ... } Any integer/floating point operator can throw a NullPointerException if one of its operands is a boxed null reference An unboxing conversion throws a NullPointerException if the boxed value is null. Calling super on a null reference throws a NullPointerException. If you are confused, this is talking about qualified superclass constructor invocations:

class Outer {
    class Inner {}
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner(Outer o) { 
        o.super(); // if o is null, NPE gets thrown
    }
}

Using a for (element : iterable) loop to loop through a null collection/array. switch (foo) { ... } (whether its an expression or statement) can throw a NullPointerException when foo is null. foo.new SomeInnerClass() throws a NullPointerException when foo is null. Method references of the form name1::name2 or primaryExpression::name throws a NullPointerException when evaluated when name1 or primaryExpression evaluates to null. a note from the JLS here says that, someInstance.someStaticMethod() doesn't throw an NPE, because someStaticMethod is static, but someInstance::someStaticMethod still throw an NPE!

*请注意,JLS可能也间接地说明了很多npe。

在Java中,所有东西(不包括基本类型)都是类的形式。

如果你想使用任何对象,那么你有两个阶段:

声明 初始化

例子:

声明:Object对象; 初始化:object = new object ();

数组的概念也一样:

声明:Item Item[] = new Item[5]; 初始化:item[0] = new item ();

如果你没有给出初始化部分,那么会出现NullPointerException。