Java中String、StringBuffer和StringBuilder的區別


轉載請注明原文地址:http://www.cnblogs.com/ygj0930/p/6581009.html 

    在編程中,對於字符串拼接,我們可以用String類重載的+或concat(str)、StringBuffer的append(str)、StringBuilder的append(str)。那么,三者到底有什么不同呢?

   

一:String

        1:String類的特性

             1)String是個final類:所以String類是不可以被繼承的。同樣,String對象的值是不可以被改變的;

             2)String對象的創建方法比較(原理比較):

                 在JVM加載運行class文件時,對於字節文件中出現的常量(符號引用、字符串字面量等)會在方法區的常量池中分類存放。其中,源代碼中出現過的字符串字面常量會保存在 CONSTANT_String_info常量表中。然后,根據不同的創建方法,會有不同的內容分配,具體如下:

                  1、字面常量方式創建String變量:String str1=“string” ;

                                                              String str2="string";

                      上面創建了兩個String引用,都指向同樣內容的字符串。我們知道該類在加載時,字符串常量被存放在 CONSTANT_String_info常量表 中,並且同一字符串內容只會存放一次(我們稱常量表中的字符串為“拘留字符串”)。故此,上面兩個String引用都指向 CONSTANT_String_info常量表 中的同一地址(同一拘留字符串)。因此,這里 str1==str2 返回true。(另:這里是沒有創建string對象的,因為沒有在堆中分配內存)

                  2、通過new關鍵字創建String對象:String str1=new String("string");

                                                                  String str2=new String("string");

                       類在加載時,首先把源代碼中出現的字符串常量放在CONSTANT_String_info常量表中,故此:上面兩語句中相同的 “string” 常量值放在了字符串常量表中同一處。然后,在執行到這兩條語句時,根據new關鍵字,在堆中分別創建String對象str1和str2,並且兩對象在堆中保存值均為 "string" 。也就是說,此時JVM中有三個地方存有同一字符串值:常量表中的拘留字符串、str1對象的堆空間、str2對象的堆空間。因此,這里的 str1==str2 返回的是false。調用String類重寫過的equals()方法比較兩對象的值才返回true。

                  3、通過intern()方法創建的String“對象”:String str1="string";

                                                                         String str2=new String("string").intern();

       intern()的作用:當常量池的字符串常量表中存在與 "string" 相同(內容相同)的字符串(拘留字符串)時,則直接返回這個拘留字符串的地址,賦值給str2(此時沒有創建新的對象);

       如果常量池中沒有與 “string” 相同的拘留字符串時,則把 "string" 添加到常量池中(成為拘留字符串,供下一個intern()時作返回用),並在堆中創建String對象然后把對象的引用返回(此時有新對象創建);

       因此,這里 str1==str2 返回true。

       由此可見,intern()方法主要可以用來避免創建內容重復的String對象,值得我們重視。

 

        2: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對象,只是把拼接結果作為拘留字符串的保存地址返回了而已。

 

        3:String中的concat(str)拼接字符串

        與 + 可以拼接其他數據類型不同,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對象。

 

二:StringBuilder

        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[]) 實現的。

 

三:StringBuffer

        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+str2、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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM