从内存角度说:除去String,==是比较两个对象的地址,也就是栈中的内容是否相同;equals则是比较两个对象堆中的内容是否相同。
从实用角度说:
==对于基本数据类型来说,比较的是左右两边值否相等。
==对于引用类型来说,比较左右两边的引用是否指向同一个对象。
从内存角度理解难一些,从实用角度理解很容易。
8种基本数据类型的数据是存储在栈中,栈里的数据是共享的。
引用类型数据,引用是存储在栈中,对象而存储在堆中。
(关于java各种数据类型在堆栈里怎么存储的问题,可以参考本人的另一篇博文,里面有详细的说明,地址:http://blog.csdn.net/onezg/article/details/53079886)
举几个例子说明一下:
int a = 1;
int b = 1;
int c = 2;
System.out.println(a == b);
System.out.println(a == c);
结果为true、false
int a=1; 这行代码会在栈里开辟一块内存,把1存进去,a指向1,int b=1; 这行代码会在栈里寻找有没有1,发现有就会直接使用(栈里的数据共享),b也指向了1,也就是说a和b的地址相同,a==b 比较的是a和b的地址,所以为true。int c=2; 这行代码在栈里寻找2这个数据,发现没有,就会新开辟一块内存,把新值2存进去,c指向了2。a==c, a和c的地址不同,所以为false。
public Class Peron {
private int IdCardNumber;
private String name;
//构造方法省略了
//...
}
Person p1 = new Person("1", "onezg");
Person p2 = new Person("1", "onezg");
System.out.println(p1 == p2);
System.out.println(p1.equals(p2));
结果为false、false
new创建的对象都是放在堆里,两个对象都放在堆里,而p1和p2两个引用对象都放在栈里,p1和p2指向了两个不同的对象,所以p1和p2地址肯定不同。==比较了p1和p2的地址是否相同,所以结果为false。
为什么p1.equals.(p2)也为false呢?那是因为Person类没有实现equals方法,p1.equals(p2)的这个equals方法实际上是调用了Object类里的equals方法(在Java里所有默认每一个类都继承Object类),Object类里equals方法等价于==(不妨去看看Object源码),所以结果也是false。
equals要想为true,Person类得实现自己的equals,好让Java知道比较的是什么,需要重写equals和hashCode方法(一般都用IDE生成)。
在==和equals区别方面,在Java里有个类特别,那就是String,这里单独提出来说一下。为什么说它特别呢?那就是在JVM设定里有个字符串常量池的概念。
先说字符串常量池的工作原理:
当代码中出现字面赋值方式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
String有字面值方式赋值和new方式赋值两种。
第1种,字面值方式赋值。
String a = "hello";
String b = "hello";
System.out.println(a == b);
System.out.pringlnt(a.equals(b));
结果为true、true
String a=”hello”,JVM在字符串常量池里没有找到内容为hello的字符串对象存在,那么会创建这个字符串对象,然后将刚创建的对象的引用放入到字符串常量池中,并且将引用返回给a。Strring b=”hello”,JVM通过查找字符串常量池,发现内容为hello字符串对象存在,于是将已经存在的字符串对象的引用返回给变量b。a和b的地地址相同,所以a==b为true。
a.equals(b)比较的a和b的内容是否相同,可看JDK源码里String的equals方法,结果为true。
第2种,new方式赋值。
这个没什么说的,和new普通对象一样。
String a = new String("world");
String b = new String("world");
System.out.println(a == b);
System.out.println(a.equals(b));
结果为false、true
这个就不多说了,和上面new Person那段一样。