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

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


当前回答

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

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

声明 初始化

例子:

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

数组的概念也一样:

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

如果你没有给出初始化部分,那么会出现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指针不同。空指针字面上不指向任何地方,这与指向一个恰好无效的位置有微妙的不同。)

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

TypeA objA;

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

请看下面的例子:

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

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

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

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

在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中,您声明的所有变量实际上都是对象(或原语)的“引用”,而不是对象本身。

当您尝试执行一个对象方法时,引用会要求活动对象执行该方法。但是如果引用引用的是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将释放该对象使用的内存,并分配另一个内存。