Java基础——包装类缓存

2020-08-04  本文已影响0人  zhangyaqi

在开始之前,我们先来看一下Java语言规范(JLS7-5.1.7)中的一小段内容:

If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f, or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

大致意思:

对boolean类型的值、byte类型的值、\u0000到\u007f(含)范围内的值、以及-128到127(含)范围内的int或者short类型的值进行自动装箱操作时,需要保证对字面量p进行任意两次装箱操作得到的对象r1和2都满足r1==r2。

结合上边的Java语言规范,我们来猜测一下下面代码的执行结果


    Integer num = 127;
    Integer num2 = 127;
    System.out.println(num == num2);
    System.out.println(num.equals(num2));

    Integer num5 = new Integer(127);
    System.out.println(num == num5);
    System.out.println(num.equals(num5));

    Integer num3 = 128;
    Integer num4 = 128;

    System.out.println(num3 == num4);
    System.out.println(num3.equals(num4));

这段代码的执行结果如下:


    true
    true
    false
    true
    false
    true

问题来了,为什么表示同样数字的Integer对象,在使用“==”和equals进行判断时,得出的结果确实如此的多变?其实这主要是因为Java的自动装箱和包装类缓存的共同作用引起的。自动装箱在的文章已经做了说明这里不再赘述,不清楚的朋友可以移步(这里)。

什么是包装类缓存呢 🤔?


还记得文章开头引用的一部分Java语言规范吗,包装类缓存就是为了实现这部分的语言规范而设计的。除了Float和Double外,其他的包装类内部都实现了针对其表示的基本类型的缓存机制,我们这里以Integer包装类的缓存为例来进行分析。下面我来一起看下自动装箱时调用的Integer.valueOf(int) 方法:


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

从上面的源码中我们可以看出,当定义的整数在一定范围内时,不论在什么地方定义,实际上返回的都是IntegerCache中初始化好的同一个对象。只有在整数值超出这个数组范围或者直接调用构造函数时,才会生成新的Integer对象。那么这个缓存的数值范围到底有多大呢,我一起来看一下。

首先我们来看一下IntegerCache的源代码:


    static final int low = -128;
    static final int high;
    static final Integer[] cache;
    static Integer[] archivedCache;

    static {
        int h = 127;
        String integerCacheHighPropValue = 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
                // 数组的大小上限为Integer.MAX_VALUE,缓存数值的最大值为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;

        // Load IntegerCache.archivedCache from archive, if possible
        VM.initializeFromArchive(IntegerCache.class);
        int size = (high - low) + 1;

        // Use the archived cache if it exists and is large enough
        if (archivedCache == null || size > archivedCache.length) {
            Integer[] c = new Integer[size];
            int j = low;
            for (int k = 0; k < c.length; k++)
                c[k] = new Integer(j++);
            archivedCache = c;
        }
        cache = archivedCache;
                // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

通过代码中for循环的部分可以看出,IntegerCache的缓存范围时通过参数low和high来控制的。而参数low定义为静态常量且值为-128,也就是说缓存的下限是无法修改;参数high则取127和java.lang.Integer.IntegerCache.high两者中的较大值(最大不能超过2147483518),想要自定义java.lang.Integer.IntegerCache.high的话,可以通过JVM启动参数-XX:AutoBoxCacheMax=来设置。

总结


上一篇下一篇

猜你喜欢

热点阅读