2010-09-26 208 views
4
public class Demo { 

    public static void main(String[] args) { 

     String s1 = "Hello"; 
     String s2 = "Hello"; 
     System.out.println("s1 == s2 " + (s1 == s2)); 

     String s5 = "Hel" + "lo"; 
     String s6 = "He" + "llo"; 
     System.out.println("s5 == s6 " + (s5 == s6)); 

     String s7 = "He"; 
     String s8 = "Hello"; 
     s7 = s7.concat("llo"); 
     System.out.println("s7 == s8 " + (s7 == s8)); 

     String s10 = "He"; 
     s10 = s10 + "llo"; 
     System.out.println("s1 == s10 "+(s1 == s10)); 
    } 
} 

在前面的代碼中,s7 == s8和s1 == s10給出false。有人可以解釋我,在這裏實際發生了什麼s7 = s7.concat(「llo」);和s10 = s10 +「llo」;我明白==運算符檢查引用和equal()檢查對象的內容。但我需要知道爲什麼s7和s10引用變量的位模式與s8和s1不同。如果這些東西與編譯時生成的字符串和運行時生成的字符串有關,那麼我怎樣才能確定它是編譯時還是運行時字符串?關於java中==運算符的問題

+0

你是什麼意思的「參考變量位模式」? – Cameron 2010-09-26 19:37:29

+0

String問題上的另一個'=='。似乎在這個相同的問題上的變化被頻繁地詢問。難道不應該有標準答案,並且所有後續事件都會被重複關閉嗎? – 2010-09-26 22:05:53

+0

@cameron我從一些書中學到了如果一個引用變量c引用了對象X017432並且引用變量d也引用了對象X017432,那麼c和d中的位就是相同的。所以我需要知道爲什麼s7和s10引用不等於已經引用字符串池中的「Hello」的s8和s10。 – ddfnfal 2010-09-27 06:46:52

回答

11

發生這種情況的原因是因爲Java正在編譯器中進行優化。當它看到你將字符串"Hello"分配給s1時,它對s2使用相同的「Hello」,因爲所有的Java String操作都是非破壞性的(例如它們返回一個克隆而不是修改原始),所以這是一個安全的事情要做。

同樣的事情發生在"Hel" + "lo" vs "He" + "llo";足夠聰明地弄清楚它們是一回事。

其他很複雜,它不能優化它們,因此你最終得到單獨的對象。

+5

「Hel」+「lo」'和「He」+「llo」的優化是[常量摺疊](http://en.wikipedia.org/wiki/Constant_folding)的結果。 – 2010-09-26 19:44:50

4

==不檢查位模式,它會比較對象的內存地址。只有同一個對象具有相同的內存地址。

+2

這是我的原始答案,這不是他要問的。 – 2010-09-26 19:38:58

+0

@ Clint Tseng:呃,你說的對,我的不好。馬克確實感到困惑,並認爲它必須以某種方式平等:p – 2010-09-26 19:43:06

0

等號運算符測試引用是否相同(即指向相同的對象),而不是引用的值是否相同。如果您需要測試一個字符串是否等於另一個字符串,則應使用內置的.equals方法。這將做一個對象值比較。例如

final String expectedValue = "Foo"; 
final String actualValue = "F" + "oo"; 
if (expectedValue.equals(actualValue)) { 
    // This will trigger where == would not 
} 

另外爲了安全起見,如果你做的比較兩個字符串,一個是恆定的,它通常是最好的調用等於在恆定的,即

String myValue = getMyValue; 
boolean theSame = "Some value".equals(myValue); 

不是

String myValue = getMyValue; 
boolean theSame = myValue.equals("Some Value"); 

原因是第二種形式存在空指針異常的風險,可以通過在保證存在的常量字符串上調用equals()來避免這種情況。

0

您無法對字符串對象進行任何假設。

虛擬機可以努力確保沒有兩個包含完全相同字符數組的字符串對象同時存在,而其他虛擬機允許重複。

0

==運算符僅檢查兩個對象是否具有相同的地址(指針)。只適用於不是引用的基本類型(如int,char等),它會比較值。

您需要使用類似s1.equals(s2)的內容來比較兩個字符串的內容。

0

在這個例子中,你提供的,這是發生了什麼事:

String s7 = 「He」; //s7 is an object referencing a part of memory holding 「He」 
String s8 = 「Hello」; //s8 is an object referencing a part of memory holding 「Hello」 
s7 = s7.concat(「llo」); //s7.concat(「llo」) created a new object in memory that contains 「Hello」 and s7 now references this now object 

(s7==s8)    //checks if the equality of the object reference and this is false since they reference different memory addresses. 

(s7.equals(s8))   //this will compare s7 and s8 however way the String class compares two String objects. 
4

克林特的回答是不錯,但我會在進一步擴大它在編譯器級別解釋。

如您所知,s1s2將最終引用相同的字符串實例"Hello"

對於s5s6,編譯器會看到常量表達式。也就是說,它看到兩個常量(字符串文字)之間的操作。編譯器知道如何添加字符串以及結果會是什麼。由於這些值是在編譯時立即知道的,因此會爲您添加新的值,從而生成字符串"Hello"。因此,它與s1s2具有相同的值,因此每個參數也會引用同一個實例。

s7不能用同樣的方法簡化。 s7當然最初以"He"開頭。區別在於s7 = s7.concat("llo");s7重新分配給函數調用的結果。這不能被簡化。就Java編譯器而言,所有函數調用的結果在編譯時都是未知的。由於它不知道結果值,因此無法簡化並保持原樣。結果調用返回"Hello"字符串的新實例,該實例與編譯時實例(s8共享)的實例不同。

s10也不能簡化爲同樣的方式。 s10當然最初以"He"開頭。然後重新分配s10 = s10 + "llo";這不能被簡化。你爲什麼會問?那麼s10是一個非最終變量表達式。從技術上講,編譯器不知道它的價值,因爲它不是一個常量。如果s10被聲明爲final String,那麼這可以是不斷摺疊的(當分配給不同的變量時)。

所以考慮這個版本的測試代碼:

public static void main(String[] args) 
{ 
    String s1 = "Hello"; 
    String s2 = "Hello"; 
    System.out.println("1: s1 == s2 " + (s1 == s2)); // 1 

    String s3 = "Hel" + "lo"; 
    String s4 = "Hel".concat("lo"); 
    System.out.println("2: s1 == s3 " + (s1 == s3)); // 2 
    System.out.println("3: s1 == s4 " + (s1 == s4)); // 3 

    String he = "He"; 
    String s5 = he + "llo"; 
    String s6 = he.concat("llo"); 
    System.out.println("4: s1 == s5 " + (s1 == s5)); // 4 
    System.out.println("5: s1 == s6 " + (s1 == s6)); // 5 

    final String fhe = "He"; 
    String s7 = fhe + "llo"; 
    String s8 = fhe.concat("llo"); 
    System.out.println("6: s1 == s7 " + (s1 == s7)); // 6 
    System.out.println("7: s1 == s8 " + (s1 == s8)); // 7 
} 

你能找出哪些線是真的嗎?

真的,真的,假的,假的,假的,真的,假的
你可能會奇怪,爲什麼3和7是不正確的。簡而言之,Java編譯器沒有被編程
要足夠聰明才能識別concat()調用,因此被視爲常規函數調用。

+0

謝謝,但我有一些疑惑,你爲什麼指的是s7.concat(「llo」);不能簡化。這是否意味着,「Hello」字符串不是在編譯時通過concat方法生成的?如果是,它會創建新的對象(Hello)而不是池中現有的「Hello」對象? – ddfnfal 2010-09-27 05:22:59

+0

對不起,我以爲我解釋了比實際寫的更多。我會詳細說明。 – 2010-09-27 05:28:05