缓存
包装类为了提高性能,会将最常用范围的基本数值对应的包装类进行缓存,通常使用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.low
和IntegerCache.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至127IntegerCache.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);
}
这两个包装类在其方法注释中都有写使用缓存,但实际上的实现并没进行缓存操作。
猜测这两个类型由于精度问题不太好做缓存,所以干脆就不做了,而这个方法可能是为了保持风格统一