Java中的基本数据类型与包装类及之间的比较(==)
本文参考https://blog.csdn.net/qq_26287435/article/details/107852241
基本数据类型与包装数据类型
原始数据类型也就是基本数据类型,八种基本数据类型和其包装类分别如下,其中包装类为引用数据类型
- byte,java.lang.Byte(父类Number)
- short,java.lang.Short(父类Number)
- int,java.lang.Integer(父类Number)
- long,java.lang.Long(父类Number)
- boolean,java.lang.Boolean(父类Object)
- char,java.lang.Character(父类Object)
- float,java.lang.Float(父类Number)
- double,java.lang.Double(父类Number)
两种数据类型的==比较
Java中的基本类型
及其包装类的比较(==)
一直是一个比较头疼的问题,不仅有自动装箱和拆箱
操作,部分的包装类还有对象缓存池
,这就导致了这部分知识容易混淆。
这是因为对于==
操作符来说,如果比较的数据是基本类型
,则比较它们的值
,如果比较的是对象
,则会比较对象的内存地址
。另外,如果一个是基本类型、一个是包装类型,在比较前会先把包装类型拆箱
成基本类型,然后进行比较。
以int为例,这里我们把参与比较的类型分为三种:int
、直接new出来的Integer对象
和自动装箱出来的Integer对象
。这里先不考虑Integer的缓存池
,我们对三种类型之间的两两比较
进行下排列组合,3 * 3一共有9
种可能。由于==
运算符是不区分左右的先后顺序
的,也就是说a == b
跟b == a
等价,所以可以去除3种重复比较,这样就只有6种情况了。具体如下表:
类型/类型 | int | new Integer | Integer AutoBoxing |
---|---|---|---|
int | ① | ② | ④ |
new Integer | ② | ③ | ⑤ |
Integer AutoBoxing | ④ | ⑤ | ⑥ |
int == int
1 | // int 与 int 类型比较 |
基本数据类型比较的是值,所以毫无疑问是true
int == new Integer
1 | // int类型和new出来的Integer对象比较 |
i4在比较时会自动拆箱成基本数据类型,所以也是值比较,结果为true
new Integer == new Integer
1 | // 两个new出来的Integer对象的内存地址比较 |
包装数据类型的比较的是对象的地址,因为是两个不同的对象,所以为false
int == Integer AutoBoxing
1 | // ③int类型 和 自动装箱的Integer对象 比较 |
i8在赋值的时候,会自动把128装箱成Integer类型,接着使用==
比较的时候,i8又会自动拆箱成int,所以实质上还是两个int
在比较
Integer AutoBoxing == new Integer
1 | // 自动装箱的Integer类型 和 new出来的Integer对象 比较 |
i9在赋值时会将128自动装箱成Interger类型,比较时是两个不同的对象比较,地址必然不同,为false
Integer AutoBoxing == Integer AutoBoxing
1 | // 自动装箱的Integer对象 和 自动装箱的Integer对象 比较,区间[-128, 127]之外 |
这里的i10和i11在赋值时都会先将128自动装箱成Integer类型
,然后再进行比较,原理同上,还是为false
上述涉及到自动装箱的Integer对象时用到的数字均为128,这是为了不受Integer缓存池的影响
Integer的缓存池
1 | // 自动装箱会从缓存池中取对象,缓存池的区间为[-128, 127] |
这个例子跟上面的最后一种情况一摸一样,只是把数字换成了127,结果就变成了true。
过程是:i13和i14的赋值过程中,均会触发自动装箱。给127生成对应的Integer对象
,接下来使用==
比较这两个Integer对象的地址
,最后结果为true,也就是说明i13和i14是同一个对象,分配的是同一块内存
Integer对应的自动装箱的方法是Integer#valueOf()
,我们看下它的实现:
1 | public static Integer valueOf(int i) { |
可以看到如果i的值在某个范围内的话,就会从IntegerCache.cache
这个数组中取出一个Integer对象
返回;如果超出了这个范围的话就直接new
一个Integer对象
返回。
省略掉无关代码,IntegerCache
的具体实现如下
1 | private static class IntegerCache { |
这里的下限low
的值默认为-128
,上限high
的值默认为127
,cache
是一个Integer类型的数组
。
static
代码块里面主要做了两件事:
- 初始化
cache
这个Integer数组,数组容量为256。 - 给
cache
的所有元素赋值。在for循环里面,依次给cache的各个元素进行赋值,Integer的值从-128开始,一直到127,对应的是闭区间[-128, 127],刚好256个元素。
所以我们常说的Integer的缓存池
其实就是这里的IntegerCache.cache
,它在Integer进行类加载
的时候初始化.
在基本类型int需要自动装箱的时候,就会调用Integer#valueOf方法,在Integer#valueOf方法的实现中,如果int的值在闭区间[-128, 127](IntegerCache.cache缓存的范围内),就返回已经构建好的Integer对象,具体是根据角标从IntegerCache.cache这个数组中去取。
经过上面的讲解可以知道,我们这里的127
经过自动装箱
后,是从缓存池中拿的Integer对象,所以i13
和i14
拿到的是同一个Integer对象(因为它们的值相同,在IntegerCache.cache
数组中对应的index相同,所以获取到的是同一个缓存对象)。
基本类型对应的包装类中,Byte
、Short
、Integer
、Long
和Character
的缓存池都是[-128, 127]
,Boolean
的缓存池比较特殊,只有true和false两个Boolean对象。Float
和Double
没有缓存池