2014-12-30 65 views
18
public static void main(String[] args) { 

    String a = new String("lo").intern(); 
    final String d = a.intern(); 
    String b = "lo"; 
    final String e = "lo"; 
    String c = "Hello"; 
    System.out.println(b==a);//true 
    System.out.println(d==a);//true 
    System.out.println(e==a);//true 
    System.out.println(c=="Hel"+a); //why is this false? when e==a is true 
    System.out.println(c=="Hel"+d); //why is this false? 
    System.out.println(c=="Hel"+b); //why is this false? 
    System.out.println(c=="Hel"+e); //this is true 

} 

這導致實習相關字符串

true 
true 
true 
false 
false 
false 
true 

表達e==a爲真意味着相同的附圖。那麼爲什麼最後一個表達式是真的,但是第四個到最後,即c== "Hel"+a是錯誤的?

+0

我認爲最後四個都應該是假的。讓我試試這個例子。 – markspace

+1

你試過等於() –

+0

我遇到了類似的問題,雖然它被回答,但也有人建議,除非有必要,它通常不值得實習字符串。 –

回答

16

表達

"Hel" + a 

不是編譯時間常數。實際上,它編譯爲:

new StringBuilder().append("Hel").append(a).toString() 

(或類似的),它在運行時創建一個新的String對象。

然而,因爲e是最後時,編譯器可以確定的"Hel"串接和e的值是恆定值,所以它實習生。

+0

而不是toString()我認爲String.valueOf()將被調用,它將在內部調用toSTring()。 – Prashant

+0

@bohemian你能告訴c ==「Hel」+ d是否爲假?這裏d是final ..? –

10

所有這些字符串在運行時計算的,這就是爲什麼他們是不同的

System.out.println(c=="Hel"+a); //why is this false? when e==a is true 
System.out.println(c=="Hel"+d); //why is this false? 
System.out.println(c=="Hel"+b); //why is this false? 

這一個在編譯時計算的,因爲e是最後:

System.out.println(c=="Hel"+e); //this is true 
如果更改代碼,這

System.out.println(c==("Hel"+a).intern()); //why is this false? when e==a is true 
    System.out.println(c==("Hel"+d).intern()); //why is this false? 
    System.out.println(c==("Hel"+b).intern()); //why is this false? 

所有人都會產生真

+0

您是否有JLS的引用,「Hel」+ e'是編譯時常量,因爲e是final?這對我來說是新的。 – markspace

+2

@markspace我沒有確切的引用,但是java編譯器計算常量表達式(在這種情況下爲「Hel」+ e),只要e是final - 編譯器假定這是常量 –

+0

@markspace [Expressions](http:/ /docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28) –

5

Java compilerjavac)將您的code in Java轉換爲byte code,由JVM執行。 它也爲你做了一些優化。你可以用-c參數查詢使用javap工具生成的字節碼

級聯有最後絃樂

c==a是真實的,因爲cfinal 下面是該段的字節碼(去年相比只):

public static void main(java.lang.String[]); 
    Code: 
    0: ldc  #2; //String Hello 
    2: astore_2 
    3: getstatic  #3; //Field java/lang/System.out:Ljava/io/PrintStream; 
    6: aload_2 
    7: ldc  #2; //String Hello 
    9: if_acmpne  16 
    12: iconst_1 
    13: goto 17 
    16: iconst_0 
    17: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V 
    20: return 

} 

正如你所看到的,java編譯器已經將「Hel」與「lo」合併在一起,只是比較兩個字符串leterals「Hello」。Java的默認實習生字符串文字 - 這就是爲什麼它返回true

級聯與非最後絃樂

如果你是串聯字符串字面量非最終字符串變量,字節碼會有所不同:

public static void main(java.lang.String[]); 
    Code: 
    0: ldc  #2; //String lo 
    2: astore_1 
    3: ldc  #3; //String Hello 
    5: astore_2 
    6: getstatic  #4; //Field java/lang/System.out:Ljava/io/PrintStream; 
    9: aload_2 
    10: new  #5; //class java/lang/StringBuilder 
    13: dup 
    14: invokespecial #6; //Method java/lang/StringBuilder."<init>":()V 
    17: ldc  #7; //String Hel 
    19: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    22: aload_1 
    23: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    26: invokevirtual #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    29: if_acmpne  36 
    32: iconst_1 
    33: goto 37 
    36: iconst_0 
    37: invokevirtual #10; //Method java/io/PrintStream.println:(Z)V 
    40: return 

} 

在這裏,我們比較java/lang/StringBuilder.toString:()Ljava/lang/String;方法,這顯然返回另一個對象的結果 - 它等於「你好」的值,但不會被引用

即使您正在使用intern()方法你可以找到在這個stackoverflow question

+0

不熟悉字節碼可以添加一些註釋以獲得更好的解釋 –

+0

@SumeetSharma我已經添加了一些更多的細節。希望現在清楚 – bedrin

+0

非常感謝對字節碼的深入瞭解。瞭解引擎蓋下的==操作非常有用。但是我發現Boehmias的答案與我所尋找的更相關。 :) 不管怎麼說,還是要謝謝你。我給了一個贊成票。 –

1

上比較字符串的詳細信息,你要還記得==比較參照,而不是價值。

所以在

System.out.println(c=="Hel"+a); 
    System.out.println(c=="Hel"+d); 
    System.out.println(c=="Hel"+b); 

"Hel" + a"Hel" + d"Hel" + b的情況下將內存中的一個新的引用不等於的c

在最後的情況下,由於字符串值是最終的,所以評估發生在編譯時而不是運行時作爲優化,因爲它永遠不會改變。此外,如果您在定義字符串文字時正在考慮,則Java會在內部實施它們。