持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
问题情境:
String a = null;
if(a.equals(null)||a == null ){
return true;
}else{
return false;
}
请问你的答案是什么?
true?还是false?我想告诉你,都不是!这里会报空指针异常!即你会看到这个异常:java.lang.NullPointerException。
why?为啥嘞?
这里我先不告诉你为什么。我先要来介绍一下java里面的null到底是个什么东东。主要以几个关键点来介绍。
1、null是一个关键字
首先,null是一个关键字,它和java里面的public、static、void等一样都是关键字。java中关键字对大小写是敏感的,所以null和Null不一样。要注意了。
2、null是任何引用类型的默认值
java中任何类型都有一个默认值,基本类型有各自的默认值,例如:你定义了一个int i,那么系统会自动帮你给这个i附一个这个类型下的默认值,int类型的默认值是0,boolean类型的默认值是false,而引用类型的默认值则是null,或者说所有Object类型及其子类型的默认值都是null。
这里你可能会说,java1.5之后对基本类型都有一个自动装箱、自动拆箱的过程啊,那int自动装箱后就是Integer引用类型了,那它的默认值就是null了,这里面不就有问题了吗?
是的,这里就有问题了,问题在哪里?这里卖个关子,我们后面说。
我们继续说对于任何引用变量的默认值都是null,它对java中的所有引用变量都是用,如,成员变量、局部变量、实例变量、静态变量等等。(这里要注意,当你使用一个没有赋值的局部变量是,编译器会给你一个错误警告,并且不会让你通过。)下面我们测试一下吧。
public class test {
private static String a2;//静态变量
public static void main(String[] args) {
String a3 ;//编译器会警告//局部变量
Cat cat = new Cat();
System.out.println("成员变量 a1="+cat.a1);
System.out.println("静态变量 a2="+a2);
//System.out.println("静态变量 a3="+a3);编译器不通过
}
}
class Cat{
public Integer a1;//成员变量
}
显示的结果:
成员变量 a1=null
静态变量 a2=null
3、null是一个特殊的值
前面说null是一个关键字,它既不是应用类型,也不是基本类型,它是一个比较特殊的值。你可以将null赋值给引用类型,你也可以将其转化为任何引用类型。例如:
String b1 = null;
Integer b2 = null;
Double b3 = null;
String c1 = (String)null;
Integer c2 = (Integer)null;
Double c3 = (Double)null;
System.out.println("b1 = "+b1);
System.out.println("b2 = "+b2);
System.out.println("b3 = "+b3);
System.out.println("c1 = "+c1);
System.out.println("c2 = "+c2);
System.out.println("c3 = "+c3);
编译通过,运行结果如下:
b1 = null
b2 = null
b3 = null
c1 = null
c2 = null
c3 = null
但是你不能将null赋值给一个基本类型。例如你在进行如下操作的时候,编译器不会让你通过编译:
int i = null;
double d = null;
boolean b = null;
char c = null;
short t = null;
long l = null;
float f = null;
byte bt =null;
以上语句编译器会报错,通过不了。既然不能将null直接赋值给基本类型数据,那么我可以先将null赋值给基本类型的包装类型,再赋值给基本类型可以吗?试试吧:
int aa1 = (Integer) null;
double aa2 = (Double) null;
boolean aa3 = (Boolean) null;
char aa4 = (Character) null;
short aa5 = (Short) null;
long aa6 = (Long) null;
float aa7 = (Float) null;
byte aa8 =(Byte) null;
System.out.println(aa1);
System.out.println(aa2);
System.out.println(aa3);
System.out.println(aa4);
System.out.println(aa5);
System.out.println(aa6);
System.out.println(aa7);
System.out.println(aa8);
可以看到当你将null利用包装类型包装后再赋值给基本类型时,编译是通过的,但是但你运行时,就汇报一下错误:
java.lang.NullPointerException
这就是空指针异常。为什么编译能通过,运行确报错呢?
这里关系到编译器机制问题了,我就不详细说了,通常,编译器只是对你的代码格式等问题进行检查,当它发现你直接将null赋值给基本类型性,编译器检查到这个格式是不正确的,所以报错,不通过编译;
但是当我们将null包装后,就成为了包装类型,这时就是等号左边是基本类型,右边是包装类型,而编译器在编译时会自动将基本类型装箱为Integer类型,所以两边都是Object类型,所以编辑器就让通过了。而运行时,比较的是真正的类型比较,也就是说,运行的时候,系统会将赋值为null的包装类型自动的有拆箱为基本类型,也就是讲null暴露出来了,那么这时,就不符合“null不能复制给基本类型的规则了”,所以就会报出一个空指针异常。
4、可以使用instanceof操作来判断null是否为引用类型
进行如下操作:
Integer in = null;
if(in instanceof Integer) {
System.out.println("true");
}else {
System.out.println("false");
}
结果是:
false
这就充分证明了null不是引用类型。所以我们以后可以通过instanceof操作判断一个变量的真正类型。
5、null在使用==和!=的情况
我们可以使用==或者!=来比较左右两边包含null的情况,例如:
Object o = null;
if(null == "") {
System.out.println("true");
}else {
System.out.println("false");
}
运行结果为:
false
但是我们不能使用其他方法来比较左右两边包含null的情况,例如
Object object = null;
if(object.equals("")) {
System.out.println("true");
}else {
System.out.println("false");
}
运行报空指针异常,结果如下:
java.lang.NullPointerException
这就解释了文章一开始设计的那个案例了。
这就是报错的核心。我们再来看一下问题的代码:
String a = null;
if(a.equals(null) || a == null ){
return true;
}else{
return false;
}
当我们运行该程序时,将null赋值给String类型,然后进入if判断,里面有两个判断条件,一个是a.equals(null),另一个是a==null。这里要注意:逻辑运算符||在进行判断时,只要有一个条件为true结果就为true。但是当我们进行if判断时,会执行a.equals(null),这时就会报出空指针异常的错误了,因为我们说了,null不能用其他方法进行比较,只能用==(!=)来比较。
这就是这个问题的整个运行过程。