自动拆箱和装箱

1. 基本类型对应的包装类型

8种基本类型对应的包装类型如下图所示:

基本类型 位数 字节 默认值 包装类型
byte 8 1 0 Byte
short 16 2 0 Short
int 32 4 0 Integer
long 64 8 0L Long
float 32 4 0f Float
double 64 8 0d Double
char 16 2 'u0000' Character
boolean 1 未定 false Boolean

基本类型存储的是具体的值,而包装类型存储的是对象的引用,显然基本类型比包装类型更加简洁和高效。

那么,为什么要引入包装类型呢?区别如下:

  1. 包装类型可以为null,而基本类型不可以。在某些情况下,比如从数据库中查询某个学生的成绩,有可能查询到的结果是null,基本类型是无法接收的,只能使用包装类型。

  2. 包装类型可以用于集合和泛型,而基本类型不可以。有些朋友可能会有疑惑,我们在往集合对象里添加基本类型时,也没有报错啊?其实这是系统自动将基本类型装箱成包装类型,然后放入到集合中,所以本质上集合存储的是包装类型。而我们如果想使用泛型对集合进行限定时,用基本类型就没法办到了,例子如下:

    int i = 10;
    List list = new ArrayList();
    list.add(i);//系统会自动将i装箱为Integer类型的对象然后再存入list集合中
    System.out.println(list); // [10]
    //使用基本类型到泛型中
    List<int> list = new ArrayList<int>();//编译报错
    

2. 自动装箱和拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

在 Java SE5 之前,开发人员要手动进行装拆箱。

Integer i = new Integer(100);  // 手动装箱
int n = i.intValue();          // 手动拆箱

Java SE5 提供了自动装箱与自动拆箱的功能

Integer i = 100;    // 自动装箱,等价于 Integer i = Integer.valueOf(100);
int n = i;          // 自动拆箱,等价于 int n = i.intValue(); 

3. 缓存池

3.1 包装类型的缓存范围

包装类型 缓存范围
Byte -128~127
Short -128~127
Integer -128~127
Long -128~127
Float 数量无限,无法缓存
Double 数量无限,无法缓存
Character 0~127
Boolean True和False两个静态常量

上一节中提到了手动装箱和自动装箱的两种方式,那么他们有什么区别呢?

Integer i = new Integer(100);  // 手动装箱每次都会新建一个对象
Integer i = Integer.valueOf(100);//查看valueOf方法的源码可知,自动装箱时,会去IntegerCache缓存池中查找值是否存在缓存池中,存在则返回。所以如果自动装箱的值在缓存池范围内,多次调用会取得同一个对象的引用。

valueOf方法和IntegerCache缓存池源码如下:

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

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

4. 面试中容易出错的点

下面程序的输出结果是什么?

public static void main(String[] args) {
        Integer a = 2;
        Integer a1 = new Integer(2);
        Integer a2 = new Integer(2);
        Integer b = 3;
        Integer b1 = new Integer(3);
        Integer c = 5;
        Integer d = 5;
        Integer e = 223;
        Integer f = 223;
        Long g = 5L;
        Long h = 3L;

        System.out.println(a1==a2);           //false,a1和a2都是新建的Integer对象,引用的内存地址不同
        System.out.println(a1.equals(a2));	  //true,a1和a2都是新建的Integer对象,引用的内存地址不同,但是值相同

        System.out.println(c==d);		      //true,自动装箱,都从缓存池中获取同一对象,引用的内存地址相同
        System.out.println(c.equals(d));	  //true,自动装箱,都从缓存池中获取同一对象,引用的内存地址相同,值也相同

        System.out.println(c==(a+b));         //true,==号右边操作数为表达式,自动拆箱,比较的是数值
        System.out.println(c.equals(a+b));    //true,对于包装器类型,equals方法并不会进行类型转换。a+b自动拆箱装箱后,转换成Integer类型,值也与C相等

        System.out.println(e==f);             //false,自动装箱,数值超出缓存池缓存范围,新建Integer对象,引用的内存地址不同
        System.out.println(e.equals(f));      //true,自动装箱,数值超出缓存池缓存范围,新建Integer对象,引用的内存地址不同,但是值相同

        System.out.println(g==(a+b));         //true,==号右边操作数为表达式,自动拆箱,比较的是数值
        System.out.println(g.equals(a+b));    //false,对于包装器类型,equals方法并不会进行类型转换。a+b自动拆箱装箱后,转换成Integer类型,Long类型和Integer类型无法比较数值,返回false

        System.out.println(g.equals(a+h));    //true,对于包装器类型,equals方法并不会进行类型转换。a+h自动拆箱装箱后,转换成Long类型,与g比较后数值相等,返回true

    }

附程序反编译代码做参考

 public static void main(String[] var0) {
     Integer var1 = Integer.valueOf(2);
     Integer var2 = new Integer(2);
     Integer var3 = new Integer(2);
     Integer var4 = Integer.valueOf(3);
     new Integer(3);
     Integer var6 = Integer.valueOf(5);
     Integer var7 = Integer.valueOf(5);
     Integer var8 = Integer.valueOf(223);
     Integer var9 = Integer.valueOf(223);
     Long var10 = Long.valueOf(5L);
     Long var11 = Long.valueOf(3L);
     System.out.println(var2 == var3);
     System.out.println(var2.equals(var3));
     System.out.println(var6 == var7);
     System.out.println(var6.equals(var7));
     System.out.println(var6.intValue() == var1.intValue() + var4.intValue());
     System.out.println(var6.equals(Integer.valueOf(var1.intValue() + var4.intValue())));
     System.out.println(var8 == var9);
     System.out.println(var8.equals(var9));
     System.out.println(var10.longValue() == (long)(var1.intValue() + var4.intValue()));
     System.out.println(var10.equals(Integer.valueOf(var1.intValue() + var4.intValue())));
     System.out.println(var10.equals(Long.valueOf((long)var1.intValue() + var11.longValue())));
 }
posted @ 2020-10-14 15:23  渺渺孤烟起  阅读(64)  评论(0)    收藏  举报