轉載請註明原文地址:http://www.cnblogs.com/ygj0930/p/6581009.html html
在編程中,對於字符串拼接,咱們能夠用String類重載的+或concat(str)、StringBuffer的append(str)、StringBuilder的append(str)。那麼,三者到底有什麼不一樣呢?編程
1)String是個final類:因此String類是不能夠被繼承的。一樣,String對象的值是不能夠被改變的;數組
2)String對象的建立方法比較(原理比較):安全
在JVM加載運行class文件時,對於字節文件中出現的常量(符號引用、字符串字面量等)會在方法區的常量池中分類存放。其中,源代碼中出現過的字符串字面常量會保存在 CONSTANT_String_info常量表中。而後,根據不一樣的建立方法,會有不一樣的內容分配,具體以下:多線程
一、字面常量方式建立String變量:String str1=「string」 ;app
String str2="string";性能
上面建立了兩個String引用,都指向一樣內容的字符串。咱們知道該類在加載時,字符串常量被存放在 CONSTANT_String_info常量表 中,而且同一字符串內容只會存放一次(咱們稱常量表中的字符串爲「拘留字符串」)。故此,上面兩個String引用都指向 CONSTANT_String_info常量表 中的同一地址(同一拘留字符串)。所以,這裏 str1==str2 返回true。(另:這裏是沒有建立string對象的,由於沒有在堆中分配內存)ui
二、經過new關鍵字建立String對象:String str1=new String("string");this
String str2=new String("string");spa
類在加載時,首先把源代碼中出現的字符串常量放在CONSTANT_String_info常量表中,故此:上面兩語句中相同的 「string」 常量值放在了字符串常量表中同一處。而後,在執行到這兩條語句時,根據new關鍵字,在堆中分別建立String對象str1和str2,而且兩對象在堆中保存值均爲 "string" 。也就是說,此時JVM中有三個地方存有同一字符串值:常量表中的拘留字符串、str1對象的堆空間、str2對象的堆空間。所以,這裏的 str1==str2 返回的是false。調用String類重寫過的equals()方法比較兩對象的值才返回true。
三、經過intern()方法建立的String「對象」:String str1="string";
String str2=new String("string").intern();
intern()的做用:當常量池的字符串常量表中存在與 "string" 相同(內容相同)的字符串(拘留字符串)時,則直接返回這個拘留字符串的地址,賦值給str2(此時沒有建立新的對象);
若是常量池中沒有與 「string」 相同的拘留字符串時,則把 "string" 添加到常量池中(成爲拘留字符串,供下一個intern()時做返回用),並在堆中建立String對象而後把對象的引用返回(此時有新對象建立);
所以,這裏 str1==str2 返回true。
因而可知,intern()方法主要能夠用來避免建立內容重複的String對象,值得咱們重視。
用String類重載的 + 拼接字符串時,能夠在 + 先後跟其餘數據類型,不必定是string類型。其餘類型數據會自動「向高看齊」轉化爲String類型。
重載的 + 操做符,實際上是建立一個StringBuffer或StringBuilder對象,用append方法對字符串進行鏈接,最後調用toString方法返回String字符串。
注意用 + 拼接字符串的兩種狀況:
2.1)用 + 拼接兩個字符串變量:String str1="str";
String str2="ing";
String str=str1+str2;
String str1_2="string";
解析:這裏用 + 拼接的是兩個字符串變量,因此會首先建立一個StringBuffer/StringBuilder,而後 append(str1).append(str2) 把str1和str2拼接起來,最後經過toString()生成一個新的String對象並把引用返回,賦值給str。因此,這裏的 str==str1_2 結果是false。這裏建立了新的String對象。
2.2)用 + 拼接兩個字符串字面量:String str1="str"+"ing";
String str2="string";
解析:用 + 拼接兩個字符串字面量時,JVM會自動把這兩個字面量的合併值做爲一個完成的字符串常量值,保存到常量池的字符串常量表中。所以,這裏 str1==str2 結果是true。這裏沒有建立新的String對象,只是把拼接結果做爲拘留字符串的保存地址返回了而已。
與 + 能夠拼接其餘數據類型不一樣,concat()只能把string類型的內容拼接到調用者後面。
經過查看源碼
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } char buf[] = new char[count + otherLen]; getChars(0, count, buf, 0); str.getChars(0, otherLen, buf, count); return new String(0, count + otherLen, buf); }
咱們能夠看到,concat()拼接字符串的原理是建立一個新的char[]字符數組,把兩個字符串轉化爲char後存在新數組中,最後用char[]建立一個新的String對象。
1:StringBuilder是個非線程安全的final類,不能被繼承
2:StringBuilder的append() 工做原理
public StringBuilder append(String str) { super.append(str); return this; }
StringBuilder是調用了其父類的append方法的,咱們來看源碼:
public AbstractStringBuilder append(String str) { if (str == null) { str = "null"; } int len = str.length(); if (len == 0) { return this; } int newCount = count + len;//統計拼接後字符串長度 if (newCount > value.length) { expandCapacity(newCount);//若是拼接結果大於所用的char[],則擴容 } //getChars將拼接字符串賦值到char[]中 str.getChars(0, len, value, count); count = newCount;//更新char[]所保存的字符串長度 return this; }
可知,用StringBuilder拼接字符串時,實際上是在底層建立了一個char[]數組,而後經過char[]把要拼接的字符串添加到char[]而已。最後經過toString()生成最終拼接結果時就是經過 return new String(char[]) 實現的。
1:StringBuffer是個線程安全的final類,不能被繼承。
2:StringBuffer的 append() 工做原理
public synchronized StringBuffer append(String str) { super.append(str); return this; }
能夠看到,StringBuffer的append也是調用父類AbstractStringBuilder的append方法實現的,原理同StringBuilder。其惟一不一樣的地方在於,加了一個syncrhoized關鍵字修飾append()方法,保證了線程同步。
1:性能:通常來講是 StringBuilder>StringBuffer>String
從上面四種(其實應該說是五種,+ 分爲字符串常量的拼接和變量的拼接兩種)的字符串拼接來看,除了字符串常量的拼接是返回拘留字符串的地址外,其餘四種(str1+str二、str1.concat(str2)、builder.append(str1).append(str2)、buffer.append(str1).append(str2))都是使用了StringBuilder,或者說是StringBuilder的父類的拼接方法來作的——建立一個char數組,把須要拼接的內容先存進char數組,最後經過char數組建立新的String對象返回。
形成三者性能差異的主要緣由是:
用String的 + 累加拼接字符串變量時,每拼接一個就會建立一個StringBuilder,並經過append()方法拼接,而後返回一個新的String對象。而後再繼續拼接下一個變量。這樣就會致使重複建立StringBuilder對象,性能低下。用 concat() 累計拼接時,則每兩個字符串拼接都會建立一個 char[] 進行內容拼接並返回一個新的String對象做爲結果,重複調用concat()會致使重複建立char[]和新String對象,性能低下。
StringBuilder在調用toString()以前都不會建立拼接結果,而且底層的char數組會自動擴容,一直到拼接字符串所有存入char數組後,調用toString()時才建立新的String對象並返回,這樣就避免了重複建立,效率提升。
StringBuffer則由於使用了syncrhoized對append()進行了加鎖,因此致使性能稍微低於StringBuilder。
2:不一樣情景下的選用
拼接兩個字符串字面量,用 + 操做符;
單線程下拼接兩個字符串變量,用StringBuilder;
多線程下拼接兩個字符串變量,用StringBuffer;