Integer的自动装箱与比较问题

问题引入

Java使用==进行引用的比较,使用equals进行对象的比较。在多数情况下,都是使用==比较基本数据类型,而使用equals比较对象类型。但Java又为每个基本数据类型建立了相应的包装类,这使得相等的比较稍微复杂了一些,其中涉及自动装箱与拆箱的问题。同时,Integer的缓存机制使得比较情况更加复杂。因此,在这里分析一下Integer的自动装箱与比较问题。

自动装箱

自动拆箱和自动装箱是 java的语法糖之一,执行在编译期,会根据代码的语法决定是否进行拆箱和装箱动作。自动装箱使得定义基本数据类型的包装类时,可以直接使用=赋值,而不使用new,如:

Integer a = 5;

在编译时,会根据这种语法,决定进行装箱操作,实际上执行的语句调用了Interger的静态方法:

Integer a = Integer.valueOf(128);

在任何时候,如果涉及到从基本数据类型到其包装类的转换,编译器都会决定进行自动装箱。比如:

Map<String,Integer> map = new HashMap<>();
map.put("a", 1); 

由于map中值的类型为Integer,而put将int类型的值放到map中,自动装箱后的语句类似于:

Map<String,Integer> map = new HashMap<>();
map.put("a", Integer.valueOf(1)); 

自动拆箱

和自动装箱相反,如果将Integer类型的数据赋值给基本数据类型int,就会执行自动拆箱。如:

Integer a = new Integer(5);
int b = a;

在编译时,会根据这种语法,决定进行拆箱操作,实际上执行的语句调用了a的intValue方法:

Integer a = new Integer(5);
int b = a.intValue();

当Integer类型的对象和基本数据类型进行算术或关系运算时,Integer对象也会自动拆箱,比如:

Integer a = new Integer(5);
int b = 6;
Integer c = a + b;

当执行Integer c = a + b;时,编译器发现一个Integer类型和一个int类型进行算术运算,决定对Integer类型进行拆箱。a+b运算的结果实际上是int类型的,将结果赋值给c时又会进行装箱操作。

Integer的缓存机制

Integer的缓存机制使得比较问题更加复杂。上面说过,自动装箱时调用了Integer.valueOf(),这个函数的源码为:

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

其中IntegerCache.low=-128,IntegerCache.high=127。这说明valueOf的参数如果介于-128和127之间的话,会返回IntegerCache中的对象,否则会重新new一个对象并返回。

Integer/int的比较

1.两个int类型进行比较,直接使用==就好了。

2.涉及Integer的比较,使用equals总不会出错。如果是两个Integer进行比较,equals当然比较的是各对象的值。如果是一个Integer和一个int比较,只能对Integer对象使用equals方法,这时会涉及自动装箱,比如:

Integer a = 5;
int b = 5;
a.equals(b);

最后一条语句会让b自动装箱,但是equals比较的始终是对象的值,因此不会有问题

3.涉及Integer的比较,如果使用==,情况会变得复杂。

如果是一个Integer和一个int比较,前面说过会进行自动拆箱,Integer对象会变为int类型,相当于两个int的==比较,没有问题。

如果是两个Integer进行比较,这里情况比较复杂。

(1)只要有一个Integer是通过new产生的,一定会得到false。因为new会在堆中分配内存,而==比较的是引用,也就是比较内存地址,只要不是别名引用,一定会得到false

Integer a = new Integer(5);
Integer b = new Integer(5);
Integer c = 5;
a == b // false
a == c // false

(2)两个Integer都是直接赋值产生的,只有被缓存时才能得到true。根据上面valueOf的源码可知,如果值落在-128~127之间,会返回IntegerCache中的对象,对于下面的代码,a和b都指向IntegerCache中的同一对象,因此==会得到true

Integer a = 5;
Integer b = 5;
a == b // true

(3)两个Integer都是直接赋值产生的,但未缓存,会得到false。根据valueOf的源码,如果值不在-128~127之间,会返回new的对象,这时和(1)完全一样。对于下面的代码,a和b都是new出的对象,==会得到false

Integer a = 128;
Integer b = 128;
a == b // false

 

对于其他基本数据类型及其包装类,也涉及装箱、拆箱以及缓存的机制

包装类型基本数据类型缓存
Booleanbooleantrue,false
Bytebyte-128~127
Shortshort-128~127
Characterchar0~127
Integerint-128~127
Longlong-128~127
Floatfloat无缓存
Doubledouble无缓存

启示

可以看出比较的情况还是比较多的,但是如果只使用equals,就不会出现任何问题。因此涉及包装类对象之间值的比较,一定要用equals方法。这也是阿里Java开发手册编程规约中指出的:强制所有包装类对象之间值的比较,全部使用 equals 方法!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值