什么是空指针异常 ( java.lang.NullPointerException
), 是什么原因导致它们?
可以使用什么方法/工具来确定原因, 以便阻止异常导致程序过早终止?
当您声明引用变量 (即对象) 时, 实际上是在创建指向对象的指针。请考虑下面的代码, 在其中声明基元类型的变量 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
}
}
NullPointerException
s 是当您尝试使用指向内存中没有位置 (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 指针不一样。 空指针实际上不是指向任何地方, 这与指向恰好无效的位置有微妙的不同。
一个很好的起点是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 = "";
print
或 printString
方法可以检查 null, 例如:
printString(
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:
==
或运算符对其进行测试 !=
, 或 instanceof
。假设我编译并运行上面的程序:
$ 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.lang.NullPointerException
,NullPointerException
在这方面是不寻常的, 因为它很少有错误消息。第二行是诊断 n p e 最重要的一行。
at Test.main(Test.java:4)
这告诉我们很多事情:
main
Test
在类的方法。如果您计算上面文件中的行, 第 4 行是我标记为 "HERE" 注释的行。
请注意, 在更复杂的示例中, NPE 堆栈跟踪中会有大量行。但你可以肯定, 第二行 (第一行 "在") 会告诉你 n p e 在哪里被抛出 1。
简而言之, 堆栈跟踪将明确地告诉我们程序的哪个语句抛出了 NPE。
1-不完全是真的。有些东西被称为嵌套异常..。
这就像你试图访问一个对象, 这是 null
。请考虑下面的示例:
TypeA objA;
此时, 您刚刚声明了此对象, 但未初始化或实例化。而每当你试图访问其中的任何属性或方法时, 它都会抛出 NullPointerException
, 这是有意义的。
请参见下面的示例:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
当应用程序尝试在需要对象的情况下使用 null 时, 将引发空指针异常。这些措施包括:
null
方法。null
。null
当作一个数组。null
, 就像它是一个数组一样。null
, 就好像它是一个可抛出的值。应用程序应抛出此类的实例, 以指示对象的其他非法用途 null
。
参考: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
NULL
指针是指向无果而终的指针。 当您取消引用指针时 p
, 您会说 "将存储在" p "中的位置的数据提供给我。 当 p
一个空指针, 存储在的位置 p
是 nowhere
, 你说 "给我的数据在位置 ' 无处 '"。 显然, 它不能这样做, 所以它抛出 NULL pointer exception
了一个。
一般情况下, 这是因为有些东西没有正确初始化。
已经有很多解释来解释它是如何发生的以及如何修复它, 但你也应该遵循最佳做法 NullPointerException
来避免。
参见:最佳实践的良好列表
我要补充的是, 非常重要的是, 要很好地利用 final
修饰符。 在 Java 中适用时使用 "最终" 修饰符
总结:
final
修饰符强制实施良好的初始化。@NotNull
和@Nullable
if("knownObject".equals(unknownObject)
valueOf()
tostring ()。StringUtils
方法 StringUtils.isEmpty(null)
。空指针异常是在不初始化对象的情况下使用该对象的指示器。
例如, 下面是一个将在我们的代码中使用它的学生类。
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 ");
}
}
}
在 Java 中, 所有内容都是以类的形式出现的。
如果要使用任何对象, 则有两个阶段:
例子:
Object a;
a=new Object();
数组概念也是如此
Item i[]=new Item[5];
i[0]=new Item();
如果您没有给出初始化部分, 则 NullpointerException
会出现。
在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 将释放此对象使用的内存, 并将分配另一个内存。
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));
}