java对象相等问题

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,
b
c变成了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方法时才会出现!

你可能感兴趣的:(java,java之路)