Java的自动拆装箱

  

  Java 1.5后引入了自动拆箱(Unboxing)和自动装箱机制(Autoboxing),用于实现基础数据类型和包装器间的隐式转换。基础数据类型和包装器类型的对应如下:


   这里写图片描述

自动拆装箱的实现

代码
public static void main(String[] args) {
        Integer i = 10;
        int n = i;
    }
反编译后
public class Demo {
    public static void main(String[] args) {
        Integer i = Integer.valueOf((int)10);
        int n = i.intValue();
    }
}

  通过代码和反编译可知,自动装箱通过包装器类型的valueOf()方法实现,而自动拆箱是通过调用包装器类型xxxValue()方法实现(xxx为基础数据类型)。

三元运算符中的拆装箱
public static void main(String[] args) {
       Integer i = 1;
        System.out.println(i == 1 ? true: false);
    }
反编译后
 public static void main(String[] args) {
        Integer i = Integer.valueOf((int)1); //自动装箱
        System.out.println((boolean)(i.intValue() == 1)); //自动拆箱
    }

  为了节省内存提高性能,部分包装器类型缓存了值在[-128,127]区间内的对象。这些包装器类型包括CharacterByteShortIntegerLong。以下以Integer类型为例:

Integer源码(Java8)的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的lowhigh的值分别为-128127。可以看出,当整数处于[-128,127]之间时,会从缓存中获取对象,否则创建新对象。
  其中,IntegerCache为静态内部类,在该类被加载时会执行静态代码块,读取缓存区间的右边界,并在对象数组cache中创建区间内的所有对象,因此在IntegerCache获取的值相同的对象是同一对象。

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() {}
    }

这五种基础类型的valueOf()函数大致相同,但double和float的valueOf()函数却不一样:

public static Double valueOf(double d) {
        return new Double(d);
    }
public static Float valueOf(float f) {
        return new Float(f);
    }

  通过源码可知,它总会直接创建一个新对象,之所以这样是因为整型区间的对象个数是有限的,而浮点类型该区间段的对象个数不是有限的。
  注意:缓存策略只针对自动装箱的情形有效,而对于直接创建的包装器类型无效,即不会被缓存。

Boolean源码(Java8)的valueOf()函数实现:
public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE); //TRUE 和 FALSE作为两个对象常量存在于cache中
    }

综合应用

示例1

Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1 == i2);   // true
System.out.println(i3 == i4);   // false

Double d1 = 100.0;
Double d2 = 100.0;
Double d3 = 200.0;
Double d4 = 200.0;
System.out.println(d1 == d2);   // false
System.out.println(d3 == d4);   // false

Boolean b1 = false;
Boolean b2 = false;
Boolean b3 = true;
Boolean b4 = true;
System.out.println(b1 == b2);   // true
System.out.println(b3 == b4);   // true
通过自动装箱的源码,再判断值是否再区间内,就可以轻易知道是否是同一个对象。
示例2
public class Test {

    public static Integer modify(Integer i) {
        i++;
        return i;
    }

    public static void main(String[] args) {
        Integer i = 200;
        System.out.println(i == modify(i));    // false
    }
}

在执行modify()函数时,变量i进行了拆箱装箱两步,拆箱做运算,装箱后将新的对象赋给了原变量。因此输出结果为false。在这可能不太直观,那让我们来编译一下:

public static Integer modify(Integer i) {
        Integer n = i;
        Integer n2 = i = Integer.valueOf((int)(i.intValue() + 1));
        return i;
    }

    public static void main(String[] args) {
        Integer i = Integer.valueOf((int)200);
        System.out.println((boolean)(i == Demo.modify((Integer)i)));
    }

通过反编译,具体是怎么运行的,一清二楚了。

示例3
public static void main(String[] args) {
        Integer i1 = 2;
        Integer i2 = 2;
        Integer i3 = i1 + i2;
        int i4 = i1 + i2;
        Long l1 = 4L;
        Long l2 = 0L;
        System.out.println(i1 == i2);                // true
        System.out.println(i3 == i4);                // true
        System.out.println(i3 == (i1 + i2));         // true
        System.out.println(l1.equals(i1 + i2));      // false
        System.out.println(l1.equals(i1 + i2 + l2)); // true
    }

反编译

 public static void main(String[] args) {
        Integer i1 = Integer.valueOf((int)2);
        Integer i2 = Integer.valueOf((int)2);
        Integer i3 = Integer.valueOf((int)(i1.intValue() + i2.intValue()));
        int i4 = i1.intValue() + i2.intValue();
        Long l1 = Long.valueOf((long)4L);
        Long l2 = Long.valueOf((long)0L);
        System.out.println((boolean)(i1 == i2));
        System.out.println((boolean)(i3.intValue() == i4));
        System.out.println((boolean)(i3.intValue() == i1.intValue() + i2.intValue()));
        System.out.println((boolean)l1.equals((Object)Integer.valueOf((int)(i1.intValue() + i2.intValue()))));
        System.out.println((boolean)l1.equals((Object)Long.valueOf((long)((long)(i1.intValue() + i2.intValue()) + l2.longValue()))));
    }
综上所述:

● 触发自动装箱的场景:将基本数据类型值赋予包装类型变量时,会自动装箱。
● 触发自动拆箱的场景:Integer做”>”、”>=”、”<”、”<=”操作时;Integer与int比较时;当有运算符时,如加、减、乘、除时……会比较数值大小,则自动拆箱。
● Boolean的两个值true和false都是在内存中,与new出的不是一块空间。
● Byte的256个值都在内存中,与new出的对象不是一块空间。
● 在反射当中,对于Integer属性不能使用field.setInt()和field.getInt()操作。
● 包装类型”==”时,判断是否指向同一个对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值