8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

什么是 NullPointerException,如何修复它?

Chris B. Behrens 2月前

158 0

什么是空指针异常 (java.lang.NullPointerException) 以及导致空指针异常的原因是什么?可以使用哪些方法/工具来确定原因,以便阻止异常导致程序停止运行?

什么是空指针异常( java.lang.NullPointerException )以及其原因是什么?

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

帖子版权声明 1、本帖标题:什么是 NullPointerException,如何修复它?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Chris B. Behrens在本站《regex》版块原创发布, 转载请注明出处!
最新回复 (0)
  • Java 中有两种主要类型的变量:

    1. p1

    2. p2

    考虑以下代码,其中声明一个 原始 类型 int 但不初始化它:

    int x;
    int y = x + x;
    

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

    现在事情变得有趣了。 引用 变量可以设置为, null 这意味着\' I am referencing nothing \'。 null 如果您明确地这样设置引用变量,则可以获取引用变量中的值,或者引用变量未初始化并且编译器没有捕获它(Java 会自动将变量设置为 null )。

    如果引用变量被你明确地或通过 Java 自动设置为 null,并且你尝试 取消引用 它,则会得到一个 NullPointerException .

    NPE NullPointerException ) 通常发生在您声明变量但在尝试使用变量的内容之前未创建对象并将其分配给变量时。因此,您引用了实际上不存在的内容。

    以下面的代码为例:

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

    在这种情况下, obj null ,并且该语句 obj.myMethod() 将抛出一个 NullPointerException .

    如果该方法旨在对传入的对象执行某些操作(如上述方法所示),则抛出是适当的, NullPointerException 因为这是一个程序员错误,程序员需要该信息来进行调试。

    除了 NullPointerException 由于方法的逻辑而抛出的 s 之外,您还可以检查方法参数的 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
        }
    }
    

    最后, How to pinpoint the exception & cause using Stack Trace

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

    Sonar 配合 find bugs 可以检测 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
        }
    }
    
    • p26

    • p27

    • p28

    • 第 29 页

      第 30 页

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

  • \'避免此类异常的最佳方法是,当您自己没有创建对象时,始终检查是否为 null。\' 如果调用者传递了 null,但 null 不是该方法的有效参数,则将异常抛回给调用者是正确的,因为这是调用者的错误。默默忽略无效输入并在方法中不执行任何操作是极其糟糕的建议,因为它会隐藏问题。

  • 我想对这篇文章做一点补充,解释一下使用自动装箱时,即使对原语进行赋值也会导致 NPE:如果 b 是整数,则 int a=b 可能会引发 NPE。有些情况下,这会给调试带来困惑。

  • 是否可以通过 Web 浏览器捕获 Web 应用程序抛出的 NPE?就像它会显示在 Web 浏览器的查看页面源代码中一样。

  • 是的,在调用对象的方法或尝试访问其可能具有的变量之前,请检查对象是否等于 null。有时,构造代码可以帮助避免空指针异常。例如,当使用常量字符串检查输入字符串时,您应该从常量字符串开始,如下所示:if (\'SomeString\'.equals(inputString)) {} //即使 inputString 为 null,也不会引发异常。因此,您可以做很多事情来确保安全。

  • 避免代码中出现 NullPointerException 问题的另一种方法是使用 @Nullable 和 @NotNull 注释。以下答案对此有更多信息。虽然这个答案专门针对 IntelliJ IDE,但它也适用于其他工具,就像评论中提到的那样。(顺便说一句,我不能直接编辑这个答案,也许作者可以添加它?)

  • 上列出了其他方式 是当您尝试使用指向内存中没有位置 (null) 的引用(就像它引用对象一样)时发生的异常。调用 null 引用上的方法或尝试访问 null 引用的字段将触发 NullPointerException NullPointerException NullPointerException

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

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

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

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

  • 我理解你写的所有内容,但这只是因为我已经编码一段时间了,知道什么是“指针”和“引用”(以及什么是 null)。当我尝试直接深入解释时,我的学生斜眼看着我,因为背景知识不够。

  • 在实践中,更常见的获取 NullPointerException 的方式是忘记在使用成员变量之前将其显式初始化为除 null 之外的其他值,如下所示。对于局部变量,编译器会捕获此错误,但在这种情况下它不会。也许这会对您的答案有所帮助?

  • @EJP 我认为你的观点是正确的,所以我更新了答案,使其更清晰,并避免在原来的地方说“指向空”。

  • @StevePowell 我很久以前就表示过我不希望我的答案改变。请尊重原作者的意图。

  • 抱歉,我按照本条目顶部的要求“改进了答案”(该问题的答案是协作努力的结果:如果您发现可以改进的地方,只需编辑答案以改进它!)我不同意您的版本更好,在我看来@EJB 的观点正确;但尽管您的答案令人困惑,我们还是欢迎您保持其完整性。

  • 什么是NullPointerException?

    是一个很好的起点 JavaDocs 。它们涵盖了以下内容:

    当应用程序在需要对象的情况下尝试使用 null 时抛出。这些包括:

    • 调用空对象的实例方法。
    • 访问或修改空对象的字段。
    • 将 null 的长度视为一个数组。
    • 访问或修改 null 的槽,就像它是一个数组一样。
    • 抛出 null 就像它是一个 Throwable 值一样。

    应用程序应该抛出此类的实例来指示空对象的其他非法用途。

    另外,如果您尝试使用空引用 synchronized 根据 JLS, 也会引发此异常 :

    SynchronizedStatement:
        synchronized ( Expression ) Block
    
    • 否则,如果表达式的值为空,则 NullPointerException 抛出。

    我该如何修复它?

    所以你有一个 NullPointerException 。你该如何修复它?让我们看一个抛出 的简单示例 NullPointerException

    public class Printer {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void print() {
            printString(name);
        }
    
        private void printString(String s) {
            System.out.println(s + " (" + s.length() + ")");
        }
    
        public static void main(String[] args) {
            Printer printer = new Printer();
            printer.print();
        }
    }
    

    识别空值

    第一步是准确识别 哪些值导致了异常 。为此,我们需要进行一些调试。学会阅读 堆栈跟踪 。这将向您显示异常被抛出的位置:

    Exception in thread "main" java.lang.NullPointerException
        at Printer.printString(Printer.java:13)
        at Printer.print(Printer.java:9)
        at Printer.main(Printer.java:19)
    

    在这里,我们看到异常在第 13 行(在方法中 printString )被抛出。查看该行并通过添加 日志语句 或使用 调试器 。我们发现为 s 空,并且在其上调用 length 从方法中删除 s.length() 时,程序停止抛出异常

    追踪这些值的来源

    接下来检查这个值来自哪里。通过跟踪方法的调用者,我们看到 s 在方法中 printString(name) 传入了 print() ,并且 this.name 为空。

    追踪这些值应该设置的位置

    在哪里 this.name ?在 setName(String) 方法中。经过进一步的调试,我们可以看到根本没有调用此方法。如果调用了该方法,请确保检查 顺序 ,并且 set 方法不会 print 方法 之后

    这足以给我们一个解决方案:在 printer.setName() 调用之前 printer.print() .

    其他修复

    该变量可以有一个 默认值 (并且 setName 可以防止其被设置为空):

    private String name = "";
    

    print printString 可以 检查是否为 null ,例如:

    printString((name == null) ? "" : name);
    

    或者您可以设计该类,使其 name 始终具有非空值

    public class Printer {
        private final String name;
    
        public Printer(String name) {
            this.name = Objects.requireNonNull(name);
        }
    
        public void print() {
            printString(name);
        }
    
        private void printString(String s) {
            System.out.println(s + " (" + s.length() + ")");
        }
    
        public static void main(String[] args) {
            Printer printer = new Printer("123");
            printer.print();
        }
    }
    

    参见:

    • 避免在 Java 中使用“!= null”语句?

    我还是找不到问题所在

    如果您尝试调试问题但仍然没有找到解决方案,您可以发布问题以获得更多帮助,但请确保包含您迄今为止尝试过的方法。至少, 在问题中 包含堆栈跟踪 标记代码中的重要行号 。此外,请先尝试简化代码(请参阅 SSCCE )。

  • @RuchirBaronia 调试器允许您逐行执行程序,以查看调用了哪些方法以及变量如何更改。IDE 应该有一些工具可以做到这一点。例如,请参阅 vogella.com/tutorials/EclipseDebugging/article.html。

  • @RuchirBaronia 您可以在堆栈跟踪中看到的任何 NullPointerExceptions 周围的方法上设置断点,并根据预期检查变量的值。如果您知道变量为空(但不应该为空),则可以在任何更改该值的代码周围设置断点。您还可以使用条件断点,它会告诉您值何时发生变化。

  • Rmy5 2月前 0 只看Ta
    引用 17

    我还建议使用静态分析工具,例如 FINDBUGS en.m.wikipedia.org/wiki/FindBugs

  • 问题:什么原因导致 NullPointerException (NPE)?

    您应该知道,Java 类型分为 原始类型 boolean , int 等)和 引用类型 。Java 中的引用类型允许您使用特殊值 null ,这是 Java 表示“无对象”的方式。

    当作真实引用 NullPointerException 使用时,运行时都会抛出 null A。

    public class Test {
        public static void main(String[] args) {
            String foo = null;
            int length = foo.length();   // HERE
        }
    }
    

    标记为 \'HERE\' 的语句将尝试 length() 上运行该方法 null ,这将引发 NullPointerException .

    有很多方法可以使用一个 null 值来产生一个结果 NullPointerException 。事实上,你 可以 用一个值来做 null 不会导致 NPE 的事情只有以下几种:

    • 将其分配给引用变量或从引用变量读取,
    • 将其分配给数组元素或从数组元素中读取它(前提是数组引用本身非空!),
    • 将其作为参数传递或作为结果返回,或者
    • == 进行测试 != ,或者 instanceof .

    问题:如何读取 NPE 堆栈跟踪?

    假设我编译并运行上述程序:

    $ javac Test.java 
    $ java Test
    Exception in thread "main" java.lang.NullPointerException
        at Test.main(Test.java:4)
    $
    

    第一个观察结果:编译成功!程序中的问题不是编译错误。这是 运行时 错误。(某些 IDE 可能会警告您的程序将始终抛出异常……但标准 javac 编译器不会。)

    第二个观察结果:当我运行该程序时,它输出两行“胡言乱语”。 错! 那不是胡言乱语。这是一个堆栈跟踪……如果您花时间仔细阅读,它提供了 重要信息 ,可帮助您追踪代码中的错误。

    让我们看看它说了什么:

    Exception in thread "main" java.lang.NullPointerException
    

    堆栈跟踪的第一行告诉您许多事情:

    • 它告诉您抛出异常的 Java 线程的名称。对于只有一个线程的简单程序(如本程序),它将是 \'main\'。让我们继续...
    • 它告诉您引发的异常的全名;即 java.lang.NullPointerException .
    • 如果异常具有相关的错误消息,则会在异常名称后输出。 NullPointerException 在这方面是不寻常的,因为它很少有错误消息。

    第二行是诊断 NPE 最重要的一行。

    at Test.main(Test.java:4)
    

    这告诉我们很多事情:

    • \'at Test.main\' 表示我们处于 main 该类的方法 Test
    • \'Test.java:4\' 给出了该类的源文件名,并且它告诉我们发生此情况的语句位于文件的第 4 行。

    如果您计算上面文件中的行数,第 4 行就是我用 \'HERE\' 注释标记的行。

    请注意,在更复杂的示例中,NPE 堆栈跟踪中会有很多行。但您可以确定第二行(第一个 \'at\' 行)会告诉您 NPE 被抛出的位置1.

    简而言之,堆栈跟踪将明确地告诉我们程序的哪个语句抛出了 NPE。

    另请参阅: 什么是堆栈跟踪,以及如何使用它来调试我的应用程序错误?

    1 - 不完全正确。有一种东西叫做嵌套异常...

    问题:如何在我的代码中追踪 NPE 异常的原因?

    这是最难的部分。简而言之,就是对堆栈跟踪、源代码和相关 API 文档提供的证据进行逻辑推理。

    我们先用上面的简单示例来说明一下。我们首先查看堆栈跟踪告诉我们发生 NPE 的位置:

    int length = foo.length(); // HERE
    

    那怎么会引发 NPE?

    事实上,只有一种方法:只有当 foo 具有值 null 这种情况。然后我们尝试运行该 length() 方法 null ,然后...砰!

    但是(我听到你说)如果在方法调用中抛出 NPE 怎么办 length()

    好吧,如果发生这种情况,堆栈跟踪看起来会有所不同。第一行“at”会说异常是在类中的某行中引发的 java.lang.String ,而第 4 行将 Test.java 是第二行“at”。

    那么,这是 null 从哪里来的呢?在这种情况下,这是显而易见的,我们需要做什么来修复它也是显而易见的。(为 分配一个非空值 foo 。)

    好的,让我们尝试一个稍微复杂一点的例子。这需要一些 逻辑推理 .

    public class Test {
    
        private static String[] foo = new String[2];
    
        private static int test(String[] bar, int pos) {
            return bar[pos].length();
        }
    
        public static void main(String[] args) {
            int length = test(foo, 1);
        }
    }
    
    $ javac Test.java 
    $ java Test
    Exception in thread "main" java.lang.NullPointerException
        at Test.test(Test.java:6)
        at Test.main(Test.java:10)
    $ 
    

    所以现在我们有了两个 \'at\' 行。第一个是针对此行的:

    return args[pos].length();
    

    第二个是针对这一行:

    int length = test(foo, 1);
        
    

    看看第一行,怎么会抛出 NPE?有两种方式:

    • 如果 bar 值为 null 则将 bar[pos] 抛出 NPE。
    • bar[pos] 值为, null 则调用 length() 它将引发 NPE。

    接下来,我们需要弄清楚哪种情况可以解释实际发生的情况。我们将从第一个开始探索:

    哪里 bar ?它是方法调用的一个参数 test ,如果我们查看它是如何 test 被调用的,我们可以看到它来自 foo 静态变量。此外,我们可以清楚地看到我们将其初始化 foo 为非空值。这足以暂时驳回这个解释。(理论上,其他东西可能会 改变 foo null ......但这里没有发生这种情况。)

    那么我们的第二种情况呢?好吧,我们可以看到是 pos 1 所以这意味着 foo[1] 一定是 null 。这可能吗?

    确实如此!这就是问题所在。当我们像这样初始化时:

    private static String[] foo = new String[2];
    

    我们分配一个 String[] ,其中包含两个 that are initialized to null 。之后,我们没有改变 foo ...的内容,所以 foo[1] 仍然是 null .

    那么在 Android 上怎么样?

    在 Android 上,追踪 NPE 的直接原因要简单一些。异常消息通常会告诉您所使用的空引用的(编译时)类型 以及 抛出 NPE 时您尝试调用的方法。这简化了查明直接原因的过程。

    但另一方面,Android 有一些常见的特定于平台的 NPE 原因。一种非常常见的情况是 getViewById 意外返回 null 。我的建议是搜索有关意外 null 返回值原因的问答。

  • 这就像你试图访问一个对象 null 。请考虑以下示例:

    TypeA objA;
    

    此时你只是 声明了 这个对象,但还没有 初始化或实例化 。每当你尝试访问其中的任何属性或方法时,它都会抛出异常, NullPointerException 这是有道理的。

    另请参见下面的示例:

    String a = null;
    System.out.println(a.toString()); // NullPointerException will be thrown
    
  • 如果我们给出 System.out.println(a.length()); // 将抛出 NullPointerException,为了跳过这个我们可以使用 try catch 块来处理。谢谢

返回
作者最近主题: