什么是 Null Pointerexception, 我如何修复它?

共12个回答,已解决, 标签: java nullpointerexception

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

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

第1个答案(采用)

当您声明引用变量 (即对象) 时, 实际上是在创建指向对象的指针。请考虑下面的代码, 在其中声明基元类型的变量 int :

int x;
x = 10;

在本例中, 变量 x 是一个 int , Java 将为您将其初始化为 0。当您将其分配给第二行中的 10 时, 您的值 10 将写入 x 指向的内存位置。

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

Integer num;
num = new Integer(10);

第一行声明一个名为变量 num , 但是, 它不包含基元值。相反, 它包含一个指针 (因为该类型 Integer 是引用类型)。由于您还没有说指向什么 Java 将其设置为 null, 意思是 "我什么都不指向"。

在第二行中, new 关键字用于实例化 (或创建) 整数类型的对象, 并为指针变量 num 分配此对象。现在, 您可以使用取消引用运算符 . (一个点) 引用对象。

Exception当您声明变量但未创建对象时, 会发生您询问的内容。如果在创建对象之前尝试取消引用 num , 则会获得 NullPointerException 。在最琐碎的情况下, 编译器会抓住问题, 让您知道 "num 可能尚未初始化", 但有时您编写的代码不会直接创建对象。

例如, 您可能有一个方法, 如下所示:

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

在这种情况下, 您不是 obj 在创建对象, 而是假设它是在 doSomething 调用方法之前创建的。遗憾的是, 可以这样调用该方法:

doSomething(null);

在这种情况下 obj 是空的。如果该方法旨在对传入对象执行某些操作, 则应该将其引发, NullPointerException 因为它是程序员错误, 并且程序员将需要该信息进行调试。

或者, 在某些情况下, 该方法的目的可能不仅仅是对传入的对象进行操作, 因此 null 参数可能是可以接受的。在这种情况下, 您需要检查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
    }
}

最后,如何使用堆栈跟踪来确定异常和原因

第2个答案

NullPointerExceptions 是当您尝试使用指向内存中没有位置 (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 指针不一样。 空指针实际上不是指向任何地方, 这与指向恰好无效的位置有微妙的不同。

第3个答案

什么是 Null Pointer 异常?

一个很好的起点是javadocs。他们涵盖了以下情况:

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

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

应用程序应引发此类的实例, 以指示 null 对象的其他非法使用。

也存在这样的情况: 如果您尝试使用空引用 synchronized , 这也会引发此异常,每个 jls:

SynchronizedStatement:
    synchronized ( Expression ) Block
  • 否则, 如果表达式的值为 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 null, 并在 length 其上调用该方法将引发异常。我们可以看到 s.length() , 当从方法中删除时, 程序将停止引发异常。

跟踪这些值的来源

下一步检查此值的来源。通过跟踪方法的调用方, 我们看到该 s printString(name) 方法是在该方法中传入的 print() this.name , 并且为 null。

跟踪应设置这些值的位置

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

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

其他修补程序

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

private String name = "";

printprintString 方法可以检查 null, 例如:

printString(
第4个答案

问: 是什么原因 NullPointerException 导致 (n p e)?

正如您应该知道的, java 类型分为基元类型( boolean int 等) 和引用类型。Java 中的引用类型允许您使用特殊值 null , 这是 Java 表示 "无对象" 的方式。

NullPointerException每当程序尝试使用 a 时, 就会引发 a, null 就像它是一个真正的引用一样。例如, 如果您编写此内容:

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:

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

问: 如何阅读 NPE 堆栈跟踪?

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

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

第一个观察: 编译成功!程序中的问题不是编译错误。这是一个运行时错误。(某些 Ide 可能会警告您的程序将始终引发异常..。但标准 javac 编译器没有。

第二点观察 : 当我运行程序时 , 它输出两行 gobbfydy - 。错了!!那不是胡说八道。这是一个堆栈痕迹..。它提供了重要的信息, 将帮助您跟踪代码中的错误, 如果您花时间仔细阅读它。

因此, 让我们来看看它说什么:

Exception in thread "main" java.lang.NullPointerException

堆栈跟踪的第一行告诉你很多事情:

  • 它告诉您在其中引发异常的 Java 线程的名称。 对于一个简单的程序与一个线程 (像这个), 它将是 "主"。让我们继续前进.....。
  • 它告诉您引发的异常的全名;即 java.lang.NullPointerException ,
  • 如果异常具有关联的错误消息, 则该错误消息将在异常名称之后输出。NullPointerException在这方面是不寻常的, 因为它很少有错误消息。

第二行是诊断 n p e 最重要的一行。

at Test.main(Test.java:4)

这告诉我们很多事情:

  • "在 test. main" 说我们是 main Test 在类的方法。
  • "Test. java:4" 给出了类的源文件名, 它告诉我们发生这种情况的语句位于文件的第 4 行。

如果您计算上面文件中的行, 第 4 行是我标记为 "HERE" 注释的行。

请注意, 在更复杂的示例中, NPE 堆栈跟踪中会有大量行。但你可以肯定, 第二行 (第一行 "在") 会告诉你 n p e 在哪里被抛出 1。

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

1-不完全是真的。有些东西被称为嵌套异常..。

第5个答案

这就像你试图访问一个对象, 这是 null 。请考虑下面的示例:

TypeA objA;

此时, 您刚刚声明了此对象, 但未初始化或实例化。而每当你试图访问其中的任何属性或方法时, 它都会抛出 NullPointerException , 这是有意义的。

请参见下面的示例:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
第6个答案

当应用程序尝试在需要对象的情况下使用 null 时, 将引发空指针异常。这些措施包括:

  1. 调用对象的实例 null 方法。
  2. 访问或修改对象的字段 null
  3. 把它的长度 null 当作一个数组。
  4. 访问或修改插槽 null , 就像它是一个数组一样。
  5. 抛出 null , 就好像它是一个可抛出的值。

应用程序应抛出此类的实例, 以指示对象的其他非法用途 null

参考: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

第7个答案

NULL指针是指向无果而终的指针。 当您取消引用指针时 p , 您会说 "将存储在" p "中的位置的数据提供给我。 当 p 一个空指针, 存储在的位置 pnowhere , 你说 "给我的数据在位置 ' 无处 '"。 显然, 它不能这样做, 所以它抛出 NULL pointer exception 了一个。

一般情况下, 这是因为有些东西没有正确初始化。

第8个答案

已经有很多解释来解释它是如何发生的以及如何修复它, 但你也应该遵循最佳做法 NullPointerException 来避免。

参见:最佳实践的良好列表

我要补充的是, 非常重要的是, 要很好地利用 final 修饰符。 在 Java 中适用时使用 "最终" 修饰符

总结:

  1. 使用 final 修饰符强制实施良好的初始化。
  2. 避免在方法中返回 null, 例如, 在适用的情况下返回空集合。
  3. 使用批注 @NotNull@Nullable
  4. 快速失败并使用断言以避免空对象在不应该为 null 的情况下在整个应用程序中传播。
  5. 首先与已知对象相等:if("knownObject".equals(unknownObject)
  6. 更喜欢 valueOf() tostring ()。
  7. 使用空安全 StringUtils 方法 StringUtils.isEmpty(null)
第9个答案

空指针异常是在不初始化对象的情况下使用该对象的指示器。

例如, 下面是一个将在我们的代码中使用它的学生类。

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

下面的代码为您提供了一个空指针异常。

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}

因为您正在使用 Obj_Student , 但您忘记了像在下面所示的正确代码中一样对其进行初始化:

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student = new Student();
            obj_Student.setId(12);
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}
第10个答案

在 Java 中, 所有内容都是以类的形式出现的。

如果要使用任何对象, 则有两个阶段:

  1. 宣布
  2. 初始 化

例子:

  • 声明:Object a;
  • 初始 化:a=new Object();

数组概念也是如此

  • 声明:Item i[]=new Item[5];
  • 初始 化:i[0]=new Item();

如果您没有给出初始化部分, 则 NullpointerException 会出现。

第11个答案

Java中, 您声明的所有变量实际上都是对对象 (或基元) 的 "引用", 而不是对象本身。

当您尝试执行一个对象方法时, 引用会要求活动对象执行该方法。但是, 如果引用为 NULL (无、零、void、nada), 则无法执行该方法。然后, 运行时通过引发一个 Null Pointerexception 让您知道这一点。

您的引用是 "指向" 空, 从而 "空-> 指针"。

对象位于 VM 内存空间中, 访问该对象的唯一方法是使用 this 引用。以下面的示例为例:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

在代码中的另一个位置:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

这是一个重要的事情要知道-当没有更多的引用到一个对象 (在上面的例子时, reference otherReference 并且都指向 null), 那么该对象是 "无法访问"。我们无法使用它, 因此此对象已准备好进行垃圾回收, 并且在某个时候, VM 将释放此对象使用的内存, 并将分配另一个内存。

第12个答案

NullPointerException当您声明对象数组, 然后立即尝试取消其内部的引用元素时, 就会发生另一个事件。

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

如果比较顺序相反, 可以避免这种特定的 NPE;即, .equals 在保证的非空对象上使用。

数组中的所有元素都被初始化为其共同的初始值;对于任何类型的对象数组, 这意味着所有元素都是 null

在访问或取消引用数组中的元素之前,必须对其进行初始化。

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

相关问题

Java 是 "逐项传递" 还是 "按值传递"? 如何比较 Java 中的字符串? 什么是 Null Pointerexception, 我如何修复它? NullPointerException 是什么?如何修复它? 在 android 的 Toast 消息中打印 StringArray [] 时 NullPointerException