Java包装类中的缓存

缓存

包装类为了提高性能,会将最常用范围的基本数值对应的包装类进行缓存,通常使用cache内部类的static静态代码块进行缓存填充操作,valueOf()中存在缓存读取操作。我们通常定义包装类型时Integer a = 1,其默认就是使用的valueOf()方法,可自行通过javap进行反编译查看。

Integer a = 1;
Long b = 2l;
Character c = 'a';

javac编译,javap -c反编译

 public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: astore_1
       5: ldc2_w        #5                  // long 2l
       8: invokestatic  #7                  // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
      11: astore_2
      12: bipush        97
      14: invokestatic  #8                  // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      17: astore_3
      18: return

缓存带来的比较问题

我们知道java中的==比较的是对象地址,即是否是同一个对象,而equals为内容比较。
因此在包装类进行比较时,如果是在缓存范围内的包装类,使用==得到的是true
但若是落在缓存范围以外,则包装对象为新new出来的,使用==会得到false
因此,最好统一使用equals进行比较,包装类的该方法会将包装类进行拆包得到基础类型的值进行比较

public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }

Integer

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

我们可以看到Integer的缓存范围是IntegerCache.lowIntegerCache.high,我们进入IntegerCache中查看得知:

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

默认缓存范围:-128至127
IntegerCache.low的值固定为-128,而IntegerCache.high的值可以通过系统属性java.lang.Integer.IntegerCache.high或通过虚拟机启动指令-XX:AutoBoxCacheMax=<size>来手动指定,如果没有指定则默认为127,在静态初始化块中会进行填充操作。

Long

public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }

固定-128至127

Character

public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

缓存至127

Short

public static Short valueOf(short s) {
        final int offset = 128;
        int sAsInt = s;
        if (sAsInt >= -128 && sAsInt <= 127) { // must cache
            return ShortCache.cache[sAsInt + offset];
        }
        return new Short(s);
    }

也是固定-128至127

Boolean

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

由于布尔值只有这两种可能,因此直接全部缓存了。

Double和Float

/**
     * Returns a {@code Float} instance representing the specified
     * {@code float} value.
     * If a new {@code Float} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Float(float)}, as this method is likely to yield
     * significantly better space and time performance by caching
     * frequently requested values.
     *
     * @param  f a float value.
     * @return a {@code Float} instance representing {@code f}.
     * @since  1.5
     */
    public static Float valueOf(float f) {
        return new Float(f);
    }

这两个包装类在其方法注释中都有写使用缓存,但实际上的实现并没进行缓存操作。
猜测这两个类型由于精度问题不太好做缓存,所以干脆就不做了,而这个方法可能是为了保持风格统一

Java包装类中的缓存Java包装类中的缓存 Mutou_ren 发布了104 篇原创文章 · 获赞 9 · 访问量 1万+ 私信 关注
上一篇:类型强制转换


下一篇:char[] 的toString()和String.valueOf(char[])的区别