什么是NullPointerException,以及如何解决它?

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

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


当你声明一个引用变量(即一个对象)时,你真的创建了一个指向对象的指针。 在声明基本类型int的变量时考虑以下代码:

int x;
x = 10;

在这个例子中,变量x是一个int ,Java会将它初始化为0。 当你在第二行中将它赋值为10时,你的值10被写入x所指向的内存位置。

但是,当您尝试声明引用类型时会发生不同的事情。 采取以下代码:

Integer num;
num = new Integer(10);

第一行声明一个名为num的变量,但它不包含原始值。 相反,它包含一个指针(因为类型是Integer ,它是一个引用类型)。 既然你还没有说Java到底指向什么,它意味着“ 我没有指向任何东西 ”。

在第二行中, new关键字用于实例化(或创建)Integer类型的对象,并且指针变量num被分配给该对象。 现在可以使用解引用操作符来引用该对象. (一个点)。

当您声明一个变量但未创建对象时,您询问的Exception就会发生。 如果您尝试在创建对象之前解除引用num ,则会得到NullPointerException 。 在最微不足道的情况下,编译器会捕获这个问题,并让你知道“num可能没有被初始化”,但有时你会编写不直接创建对象的代码。

例如,你可能有一个方法如下:

public void doSomething(SomeObject obj) {
   //do something to obj
}

在这种情况下,您不是创建对象obj ,而是假定它是在doSomething方法被调用之前创建的。 不幸的是,可以像这样调用方法:

doSomething(null);

在这种情况下, obj为null。 如果该方法旨在对传入的对象执行某些操作,则抛出NullPointerException是合适的,因为这是程序员错误,程序员将需要该信息用于调试目的。

或者,可能会出现这样的情况:方法的目的不仅仅是对传入的对象进行操作,因此可能接受空参数。 在这种情况下,您需要检查一个空参数并且行为不同。 你也应该在文档中解释这一点。 例如, 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
    }
}

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


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

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

public class Example {

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

}

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

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


什么是NullPointerException?

JavaDocs是一个很好的开始。 他们涵盖了这些内容:

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

  • 调用空对象的实例方法。
  • 访问或修改空对象的字段。
  • 将null的长度视为数组。
  • 像访问数组一样访问或修改null的槽。
  • 如果它是Throwable值,则抛出null。
  • 应用程序应该抛出此类的实例来指示其他非法使用null对象。

    同样的情况是,如果您尝试使用带有synchronized的空引用,那么也会根据JLS引发此异常:

    SynchronizedStatement:
        synchronized ( Expression ) Block
    
  • 否则,如果Expression的值为null,则抛出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()从方法中移除时,我们可以看到程序停止抛出异常。

    跟踪这些值来自哪里

    接下来检查这个值来自哪里。 通过跟随方法的调用者,我们看到sprint()方法中使用printString(name)传入,并且this.name为null。

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

    this.name在哪里设置? 在setName(String)方法中。 通过更多的调试,我们可以看到这个方法根本不被调用。 如果调用该方法,请确保检查这些方法的调用顺序,并且在打印方法之后不调用set方法。

    这足以给我们一个解决办法:加入到呼叫printer.setName()调用之前printer.print()

    其他修补程序

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

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

    链接地址: http://www.djcxy.com/p/13309.html

    上一篇: What is a NullPointerException, and how do I fix it?

    下一篇: Check for null object