自动装箱与拆箱 - morris131/morris-book GitHub Wiki

自动装箱与拆箱

八种基本数据类型(byte,short,char,int,long,float,double,boolean)对应八种包装类型(Byte,Short,Charactor,Integer,Long,Float,Double,Boolean)。

装箱是自动将基本数据类型转换为包装器类型;拆箱是自动将包装器类型转换为基本数据类型。

Integer i = 10;  //装箱
int n = i;   //拆箱

实现原理

以Interger类为例,下面看一段代码:

package com.morris.base;

public class AutoPackingTest {
	
	public static void main(String[] args) {
		Integer m = 10;
		int n = m;
	}
	
}

对编译后的class文件使用Java Decompiler进行反编译:

package com.morris.base;

public class AutoPackingTest
{
  public static void main(String[] args)
  {
    Integer m = Integer.valueOf(10);
    int n = m.intValue();
  }
}

从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法,,而在拆箱的时候自动调用的是Integer的intValue方法。

总结:装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

面试题

package com.morris.base;

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

		Integer a = 1;
		Integer b = 2;
		Integer c = 3;
		Integer d = 3;
		Integer e = 321;
		Integer f = 321;
		Long g = 3L;
		Long h = 2L;
		Integer i = new Integer(1);

		System.out.println(c == d);
		System.out.println(e == f);
		System.out.println(c == (a + b));
		System.out.println(c.equals(a + b));
		System.out.println(g == (a + b));
		System.out.println(g.equals(a + b));
		System.out.println(g.equals(a + h));
		System.out.println(i == a);
	}
}

运行结果如下:

true
false
true
true
true
false
true
false

反编译结果如下:

package com.morris.base;

import java.io.PrintStream;

public class PackingEqualTest
{
  public static void main(String[] args)
  {
    Integer a = Integer.valueOf(1);
    Integer b = Integer.valueOf(2);
    Integer c = Integer.valueOf(3);
    Integer d = Integer.valueOf(3);
    Integer e = Integer.valueOf(321);
    Integer f = Integer.valueOf(321);
    Long g = Long.valueOf(3L);
    Long h = Long.valueOf(2L);
    Integer i = new Integer(1);
    
    System.out.println(c == d);
    System.out.println(e == f);
    System.out.println(c.intValue() == a.intValue() + b.intValue());
    System.out.println(c.equals(Integer.valueOf(a.intValue() + b.intValue())));
    System.out.println(g.longValue() == a.intValue() + b.intValue());
    System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue())));
    System.out.println(g.equals(Long.valueOf(a.intValue() + h.longValue())));
    System.out.println(i == a);
  }
}


Integer源码分析

Integer中有个静态内部类IntegerCache,里面有个cache[],也就是Integer常量池,常量池的大小为一个字节(-128~127),可以使用JVM的启动参数(-XX:AutoBoxCacheMax=size)设置最大值。

    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    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) {
                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);
            }
            high = h;

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

        private IntegerCache() {}
    }

所有整数类型的类都有类似的缓存机制:Byte,Short,Long 的缓存池范围默认都是:-128到127。可以看出,Byte的所有值都在缓存区中,用它生成的相同值对象都是相等的。所有整型(Byte,Short,Long)的比较规律与Integer是一样的。同时Character对象也有CharacterCache缓存池,范围是0到127。除了Integer可以通过参数改变范围外,其它的都不行。

Integer重写了Object的equals()方法,比较时会自动拆箱。

    /**
     * Compares this object to the specified object.  The result is
     * {@code true} if and only if the argument is not
     * {@code null} and is an {@code Integer} object that
     * contains the same {@code int} value as this object.
     *
     * @param   obj   the object to compare with.
     * @return  {@code true} if the objects are the same;
     *          {@code false} otherwise.
     */
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
⚠️ **GitHub.com Fallback** ⚠️