String、StringBuffer、StringBuilder的相關Q&A

Q1:是否可變?

A1:結論:String不可變,StringBuffer和StringBuilder可變。理由如下:

看jdk的源碼,String類的源碼如下:

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

因爲有final修飾符,所以顯然,String是不可變的;
接着看AbstractStringBuilder的源碼:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中是使用字符數組保存字符串,所以StringBuffer和StringBuilder都是可變的。StringBuffer源代碼如下,StringBuilder源代碼就不貼了,大同小異。

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    /**
     * Constructs a string buffer with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuffer() {
        super(16);
    }

Q2:是否線程安全

A2:結論:String、StringBuffer是線程安全的,StringBuilder是非線程安全的。理由如下:

String對象不可變啊,顯然線程安全。
StringBuffer和StringBuilder都繼承自AbstractStringBuilder,而該類已經實現了很多關於字符串的方法,以append方法爲例:

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

StringBuffer和StringBuilder很多方法直接調用父類方法,但是StringBuffer添加了添加了synchronized關鍵字:

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

而StringBuilder類中沒有加synchronized關鍵字

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

Q3:使用場景問題

A3:String是不可變的,每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,然後將指針指向新的 String 對象,所以經常改變內容的字符串最好不要用 String。

以下是實驗的情況:

public class Test0 {
    public static void main(String[] args) {
        long beginTime = System.currentTimeMillis();
        String str = new String();
        for (int i = 0; i < 100000; i++) {
            str = str + i;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("time1:" + (endTime - beginTime));
        beginTime = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer();
        for (int i = 0; i < 100000; i++) {
            sbf.append(i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("time2:" + (endTime - beginTime));
    }
}

結果:

time1:15606
time2:6

StringBuffer和StringBuilder主要區別在於一個是線程安全的,另一個則不是,顯然非線程安全的StringBuilder在效率上更高。所以StringBuffer適合需要保證線程安全的場景中,而沒有此要求的則用StringBuilder來提高效率。

補充知識:

String類覆蓋了equals方法和hashCode方法,而StringBuffer沒有覆蓋equals方法和hashCode方法。StringBuilder也沒有覆蓋這兩個方法。所以下面的代碼輸出是false。

StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb.equals(sb2));

Q4:下面這條語句一共創建了多少個對象:

String s="a"+"b"+"c"+"d";

A4:對於如下代碼:

String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");

第一條語句打印的結果爲false,第二條語句打印的結果爲true,這說明javac編譯可以對字符串常量直接相加的表達式進行優化,不必要等到運行期去進行加法運算處理,而是在編譯時去掉其中的加號,直接將其編譯成一個這些常量相連的結果。
題目中的第一行代碼被編譯器在編譯時優化後,相當於直接定義了一個”abcd”的字符串,所以,上面的代碼應該只創建了一個String對象。寫如下兩行代碼,

String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");

最終打印的結果應該爲true。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章