前言:我们知道,缓存一些常用的数据能提高程序的运行效率,而在Java中,Java给一些基本类型提供了一个缓存池,缓存池中已经提前存进去了一些元素,提高数据的读取速度。在此之前,先了解一下Java的自动包装机制。
本文参考自CS-Notes中的java基础
拆箱装箱
基本类型要转换成包装类型,需要调用包装类型的静态valueOf()函数;而包装类型要转换成基本类型,需要调用以基本类型开头的Value(),比如 intValue() 。
在一般情况下,Java会自动帮你实现拆箱装箱,比如
Integer x = 2; // 装箱 实际是Integer x = Integer.valueOf(2)
int y = x; // 拆箱 实际是int y = intValue(x)
拆箱装箱详情可参考深入剖析Java中的装箱和拆箱(缓存池技术)
缓存池介绍
在使用这些基本类型对应的包装类型时,如果该数值范围在缓冲池范围内,就可以直接使用缓冲池中的对象。否则,就创建一个对象存储相应的数据。
各种基本类型的缓存池内容如下:
boolean values true and false //布尔类型中的两个取值 true和false
all byte values //byte类型所有数据,即-128~127
short values between -128 and 127 //short类型大小范围-128~127
int values between -128 and 127 //int类型大小范围 -128~127
char in the range \u0000 to \u007F //char类型所有数据,即所有字符
我们可以看一下Integer缓存池的源码(1.8版本的源码):
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 =
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() {}
}
在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax=<size> 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界。
创建包装类型的对象有两种方式,两种方法都可以将基本类型作为参数传入。两种方法的区别在于:
- new Integer(123) 每次都会新建一个对象
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
Integer m = Integer.valueOf(189);
Integer n = Integer.valueOf(189);
System.out.println(m == n); // false 189大于缓存池的最大值127 因此每次都会新建一个对象 他们的地址不同
再看看valueOf()方法的源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)]; //如果在缓存范围内,直接获取缓存中的地址
return new Integer(i); //否则新建一个对象
}
可以发现,他是先判断数值是否在缓存池的范围中,如果在就直接返回缓存池中的对象,如果不存在会直接返回一个 new 出来的新的对象。