Integer缓存机制、装箱拆箱实现原理

概述

了解Java语言的应该都知道Integer这个类,知道它是基本数据类型int的包装器类,也知道它的用法等,但仅仅停留在[会用]这个层面上的程序员不是好司机。通过本文,我们将知道:

  • Integer核心源码
  • 自动装箱和自动拆箱实现原理
  • Integer缓存机制

自动装箱


请看下面代码:

public class IntegerTest {
    public static void main(String[] args) {
        Integer i1 = 1; //why
        System.out.println(i1);
    }
}

我们对上面的代码再熟悉不过,熟悉到我们可以不假思索地说出输出结果是1,熟悉到我们忘了问自己:
为什么可以把一个int类型的数赋值给Integer类型? 下面我们就一步步揭开这条语句的神秘面纱。

通过工具javap来反编译IntegerTest.class(javap -c IntegerTest.class),部分结果截图如下:

Integer缓存机制、装箱拆箱实现原理_第1张图片
image

从以上截图可以看到,编译器在编译Integer i1 = 1时调用了Integer.valueOf(int i)方法,我们来看看valueOf方法源码:

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

这是一个if-else结构语句,else语句体很好理解,通过调用Integer构造器创建一个新对象。但if语句体里面是什么鬼东西呢?其实是这样的,在Java中new对象会调用构造器创建一个新的对象,给它分配内存、管理它的生命周期等等等。为了解决每次new对象带来的存储分配、生命周期管理等问题,Java引入了 [对象缓存] ,通过将那些比较常用的对象缓存起来,这样就不必每次都重新创建一个新的对象,而只需将已缓存对象返回即可。这种缓存策略在Boolean、Byte、Char、Integer、Long中都有体现,今天我们看看缓存在Integer中是如何实现的:

#Integer.java
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() {}
    }

从以上源码中可以看出,Integer静态内部类 [IntegerCache] 默认将-128~127之间的整数对应的对象缓存在cache数组中,其中最大整数high可以通过JVM启动参数+XX:AutoBoxCacheMax=size修改,这样我们就可以根据应用实际情况灵活地调整来提高性能。
再回到valueOf(int i)方法,如果参数i的值在[IntegerCache.low, IntegerCache.high]之间,则返回IntegerCache.cache数组中对应的对象,否则,通过Integer构造器创建新的对象后返回。
以上就是Integer自动装箱的实现原理:编译器编译Integer i1 = 1语句时,通过调用Integer.valueOf(int i)方法实现自动装箱。

自动拆箱


再看以下代码:

public class IntegerTest {
    public static void main(String[] args) {
        Integer i1 = 1;
        int i2 = i1;  //why
        System.out.println(i2);
    }
}

这又是什么鬼?为什么Integer对象可以赋值给基本数据类型int? 同样,我们通过javap工具反编译IntegerTest.class,截图如下:

Integer缓存机制、装箱拆箱实现原理_第2张图片
image

从以上反编译结果可以看到,编译器在编译int i2 = i1语句时,调用了Integer.intValue()方法

#Integer.java
public int intValue() {
        return value;
}

intValue()方法返回了对象的value值。

#Integer.java
private final int value;

public Integer(int value) {
        this.value = value;
}

以上就是Integer自动拆箱的实现原理:编译器编译int i2 = i1时,通过调用Integer.intValue()方法实现自动拆箱。

缓存机制


再看以下代码:

public class IntegerTest {
    public static void main(String[] args) {
        Integer i1 = 1;
        Integer i2 = 1;

        Integer i3 = 128;
        Integer i4 = 128;

        System.out.println(i1 == i2);  //true
        System.out.println(i3 == i4);  //false
    }
}

从自动装箱我们知道Integer将常用整数[-128~127]缓存在IntegerCache.cache对象数组中,所以引用i1和i2指向同一个对象,即i1 == i2;i3和i4的值128没有缓存在cache数组中,所以每次调用Integer.valueOf(int i)方法时,都会重新创建一个对象,因此引用i3和i4指向不同的对象,即i3 != i4。

欢迎留言交流

你可能感兴趣的:(Integer缓存机制、装箱拆箱实现原理)