Integer
类在对象中包装了一个基本类型 int
的值。Integer
类对象包含一个 int
类型的字段。
此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。
在Integer
源码中,可以找一个一个名为IntegerCache
的私有静态内部类,这个类是用来实现Integer的缓存常量池的。
// Integer类中私有的静态类 承载cache的实现
private static class IntegerCache {
static final int low = -128;// 最小支持为-128
static final int high;//最大支持
static final Integer cache[];// 用来装载缓存 常量池
static {
// -128~127 这个范围的整数值是使用最广泛的
int h = 127;
// Java6中可以通过调整JVM启动参数来设置最大值
// 根据应用程序的实际情况 灵活的调整来提高性能
// JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改最大值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
// 获取较大者
i = Math.max(i, 127);
// 设置最大值不能超过Inter.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// 如果该值配置错误则忽略该参数配置的值,使用默认范围-128~127
}
}
high = h;
// 初始化数组容量为127 + 128 + 1(以默认区间为参考)
cache = new Integer[(high - low) + 1];
int j = low;
// 缓存通过for循环来实现,创建范围内的整数对象并存储到cache数组中
// 程序第一次使用Integer的时候需要一定的额外时间来初始化该缓存
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-127之间,无论是采用Integer a=Integer.valueOf(127)
还是Integer a=127
,都会在缓存常量池中获取到提前创建好的对象。
所以只要值相同,它们获取到的对象是同一个对象,因此地址也相同。
为什么
Integer a=127
也适用呢?
像上面这样将Integer
类型的a
直接赋值给127,Java会通过自动装箱机制,将int类型转化为Integer类型。
自动装箱机制,其实也就是编译期间,编译器自动调用Integer
类中的valueOf
方法
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
查看valueOf
源码不难看出,首先会判断参数i
是否在缓存常量池的最小值low
和最大值high
之间,如果是,
就会返回常量池中的对应对象。
否则,就会创建新对象。
public int hashCode() {
return Integer.hashCode(value);
}
public static int hashCode(int value) {
return value;
}
Integer
重写了hashCode
方法,调用hashCode
方法返回值为本身对象的value。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
public int intValue() {
return value;
}
Integer
重写了equals
方法
调用equals
方法,首先会判断参数Object obj
是否是Integer
类型
如果是,则会将obj
强转成Integer
,然后取其对象中的value
,跟自己的value
进行判断。
如果不是,则返回false
两个Integer对象进行比较
public static void main(String[] args) {
Integer a = new Integer(66);
Integer b = new Integer(66);
System.out.println(a == b);
}
因为a和b均为新new出来的对象,并且==
比较的是地址,两个new出来的对象永远不可能相等,因此输出false
两个Integer对象使用equal方法进行比较
public static void main(String[] args) {
Integer a = new Integer(66);
Integer b = new Integer(66);
System.out.println(a.equals(b));
}
上面有说过使用equal
方法,如果两个比较的对象均为Integer
,则比较它们的value
是否相等,很明显,它们的value
相等,所以是true
。
基本类型和Integer类型 通过 == 比较
public static void main(String[] args) {
Integer a = new Integer(66);
int b = 100;
System.out.println(a == b);
}
这里,基本类型和Integer
类型比较,Java会先自动进行拆箱操作,也就是将包装类型转化为基本类型,然后再进行比较,所以比较的是两个int值是否相同,答案是true
在缓存范围内的比较
public static void main(String[] args) {
Integer a = 66;
Integer b = 66;
System.out.println(a == b);
}
对于这种直接将两个int
类型赋值给Integer
,Java会先进行自动装箱,也就是调用valueOf
方法。
由于66在[-127,128]范围内,也就是默认的缓存范围,所以这里不会新创Integer
对象,而是生成的对象引用都会指向Integer内部的常量池中对应的值的对象。所以两个对象为同一对象。因此答案是true
。
在缓存范围外的比较
public static void main(String[] args) {
Integer a = 128;
Integer b = 128;
System.out.println(a == b);
}
同样的,Java会进行装箱操作,但是由于值不在[-127, 128]范围内,因此valueOf
方法会返回一个new Integer(i);
,因此两个对象并不是指向常量池对应的值的对象,故不是同一对象。答案是false
new生成的Integer对象与直接赋值的Integer对象的比较
public static void main(String[] args) {
Integer a = new Integer(66);
Integer b = 66;
System.out.println(a == b);
System.out.println(a.equals(b));
}
首先,a是new出来的对象,而b是指向Integer内部的常量池中对应的值的对象。
两者明显不是一个对象,只是两个对象的value相同而已。
所以答案是false
和true
首先,不仅仅是Integer
类型有缓存常量池这个说法。
Byte,Short,Integer,Long,Character都有缓存常量池。
其次,开发中应该尽量避免多次装箱开箱操作。
int值直接存放于内存栈中,虽然数值操作不是原子性的,但是在单线程的存取操作中,开销小,所以速度快很多,不像Integer**,还需要牵扯对象头的内存存储以及其他操作,更加的消耗性能**。