Integer Long缓存知识

我们都知道Integer是一个int类型的包装类,具有缓存-128-127.

这里通过源代码展示下为什么是这个区间的缓存值

/**
     * 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 {@code -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
     * jdk.internal.misc.VM class.
     *
     * WARNING: The cache is archived with CDS and reloaded from the shared
     * archive at runtime. The archived cache (Integer[]) and Integer objects
     * reside in the closed archive heap regions. Care should be taken when
     * changing the implementation and the cache array should not be assigned
     * with new Integer object(s) after initialization.
     */

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    h = Math.max(parseInt(integerCacheHighPropValue), 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(h, 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
            CDS.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 i = 0; i < c.length; i++) {
                    c[i] = new Integer(j++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
/**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    @IntrinsicCandidate
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

这里的意思是JLS的要求缓存是-128-127,并且为自动装箱和自动拆箱服务

也可以通过

-XX:AutoBoxCacheMax=

-Djava.lang.Integer.IntegerCache.high=

不过这样只能修改最大值而不是最小值

接下来我们看一下Long类型的缓存机制

private static class LongCache {
        private LongCache() {}

        static final Long[] cache;
        static Long[] archivedCache;

        static {
            int size = -(-128) + 127 + 1;

            // Load and use the archived cache if it exists
            CDS.initializeFromArchive(LongCache.class);
            if (archivedCache == null || archivedCache.length != size) {
                Long[] c = new Long[size];
                long value = -128;
                for(int i = 0; i < size; i++) {
                    c[i] = new Long(value++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
        }
  }
/**
     * Returns a {@code Long} instance representing the specified
     * {@code long} value.
     * If a new {@code Long} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Long(long)}, as this method is likely to yield
     * significantly better space and time performance by caching
     * frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  l a long value.
     * @return a {@code Long} instance representing {@code l}.
     * @since  1.5
     */
    @IntrinsicCandidate
    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);
    }

这里则并没有提及JLS的要求缓存,而注释也说明了设计这个缓存的目的,用于提高构造Long类型性能

与上方integer的缓存机制相比,Long的缓存机制较为简单而且也并不能上调大小

接下来让我们看一下Character的缓存机制

private static class CharacterCache {
        private CharacterCache(){}

        static final Character[] cache;
        static Character[] archivedCache;

        static {
            int size = 127 + 1;

            // Load and use the archived cache if it exists
            CDS.initializeFromArchive(CharacterCache.class);
            if (archivedCache == null || archivedCache.length != size) {
                Character[] c = new Character[size];
                for (int i = 0; i < size; i++) {
                    c[i] = new Character((char) i);
                }
                archivedCache = c;
            }
            cache = archivedCache;
        }
    }

    /**
     * Returns a {@code Character} instance representing the specified
     * {@code char} value.
     * If a new {@code Character} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Character(char)}, as this method is likely to yield
     * significantly better space and time performance by caching
     * frequently requested values.
     *
     * This method will always cache values in the range {@code
     * '\u005Cu0000'} to {@code '\u005Cu007F'}, inclusive, and may
     * cache other values outside of this range.
     *
     * @param  c a char value.
     * @return a {@code Character} instance representing {@code c}.
     * @since  1.5
     */
    @IntrinsicCandidate
    public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

这里则显示Character是使用[0,127]作为缓存,想想也知道单个字符并没有一个负数作为对应字符

同样的也是一种空间换时间的缓存机制

接下来让我看看Short的缓存机制

private static class ShortCache {
        private ShortCache() {}

        static final Short[] cache;
        static Short[] archivedCache;

        static {
            int size = -(-128) + 127 + 1;

            // Load and use the archived cache if it exists
            CDS.initializeFromArchive(ShortCache.class);
            if (archivedCache == null || archivedCache.length != size) {
                Short[] c = new Short[size];
                short value = -128;
                for(int i = 0; i < size; i++) {
                    c[i] = new Short(value++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
        }
    }

    /**
     * Returns a {@code Short} instance representing the specified
     * {@code short} value.
     * If a new {@code Short} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Short(short)}, as this method is likely to yield
     * significantly better space and time performance by caching
     * frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  s a short value.
     * @return a {@code Short} instance representing {@code s}.
     * @since  1.5
     */
    @IntrinsicCandidate
    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);
    }

这里与拉到上方的Long类型是差不多的缓存机制,这里就不多赘述

接下来看一下Boolean的缓存机制

/**
     * Returns a {@code Boolean} instance representing the specified
     * {@code boolean} value.  If the specified {@code boolean} value
     * is {@code true}, this method returns {@code Boolean.TRUE};
     * if it is {@code false}, this method returns {@code Boolean.FALSE}.
     * If a new {@code Boolean} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Boolean(boolean)}, as this method is likely to yield
     * significantly better space and time performance.
     *
     * @param  b a boolean value.
     * @return a {@code Boolean} instance representing {@code b}.
     * @since  1.4
     */
    @IntrinsicCandidate
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

    /**
     * Returns a {@code Boolean} with a value represented by the
     * specified string.  The {@code Boolean} returned represents a
     * true value if the string argument is not {@code null}
     * and is equal, ignoring case, to the string {@code "true"}.
     * Otherwise, a false value is returned, including for a null
     * argument.
     *
     * @param   s   a string.
     * @return  the {@code Boolean} value represented by the string.
     */
    public static Boolean valueOf(String s) {
        return parseBoolean(s) ? TRUE : FALSE;
    }

可以从源码中看到,Boolean并没有缓存机制

补充知识

我通常喜欢判空使用Objects.equals()

偶然在公众号上发现这个有坑

这里做一个记录

var integer = new Integer(1);
    long l = 1L;
    System.out.println(Objects.equals(integer, l));
    System.out.println(Objects.equals(integer, aLong));

这里我们使用Objects.equals()判断

结果都为false

Integer的1和long类型或者Long的1是否相等

/**
     * Returns {@code true} if the arguments are equal to each other
     * and {@code false} otherwise.
     * Consequently, if both arguments are {@code null}, {@code true}
     * is returned.  Otherwise, if the first argument is not {@code
     * null}, equality is determined by calling the {@link
     * Object#equals equals} method of the first argument with the
     * second argument of this method. Otherwise, {@code false} is
     * returned.
     *
     * @param a an object
     * @param b an object to be compared with {@code a} for equality
     * @return {@code true} if the arguments are equal to each other
     * and {@code false} otherwise
     * @see Object#equals(Object)
     */
    public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

这里拿出JDK7加入的Objects.equals()方法,然后发现这个方法会调用参数本身的equals方法,这里我们拿出Integer的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;
    }

这里会先进行类型的判断,所以直接就会返回false

Q.E.D.