2010-06-15 52 views
4

說,我有我的程序如下一行:字符串對象。澄清需要

jobSetupErrors.append("abc"); 

在上面,其中jobSetupErrors是一個StringBuilder()的情況下,我所看到的發生是:

創建
  1. 新的String對象和分配值「ABC」
  2. 該字符串對象的
  3. 值被分配給現有的StringBuilder對象

如果這是正確的,並且我添加1條線...

jobSetupErrors.append("abc"); 
logger.info("abc"); 

在上面的例子中,我們創建字符串對象分別 2次?

如果是這樣,做這樣的事情會更合適嗎?

String a = "abc"; 
jobSetupErrors.append(a); 
logger.info(a); 

這是一個更好的方法嗎?請告知

回答

4

爲了找出答案,我寫了一個類像下面這樣:

class Test { 
    String a = "abc" ; 
    StringBuilder buffer = new StringBuilder() ; 

    public void normal() { 
    buffer.append("abc") ; 
    buffer.append("abc") ; 
    } 

    public void clever() { 
    buffer.append(a) ; 
    buffer.append(a) ; 
    } 
} 

如果我們編譯這個,然後運行javap的在它提取字節碼:

14:09:58 :: javap $ javap -c Test 
Compiled from "Test.java" 
class Test extends java.lang.Object{ 
java.lang.String a; 

java.lang.StringBuilder buffer; 

Test(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: aload_0 
    5: ldC#2; //String abc 
    7: putfield #3; //Field a:Ljava/lang/String; 
    10: aload_0 
    11: new #4; //class java/lang/StringBuilder 
    14: dup 
    15: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V 
    18: putfield #6; //Field buffer:Ljava/lang/StringBuilder; 
    21: return 

public void normal(); 
    Code: 
    0: aload_0 
    1: getfield #6; //Field buffer:Ljava/lang/StringBuilder; 
    4: ldC#2; //String abc 
    6: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    9: pop 
    10: aload_0 
    11: getfield #6; //Field buffer:Ljava/lang/StringBuilder; 
    14: ldC#2; //String abc 
    16: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    19: pop 
    20: return 

public void clever(); 
    Code: 
    0: aload_0 
    1: getfield #6; //Field buffer:Ljava/lang/StringBuilder; 
    4: aload_0 
    5: getfield #3; //Field a:Ljava/lang/String; 
    8: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    11: pop 
    12: aload_0 
    13: getfield #6; //Field buffer:Ljava/lang/StringBuilder; 
    16: aload_0 
    17: getfield #3; //Field a:Ljava/lang/String; 
    20: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    23: pop 
    24: return 

} 

我們可以看到2件事。

首先,normal方法是使用相同的字符串實例爲這兩個呼叫的(事實上,它是相同的文字作爲被設置爲在初始化塊中的這個類的a成員變量)

其次, clever方法比normal更長。這是因爲需要額外的步驟才能將該屬性從類中取出。因此,故事的寓意是,99%的時間,Java在自己的事情上以正確的方式做事,而且不需要嘗試聰明;-)(和javap是一個非常酷的工具,當時你想知道到底發生了什麼)

+1

javap,而不是jsonp。 – danben 2010-06-15 13:27:56

+0

哎呀! ;-)你能說出我*應該做的事嗎? ;-) – 2010-06-15 13:36:19

+0

如果比較代碼長度,你應該使用局部變量而不是'clever'中的字段;但無論如何,這個問題主要是關於創建字符串的問題,並且從您的示例中可以看出,只涉及一個String對象! – 2010-06-15 13:37:15

8

在上面的例子中,我們創建 字符串對象分開2次?

不,因爲在Java字符串文字(雙引號中的任何內容)被禁用。這意味着這兩條線都指相同的String,所以不需要進一步的優化。

在第二個示例中,您只是爲相同的String創建了一個額外的引用,但這是Java通過將其引用到字符串池中而爲您完成的。第一次看到「abc」時會發生這種情況;第二次,它檢查池並發現「abc」已經存在,所以它被替換爲與第一個相同的引用。

有關String實習的更多信息,請參閱http://en.wikipedia.org/wiki/String_interning