JDK定义
Thrown when an application attempts to use null in a case where an object is required. These include:
- Calling the instance method of a null object.
- Accessing or modifying the field of a null object.
- Taking the length of null as if it were an array.
- Accessing or modifying the slots of null as if it were an array.
- Throwing null as if it were a Throwable value.
Applications should throw instances of this class to indicate other illegal uses of the null object. NullPointerException objects may be constructed by the virtual machine as if suppression were disabled and/or the stack trace was not writable.
异常说明
该异常应该是我们平时学习工作中,遇见频率最高的异常其中之一了,也是绝大部分初学者的噩梦(至少在我的学习历程中,这个异常经常出现,当时也看不懂啥堆栈,出现异常就是一脸懵)。
此异常中文解释为空指针异常,也就是程序试图访问一个空的内存地址,这个异常有个异常代码模板:null.xxx,一个为null对象,却试图访问这个null对象的字段或方法时,就会抛出这个异常。
常见场景
1. 直接调用或访问对象为null的方法或字段
public static void main(String[] args) {
//定义对象a但未赋值
Integer a=null;
//直接调用null对象a的hashCode方法
System.out.print(a.hashCode());
}
这种情况应该是平时遇到的最多的情况,对象忘了赋值,或经过一段逻辑后成了null,在或者未对接收的参数进行非空校验等,最终如果对其访问都会抛出java.lang.NullPointerException,符合异常代码模板null.xxx的情况
2.隐式调用或访问对象为null的方法或字段
public static void main(String[] args) {
//定义对象a但未赋值
Integer a=null;
//对null对象进行比较
System.out.print(a==8);
}
这种代码其实乍一看其实没有太大问题,顶多输出一个false,但事实非这么简单,探究本质我们得看看编译后的字节码,使用插件jclasslib 以下字节码仅展示main方法代码部分
0 aconst_null //将空对象引用压入操作数堆栈
1 astore_1 // 把刚推入的空对象引用存储到局部变量表中索引为1的变量上(这个变量1可以从本地变量表LocalVariableTable中查到)
2 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;> //一看就懂获取静态字段 out
5 aload_1 //加载局部变量表索引为1的变量存储的引用,也就是我们变量a的引用,其值为null
// 这行就是实际引发异常的原因
//由于定义的变量a其类型为Integer包装类,实际是个引用类型,但是a==8 8这个数字其类型为int的值类型
// 所以这里会调用Integer的intValue方法获取包装类Integer中value字段的值,该值为实际值,类型为int,
// 目的是为了与数字8直接进行值比较
// 重点来了 null.intValue() 是不是也异常代码模板null.xxx
6 invokevirtual #3 <java/lang/Integer.intValue : ()I>
//后续就不逐一分析了 到这里就异常中断
9 bipush 8
11 if_icmpne 18 (+7)
14 iconst_1
15 goto 19 (+4)
18 iconst_0
19 invokevirtual #4 <java/io/PrintStream.print : (Z)V>
22 return
对于这种写法如果出现异常,一时还难以反应过来,所以为了避免这种不小心建议大家使用Objects.equals进行比较
public static void main(String[] args) {
//定义对象a但未赋值
Integer a=null;
//对null对象进行比较
System.out.print(Objects.equals(a,8));
}
实际引发异常的场景肯定不止这两个,主要是分享平时常见的情况,如果你有觉得比较有意思的场景,请评论区分享