2010-08-12 13 views
8

我正在爲J2ME設備編寫應用程序,並且非常在意不必要的字符串創建。 由於使用Strings是內置的,即沒有必要明確地創建它們,所以我不確定我是否理解它是正確的。在Java中創建字符串的時間

例如,返回String(僅使用雙引號)會在返回時創建該字符串,即如果我有幾個返回語句返回不同的字符串,則只會創建其中一個。是對的嗎?

此外,當使用字符串打印帶有異常的消息時,如果異常沒有被拋出,這些字符串永遠不會被創建,對嗎?

對不起,打擾你這樣的新手問題。

+0

非常感謝所有的麻煩! – 2010-08-12 09:37:58

回答

4

我完全不知道迄今爲止收到的答案。如果你是只是返回字符串文字,例如

return "foo"; 

然後這些值被嵌入到類文件中。 JVM確保從字面上只創建了一個字符串實例 - 但我不認爲當類本身是類時,它不會爲類中的常量創建字符串全部加載。

然後再次,因爲字符串將只創建一次(每個字符串常量)它不可能是一個問題。現在

,如果你的代碼其實更像是這樣的:

return "foo" + new Date(); 

那麼動態創建一個字符串 - 但如果return語句竟然打它只會被創建。

+0

它在'String#intern'中說:*所有文字字符串和字符串值的常量表達式都被禁用。*。因此,基於文字的字符串會被創建一次,至少在執行使用該文字的第一個表達式時會被創建。我懷疑類加載器會查看常量池,併爲所有文字主動創建String實例。但你永遠不會知道;) – 2010-08-12 09:36:42

+0

@Andreas_D:如果它真的那樣會讓生活變得容易很多,我會說。 – 2010-08-12 09:39:36

+1

謝謝Jon!你總是非常實用和樂於助人! – 2010-08-12 09:40:31

0

字符串(和其他對象)只有在您調用它們時纔會創建。

因此,要回答你的問題:

  1. 是,只有得到返回將獲得創建
  2. 是,只有當異常被拋出,這個字符串將會創建
1
    字符串
  1. Right
  2. Right
1

是的。如果通過調用構造函數創建實例的表達式,或者如果(對於字符串)評估文字不執行,則不會創建任何內容。

而且,編譯器會做一些優化。基於字符串文字的字符串對象將只創建一次,然後進行實例化。喜歡這裏:

public String getHello() { 
    return "Hello"; 
} 

public void test() { 
    String s = getHello(); // String object created 
    String t = getHello(); // no new String object created 
} 


JVMS有不同的 '意見':

一個新的類實例可在下列情況下,隱式地創建:

  • 加載一個類或接口包含一個String文字可能會創建一個新的String對象(§2.4.8)來表示該文字。如果已經創建了一個String對象來表示該文本的先前出現,或者如果String.intern方法已經在表示與文字相同的字符串的String對象上被調用,則可能不會發生這種情況。

所以上面的例子是不正確的(我每天都在學習新的東西)。如果"Hello"必須被創建,虛擬機將在類加載期間進行檢查(如果不存在,將創建一個字符串實例)。

因此,現在我的理解是,VM爲每個唯一的字符串文本(在字節代碼級別上)創建一個String實例,而不管它是否被使用。

在字節代碼級因爲編譯器可以優化字符串文本的級聯到一個文字,如:表達"one"+"two"將被編譯到"onetwo"

+0

我開始懷疑所有的字符串都是被禁止的,除非_specially_被聲明不是。所以看起來,通過String文字創建一個String實例與創建一個實現Flyweight模式的對象是一樣的。 – 2010-08-12 10:01:29

+0

@Albus - 不過,重要的是不要陷入陷阱。 Java實習是在考慮編譯時保證的情況下完成的,因此迭代字符串創建基於文本(例如虛構方法'repeat(「*」,5)')不會從自動實習中獲得任何好處(您可能知道現在)。使用可用的預先信息,這更多的是JVM部分的盡力而爲的操作。在附註中,感謝這個有趣的問題,這讓我想到了一些我現在想去試驗的東西。 – 2010-08-12 10:21:48

2

答案是比一些其它的更復雜一點答案建議。

  • 對應於在源代碼中一個字符串文字字符串值被加載的類時創建一次。此外,這些字符串會自動「實施」,這意味着如果相同的字面值出現在的任何類別的多個位置,則只會保留該字符串的一個副本。 (任何其他副本,如果他們創建會得到垃圾收集。)

  • 當應用程序調用new String(...),當它評估字符串連接表達式(即+運營商),或者當它調用許多之一在底層創建Strings的庫方法,將創建一個新的String。

因此,要回答你的問題:

1 - 下也不會真正創造一個串上。相反,它會返回被加載的類時創建一個字符串:

 return "some string"; 

2 - 下面創建兩個字符串。首先它會調用someObject.toString(),然後將它與文字連接起來以提供另一個String。

 return "The answer is :" + someObject; 

3 - 以下內容不會創建字符串;見1。

 throw new Exception("Some message"); 

4 - 以下內容將在執行時創建兩個字符串;見2.

 throw new Exception(someObject + "is wrong"); 

(實際上,3個4粉飾的事實,創造一個異常導致當前線程調用堆棧的細節被捕獲,這些細節包括方法名,類名和文件名當表示這些東西的字符串實際上被創建,或者它們是否被實現時沒有被指定,所以有可能是創建一個Exception對象的動作觸發了一些字符串的創建。)

+0

你確定1嗎?我的意思是,其他人似乎是說,返回一個字符串文字就像調用String對象的構造函數一樣,不同之處在於它自動合併,所以爲什麼它應該在類加載時創建? – 2010-08-12 09:55:49

+0

@Albus - 我確信**。這也同意@Jon Skeet所說的......所以它必須是正確的:-) – 2010-08-12 10:20:33

+0

我第二個斯蒂芬的結論(和信仰喬恩Skeet)。 :P – 2010-08-12 10:24:46

0

這裏的主要概念是任何字符串的堆分配(你說的創建)或者更一般的任何對象的分配都不會發生,直到你的日誌ic-flow使解釋器執行那一行編譯代碼。