什么是 NullPointerException,如何修复它?

2022-08-31 06:06:35

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

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


答案 1

Java 中有两种总体类型的变量:

  1. 基元:包含数据的变量。如果要操作基元变量中的数据,可以直接操作该变量。按照惯例,基元类型以小写字母开头。例如,类型变量或基元变量。intchar

  2. 引用:包含内存地址的变量,即引用 .如果要操作引用变量所引用的 ,则必须取消引用它。取消引用通常需要 使用 来访问方法或字段,或者使用 来索引数组。按照惯例,引用类型通常用以大写开头的类型来表示。例如,类型的变量是引用。ObjectObjectObject.[Object

请考虑以下代码,其中声明基元类型的变量并且不对其进行初始化:int

int x;
int y = x + x;

这两行将使程序崩溃,因为没有指定值,并且我们正在尝试使用 的值来指定 。在操作所有基元之前,必须将其初始化为可用值。xxy

现在,事情变得有趣了。可以将引用变量设置为“我没有引用任何内容”。如果以这种方式显式设置引用变量,则可以在引用变量中获取值,或者引用变量未初始化并且编译器未捕获它(Java 会自动将该变量设置为 )。nullnullnull

如果引用变量由您显式或通过 Java 自动设置为 null,并且您尝试取消引用它,则会得到一个 .NullPointerException

(NPE) 通常发生在您声明变量但在尝试使用变量的内容之前未创建对象并将其分配给变量时。所以你有一个引用实际上不存在的东西。NullPointerException

取以下代码:

Integer num;
num = new Integer(10);

第一行声明了一个名为 的变量,但它实际上还不包含一个引用值。由于您尚未说出要指向的内容,因此 Java 将其设置为 。numnull

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

如果在创建对象之前尝试取消引用,则会得到一个 .在最微不足道的情况下,编译器会捕获问题并让您知道“,”,但有时您可能会编写不直接创建对象的代码。numNullPointerExceptionnum may not have been initialized

例如,您可能有如下方法:

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

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

doSomething(null);

在这种情况下,is 是 ,并且语句将抛出一个 .objnullobj.myMethod()NullPointerException

如果该方法旨在像上述方法那样对传入的对象执行某些操作,则最好抛出 ,因为它是程序员错误,程序员将需要该信息进行调试。NullPointerException

除了作为方法逻辑的结果而引发的 s 之外,还可以检查方法参数的值,并通过在方法开头附近添加类似以下内容的内容来显式抛出 NPE:NullPointerExceptionnull

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

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

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

最后,如何使用 Stack Trace 查明异常和原因

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

带有查找错误的声纳可以检测NPE。声纳能否动态捕获由 JVM 引起的空指针异常

现在Java 14添加了一个新的语言功能,以显示NullPointerException的根本原因。自 2006 年以来,此语言功能一直是 SAP 商用 JVM 的一部分。

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

在线程 “main” java.lang.NullPointerException 中:无法调用 “java.util.List.size()”,因为 “list” 为 null


答案 2

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

也许我能想出的最快的示例代码来说明一个是:NullPointerException

public class Example {

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

}

在里面的第一行,我显式地将引用设置为等于 。这意味着我有一个引用,但它没有指向任何对象。之后,我尝试通过调用对象的方法来对待引用,就好像它指向一个对象一样。这会导致 ,因为在引用所指向的位置中没有要执行的代码。mainObjectobjnullNullPointerException

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


推荐