做了很长时间开发了,记得最开始写代码的时候,就是最容易出现空指针的,只是把代码写完,完全不考虑判断校验问题,也是因为刚接触生产开发,还没有这些意识。不过后面开发就逐渐完备了,下意识的会关注这些东西。
原文链接:
stackoverflow.com/questions/2…
问题
- 什么是空指针(java.lang.NullPointerException),什么导致的?
- 可以使用什么方法/工具来确定原因,以防止异常导致程序过早终止?
回答
第一个问题
Java 中的类型
Java 中有两种类型变量:
- 基本类型:包含数据,可以直接操作变量。通常来说基本类型都是小写字母开头的。比如:int / char
Java 中有8 个基本类型变量:
byte/short/int/float/long/double/boolean/char/
- 引用类型:变量拥有内存地址,变量指向这个内存地址。如果你想引用他,需要使用 . 来访问一个属性或方法,或者使用 [ 来索引一个数组。通常都是大写字母开头的,比如 Object 。
概念
思考下面的代码:你申明了 int 类型的变量,但是没有初始化
int x;
int y = x + x;
上面这两行代码会导致程序崩溃,因为我们尝试使用没有初始化的基本数据类型。
所有基本数据类型被使用前,都必须初始化。
有趣的是:引用类型可以被设置 null :意味着,我啥也没引用。你可以得到一个 null 值:你可以明确的设置或者当没有初始化的时候,编译器不会捕捉它(Java 会自动设置一个 null)
当你想要使用引用对象的时候,如果这个引用被明确的设置 null 或者 Java 自动赋值 null。你就会得到一个 NullPointerException。
NPE 发生在你使用变量的时候,还没有为这个变量创建对象。
示例
Integer num;
num = new Integer(10);
第一行代码申明了一个 num 变量,但是没有初始化对象,此时 Java 会自动设置一个值 null.
第二行代码,new 关键字实例化了一个对象,并将其指向 num .
有时候,自己定义的变量,使用的时候可能会提示你 “num 还没有初始化”。但是更多时候,比如调用返回值,封装他人的接口。就会直接 NPE 了。
比如:
Integer num;
//下面两行都会提示你需要初始化
System.out.println(num);
num.toString();
num = new Integer(10);
public void doSomething(SomeObject obj) {
// Do something to obj, assumes obj is not null
obj.myMethod();
}
//这样的就直接 NPE
doSomething(null);
除了方法的逻辑会抛出 NPE 外,还可以使用检查方法/工具类明确抛出 NPE.
Objects.requireNonNull(obj, "obj must not be null");
提前校验的好处是:
- 可以返回你定制的错误,定位问题更加快速/准确
- 对于方法的其余部分,你可以知道这个 obj 一定不是 null。可以安全的引用
在某些情况下,方法的空参数也是可以被接受的,那么此时需要检查下并另外做逻辑代码。
比如:
public void doSomething(SomeObject obj) {
// Do something to obj, assumes obj is not null
if(obj == null){
// do something
}else{
obj.myMethod();
}
}
第二个问题
Finally, How to pinpoint the exception & cause using Stack Trace
在 Java14 中,NPE 能被溯源,比如下面的报错信息:
in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.List.size()" because "list" is null
导致发生 NPE 的列表
- 访问一个空引用的实例字段(静态字段不算)
- 调用一个空引用的方法(静态方法不算)
- 直接抛出 NPE
- 访问一个空数组的元素
- 对 null 进行同步 synchronized(obj){}
- 任何整数/浮点数运算符都能抛出 NPE,如果它的一个操作数是一个框定的空引用的话
- 使用 for 循环一个空列表或数组
- switch(foo){},如果 foo 是null
- name1::name2或primaryExpression::name形式的方法引用在name1或primaryExpression评估为null时抛出一个NullPointerException。JLS的注释说,someInstance.someStaticMethod()不会抛出NPE,因为someStaticMethod是静态的,但是someInstance::someStaticMethod还是会抛出NPE!