2007年1月1日星期一

Java_001:辨析“==”与equals

结论1:只有想确定两个比较对象是否指向同一对象时,才使用“==”。
结论2:只有想确定两个比较对象是否逻辑上相等,而不是它们是否指向同一对象时,才使用“equals”,通常适合于“值类”的情形。

1. 何时重写(override)equals方法?
由于Object类具有非final方法equals,内容如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看出,默认的equals方法实现,是想确定两个比较对象是否指向同一对象。也就是说和“==”的效果一样。因此,一般来说,开发者都要重写该方法。 比如String类就重写了equals方法如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
} if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
简单推论就可以得出,对于那些永远只有一个对象实例的类,就不需要重写该方法了。

2. 关于String类的“==”和equals方法编译、运行下面这个程序:
public class Test {
public static void main(String[] args) {
String s11 = "He";
String s12 = "llo";
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = "He" + "llo";
String s5 = s11 + s12;
System.out.println("===s1 s2===");
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
System.out.println("===s1 s3===");
if (s1 == s3)
System.out.println("s1 == s3");
else
System.out.println("s1 != s3");
if (s1.equals(s3))
System.out.println("s1 equals s3");
else
System.out.println("s1 not equals s3");
s3 = s3.intern();
System.out.println("===after s3.intern()===");
if (s1 == s3)
System.out.println("s1 == s3");
else System.out.println("s1 != s3");
if (s1.equals(s3))
System.out.println("s1 equals s3");
else
System.out.println("s1 not equals s3");
System.out.println("===s1 s4===");
if (s1 == s4)
System.out.println("s1 == s4");
else
System.out.println("s1 != s4");
if (s1.equals(s4))
System.out.println("s1 equals s4");
else
System.out.println("s1 not equals s4");
System.out.println("===s1 s5===");
if (s1 == s5)
System.out.println("s1 == s5");
else
System.out.println("s1 != s5");
if (s1.equals(s5))
System.out.println("s1 equals s5");
else
System.out.println("s1 not equals s5");
}
}




运行结果如下:

===s1 s2===
s1 == s2
s1 equals s2
===s1 s3===
s1 != s3
s1 equals s3
===after s3.intern()===
s1 == s3
s1 equals s3
===s1 s4===
s1 == s4
s1 equals s4
===s1 s5===
s1 != s5
s1 equals s5


原因解释:
(1)s1与s2
Sting是final类,程序运行的时候会创建一个字符串缓冲池,当使用 s2 = "Hello" 这样的表达是创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,而执行s1 = "Hello"时,s1先被放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1,s1与s2指向相同的对象"Hello"。因此s1==s2,且s1 equals s2。
(2) s1与s3
S3 用new String()的方式创建,这明确告诉JVM,我需要一个新的字符串对象,于是一个新的"Hello"对象被创建,与字符串缓冲池中的"Hello"对象不是同一个对象。因此s1!=s3,但s1 equals s3。而在使用String.intern()方法后,会将该字符串放入String缓冲池,并返回该对象引用。因此此时,s1==s3,且s1 equals s3。
(3) s1与s4
"He"+"llo",两个常量字符串相加后,创建了一个新的字符串常量,这个工作是编译期间完成的,因此s1==s4,且s1 equals s4。
(4) s1与s5
s5是在运行期间创建的。因此s1 != s5,但s1 equals s5。

结论3:字符串的相等比较应该用equals,而不要用==。

没有评论: