什么是 NullPointerException,如何修复它?

2024-04-30

什么是空指针异常(java.lang.NullPointerException)以及是什么导致了它们?

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


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

  1. 基元:包含数据的变量。如果您想操作原始变量中的数据,您可以直接操作该变量。按照惯例,原始类型以小写字母开头。例如类型变量int or char是原语。

  2. 参考:包含内存地址的变量Object即变量refer to an Object。如果你想操纵Object引用变量引用你必须解引用它。取消引用通常需要使用.访问方法或字段,或使用[为数组建立索引。按照惯例,引用类型通常用以大写字母开头的类型来表示。例如类型变量Object是参考。

考虑以下代码,其中声明了一个变量原始 type int并且不初始化它:

int x;
int y = x + x;

这两行将使程序崩溃,因为没有指定任何值x我们正在尝试使用x指定的值y。所有基元在被操作之前都必须初始化为可用值。

现在事情变得有趣了。参考变量可以设置为null意思是 ”我正在参考nothing”。你可以获得一个null如果您以这种方式显式设置引用变量中的值,或者引用变量未初始化并且编译器不会捕获它(Java 会自动将该变量设置为null).

如果您显式或通过 Java 自动将引用变量设置为 null,并且您尝试解引用它你得到一个NullPointerException.

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

采取以下代码:

Integer num;
num = new Integer(10);

第一行声明一个变量,名为num,但实际上还不包含参考价值。由于您还没有说出要指向什么,Java 将其设置为null.

在第二行中,new关键字用于实例化(或创建)类型的对象Integer,和参考变量num被分配给那个Integer object.

如果您尝试取消引用num before创建你得到的对象NullPointerException。在最简单的情况下,编译器会捕获问题并让您知道“num may not have been initialized,”但有时您可能编写不直接创建对象的代码。

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

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

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

doSomething(null);

在这种情况下,obj is null,以及声明obj.myMethod()会抛出一个NullPointerException.

如果该方法打算像上面的方法那样对传入的对象执行某些操作,则适合抛出NullPointerException因为这是程序员的错误,程序员需要该信息来进行调试。

此外NullPointerException作为方法逻辑的结果抛出,您还可以检查方法参数null值并通过在方法开头附近添加类似以下内容来显式抛出 NPE:

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

请注意,在错误消息中清楚地说明会很有帮助which对象不能是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
    }
}

最后,如何使用堆栈跟踪查明异常和原因 https://stackoverflow.com/q/3988788/2775450

可以使用哪些方法/工具来确定原因以便您停止 导致程序提前终止的异常?

具有 find bug 功能的声纳可以检测 NPE。sonar能否动态捕获JVM引起的空指针异常 https://stackoverflow.com/questions/20899931/can-sonar-catch-null-pointer-exceptions-caused-by-jvm-dynamically

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

在 Java 14 中,以下是 NullPointerException 异常消息示例:

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

导致的情况列表NullPointerException发生

以下是所有情况NullPointerExceptionJava 语言规范直接*提到的情况:

  • 访问(即获取或设置)instance空引用的字段。 (静态字段不算数!)
  • 呼叫instance空引用的方法。 (静态方法不算!)
  • throw null;
  • 访问空数组的元素。
  • 空同步 -synchronized (someNullReference) { ... }
  • 任何整数/浮点运算符都可以抛出NullPointerException如果其操作数之一是装箱空引用
  • 拆箱转换会抛出NullPointerException如果装箱值为空。
  • Calling super在空引用上抛出NullPointerException。如果您感到困惑,这是在谈论合格的超类构造函数调用:
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)循环遍历空集合/数组。

  • switch (foo) { ... }(无论是表达式还是语句)可以抛出NullPointerException when foo一片空白。

  • foo.new SomeInnerClass()抛出一个NullPointerException when foo一片空白。

  • 表格方法参考name1::name2 or primaryExpression::name抛出一个NullPointerException当评估时name1 or primaryExpression计算结果为空。

    JLS 的注释中说,someInstance.someStaticMethod()不会抛出 NPE,因为someStaticMethod是静态的,但是someInstance::someStaticMethod还是扔NPE吧!

* Note that the JLS probably also says a lot about NPEs indirectly.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是 NullPointerException,如何修复它? 的相关文章

随机推荐