public class TestA {
private Integer a;
public Integer getA() {
return a;
}
public void setA(Integer a) {
this.a = a;
}
}
public class Test {
public static void main(String[] args) {
TestA a = new TestA();
a.setA(1);
TestA aa = a;
a.setA(3);
System.out.println(aa==a);
System.out.println(aa.getA());
}
}
输出结果是true和3,这理解起来很简单,java中对象直接"="赋值其实就是将引用传给对方,让他也指向同一个内存地址。
所以如果a改变了里面属性的值,那这个地址存储的内容就变了,当aa去拿的时候就是变了之后的,因为两个指向依然同一个地址,
所以aa==a是true的。
但如果这么来:
TestA a = new TestA();
a.setA(1);
TestA aa = a;
a = new TestA();
a.setA(3);
System.out.println(aa==a);
System.out.println(aa.getA());
那输出就是false和1了,因为a又重新new了一个对象,他引用的地址已经变了,就和aa一点关系都没有了!
以上我们都知道,但对于包装类却看似不一样:
public class Test {
public static void main(String[] args) {
Integer b = new Integer(5);
Integer c = b;
System.out.println(b==c);
b = b+1;
System.out.println(b);
System.out.println(c);
System.out.println(b==c);
}
}
以上的运行结果是:
true
6
5
false
这似乎和我们之前的例子不一样了。c和b都指向同一个地址,所以第一次bc是true的,但当b加1之后,c并没有跟着b加1,
bc变成了false,说明两个指向的地址已经不是同一个了。
一开始对这个可能无法理解,我们先看看下面的例子:
public class Test {
public static void main(String[] args) {
int x = 1;
int y = x;
x = x+1;
System.out.println(y==x);
}
}
输出是false,这个我们很容易知道,一开始x和y都是1,当x加1之后,x是2,y还是1,所以yx是false的,数值型的变量在判断时是直接比较他们的值的。
再回忆一下java的装箱和拆箱:
所谓装箱,就是把基本类型用它们相对应的引用类型包起来,使它们可以具有对象的特质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double,等等。
所谓拆箱,就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为值类型的数据。
JDK1.5之后可自动进行装箱和拆箱的操作,例子如下:
int i=10;
Integer j=Integer.valueOf(i); //手动装箱操作
int k=j.intValue();//手动拆箱操作
int i1=10
Integer j1=i1;//自动装箱操作
int k1=j1;//自动拆箱操作
通过上面的讲解应该就知道原因了:
Integer b = new Integer(5);
Integer c = b;
System.out.println(b==c);//现在两个指向同一个地址,是true的
b = b+1;//b是包装类,得拆箱成数值型,然后加1,变成数值6,然后把数值6再装箱变成包装类赋给b,等同于b=Integer.valueOf(b.intValue()+1);
//Integer.valueOf()会生成一个新的包装类对象(因为valueOf()方法的源码里写着,return的是new Integer(),源码见下)
System.out.println(b==c);//现在两个指向不同的地址,是false的
终于真相大白了,上面的问题告一段落,我自以为对包装类很懂了,但再看看下面的题目:
Integer b = 128;
Integer c = 128;
System.out.println(b==c);
输出是false,这很好理解,128是数值型的,再赋值给包装类变量时先要装箱,上面的等同于:
Integer b = Integer.valueOf(128);
Integer c = Integer.valueOf(128);
System.out.println(b==c);
这样很明显可以看成是false的,是两个不同的对象。
再看看这个:
Integer b = 100;
Integer c = 100;
System.out.println(b==c);
输出居然是true。小学生表示已经崩溃,是不是对java已经无爱了。但是我可不会就这样放弃,到底是什么原因呢?
查看Integer的valueOf方法的源码如下:
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
于是明白了,在【-128,127】之间的数字,valueOf()返回的是缓存中的对象,所以两次调用返回的是同一个对象。
也就是说Integer(100)这样的对象已经有了一个了,自动装箱之前就不去new了,直接使用缓存里的,所以是同一个对象,指向的是同一个引用地址。
以上代码等同于:
Integer b = Integer.valueOf(100);
Integer c = Integer.valueOf(100);
System.out.println(b==c);
所以输出是true。
接下来下面这个也很明白了吧:
Integer a = new Integer(100);
Integer b = 100;
System.out.println(a == b);
输出的是false,因为a是自己new的对象,不是通过valueOf获取的,没有放进缓存里,所以b在valueOf时还是会new一个
以上代码等同于:
Integer a = new Integer(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b);
这样就看的出来是false了。
总之以上的现象只会在拆装箱,或者你自己手动调用valueOf方法时才会出现!