原始类型自动拆装箱问题
[TOC]
起因
近期在工作中,发现好多代码都错误使用了数值封装类型的方法,本无需主动拆箱的,使用了拆箱方法等,对自动拆装箱不熟导致的编码的问题,所以才写了这篇文章。
数值操作符
Operator | never unboxing | auto unboxing |
---|---|---|
== | Y | N |
!= | Y | N |
>= | N | Y |
<= | N | Y |
+ | N | Y |
- | N | Y |
* | N | Y |
/ | N | Y |
PS: 如果一方为原始类型,则以上所有操作符都会自动拆箱.如果封装类型为null,自动拆箱会报NPE的错误,因为是调用intValue函数实现的自动拆箱。
为什么==和!=不会自动拆箱
这个是由jvm底层实现决定的,因为在jvm中==
跟!=
的实现是对象通用的比较符,而Integer
等封装类型也属于对象,会在堆中分配内存,而这两个操作符是直接比较的是对象在内存中是否为同一块内存地址,也可以理解为c或者c++中的指针比较,如果不是同一个内存地址,那么==
和!=
会相应返回对应的false和true。
那为什么在默认环境中,在[-128,127]
范围内,封装类型比较值会返回正确的答案?
比如如下的代码:
@Test
public void imageIoTest() throws Exception {
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true
a = new Integer(127);
b = 127;
System.out.println(a == b); // false
a = 128;
b = 128;
System.out.println(a == b); // false
}
这是因为jdk中,自动装箱调用了各类型的#valueOf(int)
方法,在里面可以看到,在[-128,127]
的范围(Integer除外)内,都会去一个cache的数组中取值,jdk在jvm启动的时候就将[-128,127]
的封装类型(仅限于Integer、Long、Short、Byte)的对象缓存起来了,默认缓存[-128,127]
的值,其中Byte缓存了所有的值,也就是说Byte类型直接使用==
和!=
号也可以得出正确的数值(只要你不是自己new出来的Byte对象)。但如果是自行new出来的对象,就无法得出正确的数值了,这是因为new相当于新开辟了一块内存存储相应的数值了,内存地址都不一样了。
Integer的特殊性
其中Integer类型又是比较特殊的,它可以通过-XX:AutoBoxCacheMax=1024
和-Djava.lang.Integer.IntegerCache.high=1024
的方式配置缓存的最大值,注意,该配置只能配置最大值,最小值目前都是-128
(目前jdk最高版本是JDK17,后面会不会也允许配置,不得而知,可能性比较低),最大值范围为[127,Integer.MAX_VALUE - -128 -1]
,因为使用的是数组的方式做缓存,因此不能超过数组允许的最大下标值Integer.MAX_VALUE
,而又要有128+1个位置来存储[-128,0]
,因此最大的缓存值为Integer.MAX_VALUE- -128 -1
。
无论怎么来说,封装类型的比较还是比较建议使用equals来进行等值比较的。
通常来(Since JDK1.7),可以用Objects.equal(one,otherOne)来比较两个数字,可以更加优雅地避免空指针的问题,如果比较封装类型的数值,建议使用该方法,代码看起来更加整洁。
网友评论