从Java 5开始,基础数据类型被赋予了自动装箱和自动拆箱机制。
问题
public static void main(String[] args) {
Integer a = , b = , c = , d = ;
System.out.print(a == b);//true
System.out.print(',');
System.out.println(c == d);//false
}
打印结果为
true,false
这是为什么呢?
原因
这是由于Integer在自动装箱过程中使用了缓存机制。
基础数据类型在自动装箱过程中会使用缓存机制来提高效率,在缓存范围内相同值的自动装箱对象相同(==为true),本文将总结基础数据类型在自动装箱过程中的缓存特点。
话不多说,先上结果:
类型 | 字节数 | 包装类型 | 缓存范围 | 说明 |
---|---|---|---|---|
byte | 1 | Byte | -128 ~ 127 | 全部缓存 |
short | 2 | Short | -128 ~ 127 | 部分缓存 |
int | 4 | Integer | -128 ~ 127 | 部分缓存 最大值默认为127 可以通过设定JVM启动参数 来修改缓存的最大值 |
long | 8 | Long | -128 ~ 127 | 部分缓存 |
char | 2 | Character | 0 ~ 127 | 部分缓存 |
boolean | 1或4 | Boolean | true, false | 全部缓存 boolean类型在编译后使用java虚拟机的int数据类型代替,boolean数组则被编码为byte数组 |
float | 4 | Float | 没有缓存 | |
double | 8 | Double | 没有缓存 |
分析
我们知道,在自动装箱过程中,使用了对应包装类的
valueOf
静态方法
例如:
Integer num = 100;
等价于
Integer num = Integer.valueOf(100);
1. int
Integer中自动装箱使用的valueOf函数定义如下:
public static Integer valueOf(int i) {
assert IntegerCache.high >= ;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看到在IntegerCache.low ~ IntegerCache.high之间的数字,将会从缓存中获取,在这个区间之外的数字,将创建一个新的Integer对象。
接下来再来看看IntegerCache的定义(它是定义在Integer内部的一个静态类):
/**
* 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 -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
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = ;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, );
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -);
}
high = h;
cache = new Integer[(high - low) + ];
int j = low;
for(int k = ; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
通过代码及注释,我们可以了解到以下几点信息:
- 在IntegerCache中定义了一个Integer数组
cache
- 在first usage(IntegerCache类被加载)时进行初始化
- 其缓存范围默认为从-128至127
- 可以通过
来控制-XX:AutoBoxCacheMax=<size>
的cache
size
所以,在默认情况下,-128~127范围内的自动装箱使用了缓存,相同的值自动装箱后的对象相同(即a == b 为true)。
延伸阅读: 为什么java中要做IntegerCache这种设置?
2. byte/short/long
byte/short/long对应的包装类的缓存方式与Integer不同,没有用到自定义Cache类,而是直接在包装类内部定义了一个长度为256的缓存数组:
2.1 byte
public static Byte valueOf(byte b) {
return VALUES[b + ];
}
private static final Byte[] VALUES = new Byte[];
static {
for (int i = -; i < ; i++) {
VALUES[i + ] = new Byte((byte) i);
}
}
2.2 short
public static Short valueOf(short s) {
return s < - || s >= ? new Short(s) : SMALL_VALUES[s + ];
}
private static final Short[] SMALL_VALUES = new Short[];
static {
for (int i = -; i < ; i++) {
SMALL_VALUES[i + ] = new Short((short) i);
}
}
2.3 long
public static Long valueOf(long v) {
return v >= || v < - ? new Long(v) : SMALL_VALUES[((int) v) + ];
}
private static final Long[] SMALL_VALUES = new Long[];
static {
for (int i = -; i < ; i++) {
SMALL_VALUES[i + ] = new Long(i);
}
}
3. char
char的包装类型Character的自动装箱缓存方式类似于byte/short/long,不过缓存长度只有128(0~127)
public static Character valueOf(char c) {
return c < ? SMALL_VALUES[c] : new Character(c);
}
private static final Character[] SMALL_VALUES = new Character[];
static {
for (int i = ; i < ; i++) {
SMALL_VALUES[i] = new Character((char) i);
}
}
4. boolean
boolean只有2个值:true和false,在源码中valueOf方法使用了2个常量来进行自动装箱
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
5. float/double
float和double都属于浮点型,自动装箱没有使用缓存,任何相同的值自动装箱后用==来判断都会返回false
5.1 float
public static Float valueOf(float f) {
return new Float(f);
}
5.2 double
public static Double valueOf(double d) {
return new Double(d);
}
总结
在使用基础数据类型时,我们需要注意自动装箱及自动拆箱的影响。特别是在使用==来进行判断的时候要注意自动装箱的缓存
参考文档:
- 官方文档 Primitive Data Types
- 你真的知道Java中boolean类型占用多少个字节吗?
- Java 7之基础类型第1篇 - Java数据类型