美文网首页
Java基础——包装类缓存

Java基础——包装类缓存

作者: zhangyaqi | 来源:发表于2020-08-04 13:01 被阅读0次

    在开始之前,我们先来看一下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=来设置。

    总结


    • Java的包装类默认缓存了true、false、-128~127、
    • 包装类对象之间进行相等判断时,要使用equals方法,决不能使用“==”
    • 可以通过JVM启动参数-XX:AutoBoxCacheMax=<size>来设置整数的缓存上限

    相关文章

      网友评论

          本文标题:Java基础——包装类缓存

          本文链接:https://www.haomeiwen.com/subject/hhslrktx.html