2017-05-20 23 views
3

一個基本的問題,但有人可以請解釋我爲什麼對不起事先:Java的String平等例如

String s = "lo"; 
String str7 = "Hel" + s; 
String str8 = "He" + "llo"; 
System.out.println("str7 == str8 is " + (str7 == str8)); 

輸出錯誤。 我認爲str7和str8都指向字符串池中的相同對象,因爲字符串是不可變的。

我錯了嗎? str7和str8都不在池中,而是在堆中?爲什麼?

如果結果確實是字符串池中的同一個不可變字符串,您能否給我提供一些字符串操作的示例?

PS:

String str9 = "He" +"llo"; 
System.out.println("str8 == str9 is " + (str9 == str8)); 

輸出真正

+0

@RajithPemabandu:但問題是具體關於底層表示;使用'equals'是爲了避免這些細節。 –

+0

str8 == str9的原因是編譯器優化 – Simon

+0

相關:[比較字符串與==在Java中聲明爲最終](https://stackoverflow.com/questions/19418427/comparing-strings-with-which-are-聲明在java中) – Pshemo

回答

1

str7str8都在游泳池裏,但他們是不一樣的字符串。也就是說,它們是相同字符序列的不同版本。

想想如果虛擬機在每次創建字符串時都必須掃描整個池以查看是否已經存在具有相同字符序列的另一個字符串,則會獲得性能提升。

在新的例子,你正在構建從相同的底層字符串都str8str9,所以編譯器可以更容易地知道結果的每個是相同的,可以重複使用的池條目。

+3

對不起斯科特 - 我完全困惑和不知所措。 究竟是什麼意思是「同一個字符序列的不同版本」都駐留在池中? 至於掃描整個池,我認爲這正是具有字符串池的目的/目的。 –

+1

池的目的是在認識到可以這樣做時重新使用字符串;編譯器會平衡查找匹配字符串(通常失敗)的成本以及重用字符串的好處。 –

+0

你似乎明白「同一個字符序列的不同版本」意味着足以爭論它:兩個不同的字符串在內存中佔據不同的位置,它們碰巧具有相同順序的相同字符。 –

0

如果您調用String#intern(),那麼編譯器將掃描String池並按照您的預期執行。也就是說,

String s = "lo"; 
String str7 = ("Hel" + s).intern(); 
String str8 = ("He" + "llo").intern(); 
System.out.println("str7 == str8 is " + (str7 == str8)); 

輸出

str7 == str8 is true 

有兩件事情引起這種行爲

  1. String是不變的,所以使用+必要創建新String(S)
  2. String級聯通常用StringBuilder - th來實施at是new StringBuilder("Hel").append(s).toString()new StringBuilder("He").append("llo").toString()StringBuilder不是掃描String實習生池。
+1

我知道intern()的意思,謝謝 問題是爲什麼這兩個字符串,s7和s8,指向不同的字符串在同一個地方 - 字符串池 –

+1

@ AndreyM.Stepanov我希望我的兩個項目符號清理*爲什麼*。 –

+0

..斯蒂芬C解釋了爲什麼S7和S8實際指向不同的地方 –

4

你的理解在所有文字進入字符串池的情況下都是正確的。

當您進行連接時會出現混淆。這裏有兩點需要注意。

1)如果字符串在編譯時解析,是的,它與字符串池同步並使用相同的文字。例如

String str7 = "Helllo"; 
String str8 = "He" + "llo"; 

請注意,兩者都是普通文字。因此沒有運行時轉換等

2)如果字符串在運行時解析,它會在運行時解析爲一個新字符串,並與任何其他字符串不同,除非使用.equals方法比較其內容。

String str7 = "Hel" + s; 
String str8 = "He" + "llo"; 
System.out.println("str7 == str8 is " + (str7 == str8)); //false 

與該情況的concat串(+)運算,JVM返回new StringBuilder(string...).toString()原因之一是一個普通的文字等是一個變量。

看那Java language specification

如果只有一個操作數表達式的類型是字符串,那麼字符串的轉換(§5.1.11)形成在另一個操作數執行,以產生在運行時的字符串的。

從評論的問題:

意思是,在情況下,字符串是作爲文字的級聯的產品,那麼結果總是在池中相同的字符串?

是的。請記住,您的意思是編譯時解析表達式(又名常量表達式)而不是運行時。

而當我們將字符串字面值與字符串對象連接時,結果字符串總是一個新的字符串對象,通過StringBuilder的方式構建?

是一個新的字符串被返回。附加的JVM鏈接確認你。

+1

你的答案是完美的,完整的,容易理解的大拇指! – Yahya

+0

謝謝蘇雷什。 爲了說明問題,這是否意味着如果字符串是作爲文字串聯的產物生成的,那麼結果始終是池中的相同字符串?當我們連接字符串字符串和字符串對象時,結果字符串總是一個新的字符串對象,通過StringBuilder的方式構建? –

+0

@ AndreyM.Stepanov編輯我的回答與給定的問題。 –

2
String s = "lo"; 
String str7 = "Hel" + s; 
String str8 = "He" + "llo"; 
String str9 = "He" + "llo"; 

s所指的對象是代表「LO」字面字符串對象。它在字符串池中。

str8str9指的對象是評估其是常量表達式的表達的結果。因此他們在字符串池中。實際上,由於表達式評估爲「相同」字符串,因此str8str9指的是相同的實際String對象。

str7所引用的對象是評估不是常量表達式的表達式的結果。因此它不在字符串池中。

最終原因"Hel" + s不是常量表達式這裏是s沒有聲明爲final


記住這裏的事情是,String對象在字符串池中只分配在兩種情況下:

  1. 如果他們評估常量表達式的結果,或
  2. 如果它們是使用String.intern()方法明確生成的。

字符串文字是的子情況下的常量表達式

有關什麼常量表達式是一個更詳細的解釋,請參見Java語言規範 - JLS 15.28(常量表達式)和JLS 4.12.4(恆定變量)。

任何不在字符串池中產生的字符串。

+0

謝謝您的確切解釋,斯蒂芬 –

+0

@ AndreyM.Stepanov如果您認爲某些答案可以解決您的問題,可隨時將其標記爲解決方案。更多信息在[「接受答案如何工作?」](http://meta.stackexchange.com/a/5235) – Pshemo