String, StringBuffer, StringBuilder

Consider below code with three concatenation functions with three different types of parameters, String, StringBuffer and StringBuilder:

// Java program to demonstrate difference between String,
// StringBuilder and StringBuffer
class Geeksforgeeks
{
    // Concatenates to String
    public static void concat1(String s1)
    {
        s1 = s1 + "forgeeks";
    }
 
    // Concatenates to StringBuilder
    public static void concat2(StringBuilder s2)
    {
        s2.append("forgeeks");
    }
 
    // Concatenates to StringBuffer
    public static void concat3(StringBuffer s3)
    {
        s3.append("forgeeks");
    }
 
    public static void main(String[] args)
    {
        String s1 = "Geeks";
        concat1(s1);  // s1 is not changed
        System.out.println("String: " + s1);
 
        StringBuilder s2 = new StringBuilder("Geeks");
        concat2(s2); // s2 is changed
        System.out.println("StringBuilder: " + s2);
 
        StringBuffer s3 = new StringBuffer("Geeks");
        concat3(s3); // s3 is changed
        System.out.println("StringBuffer: " + s3);
    }
}

Output:

String: Geeks
StringBuilder: Geeksforgeeks
StringBuffer: Geeksforgeeks

Explanation:

  1. Concat1 : In this method, we pass a string “Geeks” and perform “s1 = s1 + ”forgeeks”. The string passed from main() is not changed, this is due to the fact that String is immutable (String不可變是因?yàn)樵贘DK中String類被聲明為一個(gè)final類). Altering the value of string creates another object and s1 in concat1() stores reference of new string. References s1 in main() and cocat1() refer to different strings.

  2. Concat2 : In this method, we pass a string “Geeks” and perform “s2.append(“forgeeks”)” which changes the actual value of the string (in main) to “Geeksforgeeks”. This is due to the simple fact that StringBuilder is mutable and hence changes its value.

  3. Concat3 : StringBuffer is similar to StringBuilder except one difference that StringBuffer is thread safe, i.e., multiple threads can use it without any issue. The thread safety brings a penalty of performance.

問題0:String為什么是不可變的?
String的不可變指的是當(dāng)String變量被再次賦值時(shí)改變的是它的引用地址,而不是修改原來所引用的地址對(duì)應(yīng)的數(shù)據(jù)。
它不可變的原因在于以下源碼:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

final class的定義保證它不能被繼承,從而不會(huì)被破壞原有的規(guī)則。
private final char value[]中,private的定義也確保了value數(shù)組不會(huì)被輕易操作,final則只是保證了value的引用地址不變。所以實(shí)現(xiàn)其不可變性的重點(diǎn)在于String類的底層實(shí)現(xiàn)都沒有動(dòng)到這個(gè)數(shù)組里的數(shù)據(jù),并且通過final+private的權(quán)限控制來限制訪問。所以不能簡單解釋為通過final實(shí)現(xiàn)的。

問題1:StringBuffer是線程安全的,StringBuilder是線程不安全的,是使用什么方式實(shí)現(xiàn)的線程安全呢?

任何問題,如果想究其本質(zhì)還需尋根源,所以翻閱一下JDK源碼問題便可知曉:

StringBuffer的源碼如下:

public synchronized StringBuffer append(String str) {  
    super.append(str);  
    return this;  
}  

StringBuilder的源碼如下:

public StringBuilder append(String str) {  
    super.append(str);  
    return this;  
}  

對(duì)過對(duì)比即可清楚看到,StringBuffer的線程安全是使用synchronized關(guān)鍵字實(shí)現(xiàn)的!

問題2: StringBuffer和StringBuilder的擴(kuò)容問題
其實(shí)這兩個(gè)類底層的實(shí)現(xiàn)也基本相同,除了StringBuffer加了synchronized關(guān)鍵字,都用了父類AbstractStringBuilder的append方法

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

看一下ensureCapacityInternal的實(shí)現(xiàn):

private void ensureCapacityInternal(int minimumCapacity) {//minimumCapacity為當(dāng)前字符串長度加上追加的字符串長度  
    // overflow-conscious code  
    if (minimumCapacity - value.length > 0)//最小需要的字符串長度大于當(dāng)前的容量的話,進(jìn)行擴(kuò)容!  
        expandCapacity(minimumCapacity); //擴(kuò)容函數(shù)  
}  

擴(kuò)容函數(shù):

void expandCapacity(int minimumCapacity) {  
    int newCapacity = value.length * 2 + 2;//第一次擴(kuò)容,當(dāng)前長度*2+2  
    if (newCapacity - minimumCapacity < 0)//第二次判斷是否需要重新擴(kuò)容!如果第一次擴(kuò)容之后仍舊無法滿足的話,  
        newCapacity = minimumCapacity;//進(jìn)行二次擴(kuò)容,二次擴(kuò)容就是將當(dāng)前需要的大小直接作為擴(kuò)容的大小  
    if (newCapacity < 0) { //判斷是否溢出  
        if (minimumCapacity < 0) // overflow  
            throw new OutOfMemoryError();  
        newCapacity = Integer.MAX_VALUE;  
    }  
    value = Arrays.copyOf(value, newCapacity);//進(jìn)行字符數(shù)組的復(fù)制,完成擴(kuò)容  
}  

Arrays.copyOf底層使用的是java.lang.System.arraycopy(Object, int, Object, int, int)實(shí)現(xiàn)的:

public static char[] copyOf(char[] original, int newLength) {  
    char[] copy = new char[newLength];  
    System.arraycopy(original, 0, copy, 0,  
                     Math.min(original.length, newLength));  
    return copy;  
} 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容