我们先来看一段代码
public class Demo {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2);
Integer i3 = 1000;
Integer i4 = 1000;
System.out.println(i3 == i4);
}
}
执行一下:
诶,为什么两个输出的结果会不一样呢?不应该全是true或者全false吗?
然而一个是true,一个是false,这是为什么呢?代码在哪里出现了不一样的东西呢?
所以我们找到.class文件来反编译一下,根据Java编译机制, .java文件在编译后会生成 .class文件,交给JVM去加载执行
public class Demo {
public static void main(String[] args) {
Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf(100);
System.out.println(i1 == i2);
Integer i3 = Integer.valueOf(1000);
Integer i4 = Integer.valueOf(1000);
System.out.println(i3 == i4);
}
}
诶,果然发现编译器在编译我们的代码时动了一些手脚,它在我们声明的变量上加了valueOf()方法,那进一步来看一下valueOf()方法是造成不同输出结果的原因吗?
public static IntegervalueOf(inti) {
if (i >= IntegerCache.low &&i <= IntegerCache.high) //看一下是否在缓存的数组里
return IntegerCache.cache[i + (-IntegerCache.low)]; //如果在,直接取数组里相应的对象返回
return new Integer(i); //不在缓存的数组里,直接new一个对象返回
}
我们发现,Integer的作者在写这个类时,为了避免重复创建对象节省内存,对Integer值做了缓存,如果这个值还在缓存范围内,直接返回缓存好的对象,否则new一个新的对象类返回,那究竟这个缓存到底缓存了什么呢?我们去看IntegerCsche这个类:
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
/*
* 检查虚拟机里有没有相应的配置,如果有,取该值;如果没有,取默认的127
*/
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() {}
}
这是一个内部静态类,该类只能在Integer这个类的内部访问,这个类在初始化的时候,会去加载JVM的配置,如果有值,就用配置的值初始化缓存数组,否则就缓存-128到127之间的值。
再来看看之前的代码:
结论:我们在比较两个Integer对象时,无论是怎么声明的,都一定要使用equals取比较,不能用==。
引申:
8种基本类型的包装类和对象池
java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。以下是一些对应的测试代码:
public class Demo {
public static void main(String[]args) {
// 5种整形的包装类Byte,Short,Integer,Long,Character的对象,
// 在值小于127时可以使用常量池
Short s1= 127;
Short s2= 127;
System.out.println(s1==s2);// 输出true
// 值大于127时,不会从常量池中取对象
Short s3= 128;
Short s4= 128;
System.out.println(s3==s4);// 输出false
// Character
Character c1= 127;
Character c2= 127;
System.out.println(c1==c2);// 输出true
// 值大于127时,不会从常量池中取对象
Character c3= 128;
Character c4= 128;
System.out.println(c3==c4);// 输出false
// Boolean类也实现了常量池技术
Boolean bool1=true;
Boolean bool2=true;
System.out.println(bool1==bool2);// 输出true
// 浮点类型的包装类没有实现常量池技术
Double d1= 1.0;
Double d2= 1.0;
System.out.println(d1==d2);// 输出false
}
}
看一下Console: