今天发现了一个细节性的问题,随后自己查看源码以及一步步的分析,同时也百度别人的文章看看,所以做了下面的总结。
/* java自动装箱拆箱的细节性问题
*/
public class test {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);//1
System.out.println(a == 100);//2
System.out.println(c == d);//3
System.out.println(c == 128);//4
}
}
这是一个装箱拆箱的问题。说实话,自己拿不准,所以自己运行了。
“==”比较的是object的reference而不是value,自动装箱后abcd都是Integer这个Object,因此‘’==‘’比较的是其引用。按照常规的思维,1和3
都应该是false。但是其结果却是
true
true
false
true
结果2和4都应为ac进行了自动拆箱,因此其比较的是基本数据类型的比较,就跟int比较的时候是一样的。“==”在这里比较的是他们的值,而不是引用。
对于结果1,虽然比较的时候还是比较的是对象的reference,但是自动装箱的时候,java在编译时Integer a = 100;被翻译为Integer a =Integer.valueOf(100);关键这个valueOf的方法。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
根据上面的jdk源码,java为了提高效率,IntegerCache类中有一个数组缓存了值从-128到127的Integer对象。当我们调用Integer.valueOf(int i)的时候,如果i的值是>=-128且<=127时,会直接从这个缓存中返回一个对象,否则就new一个Integer对象。 具体如下:
static final Integer cache[] = new Integer[-(-128) + 127 + 1]; //将cache[]变成静态
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128); //初始化cache[i]
}
这是用一个for循环对数组cache赋值,cache[255] = new Integer(255-128),也就是newl一个Integer(127) ,并把引用赋值给cache[255],好了,然后是Integer b= 127,流程基本一样,最后又到了cache[255] = new Integer(255-128),这一句,那我们迷糊了,这不是又new了一个对象127吗,然后把引用赋值给cache[255],我们比较这两个引用(前面声明a的时候也有一个),由于是不同的地址,所以肯定不会相等,应该返回false啊!呵呵,这么想你就错了,请注意看for语句给cache[i]初始化的时候外面还一个{}呢,{}前面一个大大的static关键字,是静态的,那么我们就可以回想下static有什么特性了,只能初始化一次,在对象间共享,也就是不同的对象共享同一个static数据。
那么当我们Integer b = 127的时候,并没有new出一个新对象来,而是共享了a这个对象的引用,记住,他们共享了同一个引用!!!,那么我们进行比较a==b时,由于是同一个对象的引用(她们 在堆中的地址相同)自然也就是返回true了。下面我们再来分析一下如下的代码问题:
public class Test {
public static void main(String args[]){
Integer m = new Integer(5);
Integer n = new Integer(5);
System.out.println(m==n);
m = m-1;
n = n-1;
System.out.println(m==n);
}
} //输出什么呢??
false
true
原因:m,n因为都是new出来的对象,内存地址不一样,所以第一次m==n比较的reference不一样。
但是,m=m-1首先进行了自动拆箱m.intValue,相减后再进行装箱动作:m=Integer.valueOf(m.intValue-1),
而m和n都在 -128--127的范围,所以自动装箱后,根据上文所述,都是同一个object的引用。
因此第二次输出true。
再看一次(出自 java解惑)
public class TestWhile{
public static void main(String[] args){
Integer i=0;
Integer j=0;
Integer i=new Integer(0);
Integer j=new Integer(0);
while(i<=j & i>=j & i!=j){
System.out.println("0000");
}
}
}
那一行是拆箱?while循环里的条件看似不成立,可为什么可以运行(去掉第5、6行的注释后)?
解答这种情况下,循环不能运行对于Integer类型,<,<=,>,>=操作将导致拆箱操作,
也就是调用Integer的intValue()方法得到相应的基本类型值,然后比较。
但是,==,!=比较的,是对象的引用(Reference)。
Integer i = 0;
Integer j = 0;
这两句使用装箱操作,也就是调用Integer.valueOf(int i);注意,不是使用new。
由于0在 -128--127之间,根据上述,i和j引用的是同一个对象。
综上,i<=j & i>=j & i!=j中,
i<=j 和 i>=j都成立,而i!=j不成立,因为i和j引用的是同一个对象。
故此,循环不会执行。
注释掉3,4两句,使用5,6两句时,i和j引用的不是同一个对象,所以i!=j成立。i<=j & i>=j & i!=j成立,循环条件总是成立的,while (i <= j & i >= j & i != j)成为无穷循环,不断输出。
原题:
循环者的诅咒
请提供一个对i的声明,将下面的循环转变为一个无限循环:
由于0在 -128--127之间,根据上述,i和j引用的是同一个对象。
综上,i<=j & i>=j & i!=j中,
i<=j 和 i>=j都成立,而i!=j不成立,因为i和j引用的是同一个对象。
故此,循环不会执行。
注释掉3,4两句,使用5,6两句时,i和j引用的不是同一个对象,所以i!=j成立。i<=j & i>=j & i!=j成立,循环条件总是成立的,while (i <= j & i >= j & i != j)成为无穷循环,不断输出。
原题:
循环者的诅咒
请提供一个对i的声明,将下面的循环转变为一个无限循环:
while (i <= j && j <= i && i != j) {
}
总结,对于要比较Object的值,最稳妥的方法还是调用equals()这个方法,而不是使用==,因此会比较其引用。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
大家可以看到,只要两个Integer的int value是相等的,equals方法总是返回true。