[Java]_[初级]_[装箱和拆箱的陷阱-不要使用==进行包裹类型wrapper class比较]

Posted infoworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java]_[初级]_[装箱和拆箱的陷阱-不要使用==进行包裹类型wrapper class比较]相关的知识,希望对你有一定的参考价值。

场景

  1. 在使用JavaInteger进行算术运算时, 偶尔发现使用 == 比较运算符两个int值一样的前提下结果是false, 什么原因?

说明

  1. JDK5已经开始提供装箱(autoboxing)和拆箱(auto-unboxing)的功能,目的是可以在原始数据类型和包裹(wrapper)类型之间方便转换,也能方便进行算术运算. 这样就不需要频繁的调用Integer.intValue()Integer.valueOf()来转换类型了.

  2. 另一个作用就是在集合类里, 泛型类型必须是引用类型的,因此是没有List<int>这种写法,只能是List<Integer> 或者 List<Object>等。一些需要Object作为参数的方法,在传递int类型时会自动转换为Integer类型, 还是比较方便的.

  3. 使用Integer进行算术和比较运算时,它会自动拆箱为int类型进行计算。但是有一个==比较运算符是所有Object都支持的,如果进行Integer == Integer比较,那么实际上是调用了Object.equals进行比较,并不是算术比较,这个要注意了。再次强调, 不要对两个类型都是Integer进行==比较运算, 其中一个Integer转换为int再进行比较两两==比较。 其他的比较运算符, 如>=等会先拆箱为int比较,是正确的.

以下的两个Integer类型进行==比较是可以的.

print("ie4 == ie5",ie4.compareTo(ie5) == 0);
print("ie4.intValue() == ie5",ie4.intValue() == ie5);
print("ie4+0 == ie5", (ie4+0) == ie5);
print("(int)ie4 == ie5", (int)ie4 == ie5); 
  1. 至于如果Integer值在[-128,127]区间[2]使用的静态缓冲区也可以进行==比较。但是没用, 既然进行比较就不知道它的具体值,所以不用考虑这种比较,还是老老实实手动转换为int进行==比较.

JDK进行的valueOf实现:

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

  1. 除了Integer类型,还有以下的包裹类型支持对应的原始类型:
Primitive typeWrapper class
booleanBoolean
byteByte
charCharacter
floatFloat
intInteger
longLong
shortShort
doubleDouble

例子

  1. 以下是Integerint进行装箱的各种情况:

AutoBoxingTest

package com.example.string;

import org.junit.jupiter.api.Test;



public class AutoBoxingTest 
    
    public static<T> void print(String key,T t)
        System.out.println(key +" : "+t);
    

    
    public static<T> void add_1(Integer input)
        input += 1;
    

    public static void add_2(int input)
        print("add_2",input); // 调用泛型方法自动装箱为Integer类型.
    

    @Test
    public void testInteger()

        //情况1. Integer可用于算术运算符, 遇到算术运算符自动拆箱为int类型.
        Integer ie1 = Integer.valueOf(10); // 创建不可变Integer对象.
        print("ie1",ie1); // 10
        print("ie1 id",System.identityHashCode(ie1));
        ie1 += 100; 
        ie1 = ie1 +10;
        print("ie1",ie1); // 120
        print("ie1 id",System.identityHashCode(ie1)); // 注意: 对象ID已经改变,ie1指向的不是原来的Integer对象了.


        //情况2: Integer作为参数是值传递的.
        int i1 = ie1; // 可赋值给原始变量, 自动拆箱.
        print("i1",i1); // 120 值未发生变化 
        add_1(ie1); // 可传递给原始变量的参数. 
        print("ie1",ie1); // 120 值未发生变化

        // 情况3: Integer可传递给int参数的方法。
        add_2(ie1); // 120

        // 情况4: int传递给Integer参数方法时自动装箱.
        add_1(i1);
        print("ie1",ie1); // 120
        
        // 情况5: int赋值给Integer变量时自动装箱.
        Integer ie2 = new Integer(120);
        print("ie2",ie2); // 120
        Integer ie3 = 120;
        print("ie3",ie3); // 120
        
        //  情况6: 不允许Integer和int之间使用比较运算符.
        print("ie2 > ie1",(ie2 >= ie1)); //true
        print("ie2 == ie1",(ie2 == ie1)); // false
        print("ie3 == ie1",(ie3 == ie1)); // true -128<= ie <= 127 使用的是常量池的对象,因此比较是相同的.
            
        Integer ie4 = 290;
        Integer ie5 = 290;    
        print("ie4 == ie5",(ie4 == ie5));  // false > 127, 创建了新对象。  
        print("ie4 >= ie5",(ie4 >= ie5)); // true 自动拆箱为int比较
        // 使用compareTo来判断是否相等.
        print("ie4 == ie5",ie4.compareTo(ie5) == 0); // true
        // 结论, 不要使用 == 符号来比较两个Integer类型数据.
        // integer和int做运算时,integer会自动拆箱为int类型,结果也为int类型, 之后int和integer类型比较,integer类型又会自动拆箱.
        print("ie4+0 == ie5", (ie4+0) == ie5); // true
        print("(int)ie4 == ie5", (int)ie4 == ie5); // true int强制转换和integer赋值给int一样,会拆箱
        print("ie4.intValue() == ie5",ie4.intValue() == ie5); // true
        
        // 非wrap类型使用比较符只能是 ==.    
        String s1 = "1"; // 常量存储区
        String s2 = new String("1"); // 非常量池
        //if(s1 > s1) // 非Wrapper 类型使用非==比较运算符编译报错.
        print("s1 == s2",(s1 == s2));  //false 会转换为使用equals()比较, 所以不一样
            
        // List<int> tt; // 编译错误
    
    


输出

参考

  1. Autoboxing and Unboxing

  2. Why is 128128 false but 127127 is true when comparing Integer wrappers in Java?

  3. Java 装箱和拆箱 128陷阱问题

以上是关于[Java]_[初级]_[装箱和拆箱的陷阱-不要使用==进行包裹类型wrapper class比较]的主要内容,如果未能解决你的问题,请参考以下文章

Java中装箱和拆箱的代码

装箱和拆箱的性能损耗以及没有装箱和拆箱的性能损耗

java 啥是拆箱和装箱,拆箱和装箱 嘛用啊???

转载:详解Java 自动装箱与拆箱的实现原理

Java的自动装箱和拆箱的简单讲解

自动装箱和拆箱的几个细节