什么是 NullPointerException,我该如何解决?

新手上路,请多包涵

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

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

原文由 Ziggy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

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

  1. Primitives :包含数据的变量。如果要操作原始变量中的数据,可以直接操作该变量。按照约定,原始类型以小写字母开头。例如 intchar 类型的变量是原语。

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

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

 int x;
int y = x + x;

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

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

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

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

采取以下代码:

 Integer num;
num = new Integer(10);

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

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

如果您在创建对象 之前 尝试取消引用 num 您会得到一个 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);

在这种情况下, objnull ,并且语句 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");

请注意,在错误消息中清楚地说明 哪个 对象不能是 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
    }
}

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

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

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

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

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

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

导致 NullPointerException 发生的情况列表

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

  • 访问(即获取或设置)空引用的 实例 字段。 (静态字段不算!)
  • 调用空引用的 实例 方法。 (静态方法不算!)
  • throw null;
  • 访问空数组的元素。
  • 同步空 - synchronized (someNullReference) { ... }
  • 任何整数/浮点运算符都可以抛出 NullPointerException 如果其操作数之一是盒装空引用
  • 如果装箱值为空,则拆箱转换会引发 NullPointerException
  • 在空引用上调用 super 会引发 NullPointerException 。如果您感到困惑,这是在谈论合格的超类构造函数调用:
 class Outer {
    class Inner {}
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner(Outer o) {
        o.super(); // if o is null, NPE gets thrown
    }
}

  • 使用 for (element : iterable) 循环遍历空集合/数组。

  • switch (foo) { ... } (无论是表达式还是语句)可以在 foo 为空时抛出 NullPointerException —。

  • foo.new SomeInnerClass() NullPointerExceptionfoo 为空时抛出 —。

  • Method references of the form name1::name2 or primaryExpression::name throws a NullPointerException when evaluated when name1 or primaryExpression evaluates to null .

这里 JLS 的一条注释说, someInstance.someStaticMethod() 不会抛出 NPE,因为 someStaticMethod 是静态的,但是 someInstance::someStaticMethod 仍然抛出 NPE!

\* 请注意,JLS 可能也 间接 提到了很多关于 NPE 的内容。

原文由 Vincent Ramdhanie 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

 public class Example {

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

}

On the first line inside main , I’m explicitly setting the Object reference obj equal to null .这意味着我有一个引用,但它没有指向任何对象。之后,我尝试通过在其上调用方法来将引用视为指向对象。这导致 NullPointerException 因为在引用指向的位置没有要执行的代码。

(这是一个技术问题,但我认为值得一提:指向 null 的引用与指向无效内存位置的 C 指针不同。空指针实际上不指向 _任何地方_,这与指向一个恰好无效的位置。)

原文由 Bill the Lizard 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题