首页  >  问答  >  正文

什么是 NullPointerException,如何修复它?

<p>什么是空指针异常 (<code>java.lang.NullPointerException</code>) 以及导致它们的原因是什么?</p> <p>可以使用哪些方法/工具来确定原因,以便阻止异常导致程序提前终止?</p>
P粉649990273P粉649990273393 天前546

全部回复(2)我来回复

  • P粉252116587

    P粉2521165872023-08-24 09:14:35

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

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

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

    main 内的第一行,我显式地将 Object 引用 obj 设置为 null 。这意味着我有一个引用,但它没有指向任何对象。之后,我尝试通过调用该引用的方法来将该引用视为指向一个对象。这会导致 NullPointerException,因为在引用指向的位置没有要执行的代码。

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

    回复
    0
  • P粉593536104

    P粉5935361042023-08-24 00:10:48

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

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

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

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

    int x;
    int y = x + x;
    

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

    现在事情变得有趣了。 引用变量可以设置为null,这意味着“我没有引用任何东西”。如果您以这种方式显式设置引用变量,则可以在引用变量中获取 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);

    在这种情况下,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 ,否则它不为 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
        }
    }

    最后,如何使用堆栈跟踪查明异常和原因

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

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

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

    导致 NullPointerException 发生的情况列表

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

    • 访问(即获取或设置)空引用的实例字段。 (静态字段不算数!)
    • 调用空引用的实例方法。 (静态方法不算!)
    • 抛出空值;
    • 访问空数组的元素。
    • 在 null 上同步 - synchronized (someNullReference) { ... }
    • 如果其操作数之一是装箱空引用,则任何整数/浮点运算符都可能抛出 NullPointerException
    • 如果装箱值为 null,则拆箱转换会引发 NullPointerException
    • 对 null 引用调用 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()foo 为 null 时抛出 NullPointerException

    • name1::name2primaryExpression::name 形式的方法引用在以下情况下求值时会抛出 NullPointerException name1 或 primaryExpression 计算结果为 null。

      来自 JLS 的注释指出,someInstance.someStaticMethod() 不会抛出 NPE,因为 someStaticMethod 是静态的,但 someInstance:: someStaticMethod 仍然抛出 NPE!

    * 请注意,JLS 可能还间接谈论了很多有关 NPE 的内容。

    回复
    0
  • 取消回复