一、问题
一段代码引发的血案
为了让int类型的方法参数可以接收null值,通常我们会把设置成int类型的包装类型Integer,我们的故事便从这里开始,我们先来看一段代码:
public class Demo {
public static void main(String[] args) {
test(10,10);
test(128,128);
}
public static void test(Integer i,Integer j){
if(i == null || j == null){
return;
}
if(i == j){
System.out.println("成功!");
}else {
System.out.println("失败!");
}
}
}
控制台输出:
成功!
失败!
My God,为什么第2个输出失败,而不是成功!崩溃。。。。
二、原因
猜测
我们猜测是Integer类导致的,我们知道Integer是int类型的包装类,这里就涉及到Java的自动装箱与拆箱了。那什么是自动装箱与拆箱呢?
Java中的自动装箱与拆箱
从Java SE5开始就提供了自动装箱与拆箱的特性。装箱就是自动将基本数据类型转换为包装器类型;拆箱是自动将包装器类型转换为基本数据类型。如下所示:
Integer m = 100; //装箱
int y = m; //拆箱
装箱与拆箱的底层实现
我们以Integer类为例,下面先看一段代码:
public class Case {
public static void main(String[] args) {
Integer m = 100;
int y = m;
}
}
这段代码反编译的字节码如下:
从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法,而在拆箱的时候自动调用的是Integer的intValue方法。其他的基本类型也类似,感兴趣的小伙伴可以去尝试一下!
接下来我们就需要查看Integer.valueOf方法的源码,我们来看一下:
从上我们能够看出,这里用到了IntegerCache,接下来我们来看一下IntegerCache类,我们发现IntegerCache是Integer的一个内部类:
我们可以从源码中发现,原来Integer类里面默认自带缓存,缓存范围默认为[-128-127]。这样我们就能解释上面例子的情况了,10在缓存范围内,所以两次返回的都是相同的对象,而128不在缓存范围内,所以每次都会新建一个Intger对象,这样就导致两个对象不等。
三、解决方法
要解决我们上面的问题,我们只需要使用Integer.intValue()方法就可以了,修改后的代码如下:
public class Demo {
public static void main(String[] args) {
test(10,10);
test(128,128);
}
public static void test(Integer i,Integer j){
if(i == null || j == null){
return;
}
if(i.intValue() == j.intValue()){
System.out.println("成功!");
}else {
System.out.println("失败!");
}
}
}
控制台输出:
成功!
成功!
我们可以看到,和我们预期的答案是一致的
四、总结
Integer类默认自带了缓存,可能会导致某些情况下出现一些匪夷所思的结果,所以我们在使用的时候需要注意!
好了,今天我们就到这了,大家可以思考一个问题,Java中的8中基本数据类型都有对应的包装类,那么是不是所有的包装类内部都有缓存呢?欢迎大家下方留言!
欢迎关注小强1024实验室,和小强一起用技术改变世界