自动装箱和自动拆箱

246 阅读4分钟
原文链接: my.oschina.net

自动装箱和自动拆箱

Java 提供了 8 种基本数据类型,每种数据类型都有其对应的包装类型,包装类是面向对象的类,是一种高级的数据类型,可以进行一些比较复杂的操作,它们是引用类型而不再基本类型了。

基本类型和包装类型的对应关系
          基本类型             包装类型
byte(1字节 Byte
short(2字节) Short
int(4字节) Integer
long(8字节) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(1字节) Boolean

 

自动装箱
自动装箱,简单来说,就把基本数据类型自动包装为引用类型,

在 JDK 1.5 之前,是不能把一个基本类型直接赋值给引用类型的,需要引用类型,就必须使用创建对象的方式,如下:

        Integer integer = new Integer(10);

JDK 1.5 之后,它们之间可以自由的转换,如下所示:

        int j = 20;
        Integer integer1 = j;

JVM 会把基本类型 j 自动包装为引用类型 Integer,这就是自动装箱;查看下它的class文件,如下:

    int j = 20;
    
    Integer integer1 = Integer.valueOf(j);

可以看到,自动装箱,就是调用 valueOf() 方法来实现的。

自动拆箱

自动拆箱,就是把一个引用类型自动转换为基本类型,如下所示:

        Integer integer = new Integer(10);
        int i = integer;

反编译它的class文件如下:

    Integer integer = new Integer(10);
    int i = integer.intValue();

可以看到,自动拆箱就是调用 intValue() 方法来实现的。

上述完整代码如下:

public class Hello {
    public static void main(String[] args) {

        Integer integer = new Integer(10);
        int i = integer;

        int j = 20;
        Integer integer1 = j;
    }
}

反编译class文件如下:

public class Hello
{
  public static void main(String[] args)
  {
    Integer integer = new Integer(10);
    int i = integer.intValue();
    
    int j = 20;
    Integer integer1 = Integer.valueOf(j);
  }
}

 

还有一点需要注意的是,当在调用 valueOf() 方法的时候,默认情况下,如果需要转换的数值在 -128~127 之间,则直接从缓存中取,而不会创建 Integer 对象,源码如下:

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

接下来看下它的静态内部类 IntegerCache,它在第一次使用的时候进行加载,上面说了,默认情况下在 -128~127 之间从缓存读取,这个最大值 127 是可以配置的,通过虚拟机参数 -XX:AutoBoxCacheMax=<size> 进行配置,它的源码如下:

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

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

            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
    }

接下来验证一下:

默认情况下:

        Integer i1 = Integer.valueOf(-20);
        Integer i2 = Integer.valueOf(-20);
        System.out.println(i1 == i2); // true

        Integer i3 = Integer.valueOf(20);
        Integer i4 = Integer.valueOf(20);
        System.out.println(i3 == i4); // true

        Integer i5 = Integer.valueOf(127);
        Integer i6 = Integer.valueOf(127);
        System.out.println(i5 == i6); // true

        Integer i7 = Integer.valueOf(-129);
        Integer i8 = Integer.valueOf(-129);
        System.out.println(i7 == i8); // false

        Integer i9 = Integer.valueOf(128);
        Integer i10 = Integer.valueOf(128);
        System.out.println(i9 == i10); // false

可以看到,在 -128~127 之外,则会创建新的对象,在之间则会从缓存中读取。

接下来修改这个上限值,通过添加虚拟机参数  -XX:AutoBoxCacheMax=<size> 把上限值设置为 200

在运行上面的用例:

        Integer i1 = Integer.valueOf(128);
        Integer i2 = Integer.valueOf(128);
        System.out.println(i1 == i2); // true

        Integer i3 = Integer.valueOf(200);
        Integer i4 = Integer.valueOf(200);
        System.out.println(i3 == i4); // true

        Integer i5 = Integer.valueOf(201);
        Integer i6 = Integer.valueOf(201);
        System.out.println(i5 == i6); // false

可以看到,上限值已经改变了。

Integer、Short、Byte、Character、Long 的 valueOf 方法都是和上面一样,在 -128~127 之间之间从缓存从取。

Double,Float 则不是,因为在某个范围内的整型数值的个数是有限的,而浮点数却不是

        Double l1 = Double.valueOf(100.0);
        Double l2 = Double.valueOf(100.0);
        System.out.println(l1 == l2); //false

        Double l3 = Double.valueOf(200.0);
        Double l4 = Double.valueOf(200.0);
        System.out.println(l3 == l4);//false

它们的 valueOf 方法如下:

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

    public static Float valueOf(String s) throws NumberFormatException {
        return new Float(parseFloat(s));
    }

Boolean 类型又是如何呢?

       Boolean b1 = Boolean.valueOf(true);
       Boolean b2 = Boolean.valueOf(true);
       System.out.println(b1 == b2);//true

       Boolean b3 = Boolean.valueOf(false);
       Boolean b4 = Boolean.valueOf(false);
       System.out.println(b3 == b4);//true

它的源码如下:

    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

可以看到,它们指向的是同一个静态对象。

总结

1.自动装箱:把基本类自动转换为引用类型,调用的是  valueOf() 方法实现

2.自动拆箱:把引用类型自动转换为基本类型,调用的是  intValue() 方法实现

3.valueOf() 方法:Integer、Short、Byte、Character、Long 的 valueOf() 方法,在一定数值范围内直接从缓存中获取,而不会创建对象,只有超过了界限才会创建对象。Double,Float 的 valueOf() 方法每次都会创建新的对象,因为在一定范围内浮点数的个数是无限的。Boolean 对象的 valueOf() 方法调用的都是同一个静态对象。