==
可以用于基本类型和引用类型的比较,比较基本类型时,是比较其值是否相同;比较引用类型时,是比较其是否引用了同一对象(其实也是比较其值是否相同,这里的值是对象的地址)。例如:
int a = 1;
System.out.println(a == 1); // true
String s1 = "hello";
String s2 = s1;
System.out.println(s1 == s2); // true
String s3 = "hello";
String s4 = new String("hello");
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
其中s1 == s3
结果为true
,这个与JVM工作机制有关,后面会解释。
equals()
用于比较两个对象的内容是否相同,例如:
String s1 = "hello";
String s4 = new String("hello");
System.out.println(s1 == s4); // false
System.out.println(s1.equals(s4)); // true
equals()
是基类Object
的一个方法,其默认实现如下(摘自java SDK源码),可见默认调用==
来比较。
public boolean equals(Object obj) {
return (this == obj);
}
如果没有override该方法,那么就退化为和==
相同的功效了。但是一般都会override该方法,用来比较两个对象内容是否相同,例如String
的equals()
会比较字符串序列是否相同,所以,如果要比较对象内容是否相同用equals
,对于自定义对象,根据需要override该方法。
下面代码中,字符串s1 == s3
为true
,表示他们都引用了相同对象,这与new String("hello")
差异在哪里呢?
String s1 = "hello";
String s3 = "hello";
String s4 = new String("hello");
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
JVM中有一个称为”字符串池”的共享区域,用来保存字符串常量,每当创建字符串常量时,会先查找字符串池,如果找到返回其引用,如果不存在则创建一个新的字符串,并添加到字符串池中。注意:该规则仅针对字符串常量,对于通过new
创建的字符串没有该规则。
字符串常量,包括字面值(literal strings)和常量表达式(string-valued constant expressions),例如:
String s1 = "hello"; // 字符串常量
String s2 = "h" + "e" + "l" + "l" + "o"; // 字符串常量
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2)); // true
String s3 = "hello" + s1; // 非字符串常量
s1 = "hello"
由于是字符串常量,创建时又不存在,所以创建时会被添加到字符串池中,当创建s2
时,由于s2
也是一个字符串常量(虽然是拼接而成,但每个组成部分都是常量,在编译时就可以确定,所以其也是字符串常量),其创建时会先查找字符串池,找到并返回引用,该引用与s1
相同,所以s1 == s2
比较为true
,自然equals()
比较也为true
。
可见定义多个相同的字符串常量值,其只占用一份内存空间,只会被创建一次,性能上好些。
最后一起看看java.lang.String
类的equals()
的源码,主要看下其实现原理和方法,方便我们为自定义类重写equals()
方法时提供参考。
// java/lang/String.java
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
首先判断引用是否相同,如果引用相同,那么内容必然相同,直接返回true
。接下来判断比较对象是否是String
类的一个实例,这样做是为了保证被比较对象类型一致(如果比较的两个对象是继承关系,这里也视作类型不同),类型一致后,比较字符串的长度,如果长度相同则比较字符串的每个字符,如果每个字符都相同返回true
,对于其他情况都返回false
。
通过前面的学习,对equals()
有了一些了解,下面通过为自己的类重写equals()
方法来巩固一下。代码如下:
class MyInteger {
private int mValue;
public MyInteger(int v) {
mValue = v;
}
public int getValue() { return mValue; }
public int hashCode() { return mValue; }
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MyInteger) {
MyInteger i = (MyInteger)obj;
if (mValue == i.getValue()) {
return true;
}
}
return false;
}
}
public class Test {
public static void main (String [] args) {
MyInteger i1 = new MyInteger(100);
MyInteger i2 = new MyInteger(100);
System.out.println(i1 == i2); // false
System.out.println(i1.equals(i2)); // true
}
}
MyInteger
是一个很简单的类,其只包含一个int
类型的成员变量,在equals()
中,我仿照java.lang.String
类,先判断引用是否相同,如果相同直接返回true
,如果引用不相同,再判断被比较对象是否是MyInteger
类型,如果是再比较成员变量mValue
是否相同,如果相同返回true
,其他情况返回false
。
也许你注意到了,我还定义了一个hashCode()
成员函数,这是JDK中要求的,查看java.lang.Object
中equals()
方法说明,其中提到重写equals()
方法时,一般也需要重写hashCode()
(我感觉是必须啊,不然编译不过–!)。而且要保证如果两个对象使用equals()
比较相同,则被比较的两个对象的hashCode()
返回值也必须相同,反之亦然。
在自定义的MyInteger
中由于只有一个成员变量,可以直接在hashCode()
的实现中直接返回该成员变量,这样可以保证上面所说的要求。
原文:http://blog.csdn.net/xiaohui_hubei/article/details/44538145