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.