Java中的 == 和 equals 到底有什麼差別?
-
提到Java的 == 和 equals,很多人就會說 == 比較的值(位址), equals比較内容,這句話到底對不對?
equals是object類的一個方法,定義如下:
public boolean equals(Object obj) {
return (this == obj);
}
我們看到,當調用equals時, 實際還是調用了 == ,而 == 操作符 當且僅當 左右兩端的值相等時傳回true,那麼我們常說的equals比較内容又有錯嗎?錯在哪裡?
實際上我們口中說的比較内容大都是說String類,String類重寫了Object的equals方法,定義如下:
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;
}
我們看到,String的equals方法同樣先 == 判斷,如果值相等傳回true,否則将比較對象轉成String(此處講解String比較),而後比較它們的字元序列,這就是常說的“内容”, equals方法相對簡單,不做過多講解。
2. 為什麼Java中字元串比較都用equals而不用 ==
上面已經講到字元串的equals方法,幾個字元串 == 比較的示例代碼,讀者可以不看答案自己心裡先思考一下是否相等,看看自己是否了解了 字元串的 == 操作。
public static final String str0 = “abcde”;
public static void main(String[] args) {
String strObj = new String(“abc”);
String strObj1 = new String(“abc”);
String strObj2 = new String(“de”);
String str1 = “abc”;
String str2 = “de”;
final String str3 = “abc”;
final String str4 = “de”;
System.out.println(strObj == strObj1);//false
System.out.println(strObj.intern() == strObj1.intern()); //true
System.out.println((strObj1 + strObj2) == str0); //false
System.out.println((str1 + str2) == str0);//false
System.out.println((str1 + str2).intern() == str0);//true
System.out.println((str3 + str4) == str0);//true
System.out.println((str3 + str4).intern() == str0); //true
首先 new String 操作會在堆上建立一個String對象,若字元串常量池中沒有此字元串則也會在常量池中放一份,這裡也有一道面試題String a = “a”;和String a = new String(“a”)有什麼差別,差別就是前者建立一個後者則會建立兩個, 是以這裡第一個就會傳回false。
Java中的 == 和 equals 差別 String.intern方法,如果常量池中已有此對象則傳回該對象的引用, 如果沒有則在常量池中放一份并傳回該引用。是以,strObj.intern()時常量池沒有,會放入“abc”,并傳回該引用,而strObj1.intern()時常量池中已有該字元串,則直接傳回該引用,是以位址值相等,傳回true。
第三行,被 String類對+操作符的重載導緻字元串相加實際上是建立了StringBuilder對象的append方法之後調用ToString()實作的,是以等同于:
new StringBuilder().append(strObj1).append(strObj2).toString(),顯然是false。
第四行,和第三行一樣,傳回false。
第五行,有了前面的基礎,第五行可以看作是"abcde".intern() == “abcde”,這樣傳回true就不難了解了,因為字元串”abcde“已經存在,直接傳回引用。
第六行,這裡注意,與前面的差別是final修飾符,被final修飾的不可變量會在編譯期優化掉,是以第六行編譯期間就會"知道" str3 + str4實際上是"abcde"。
第七行在第六行的基礎上已經是顯而易見了。
實際上,字元串字面量相加也會被編譯優化,例如 String test = “abc” + “de”;也會在編譯期被認為String test = “abcde”;是以,如果聲明了這個test,則test == str0和 (“abc” + “de”)== str0都會傳回true。