在Java中字符串的使用非常廣泛,我們常用的如:String ,StringBuffer, StringBuilder.但是一直都沒有系統(tǒng)的整理過他們的區(qū)別,今天就系統(tǒng)的整理一下,記錄自己的學(xué)習(xí)歷程。
一:String
首先我們看下String的源碼:
public final class String implementsjava.io.Serializable, Comparable, CharSequence
從String的類聲明上就可以發(fā)現(xiàn),String是一個(gè)類而不是基本數(shù)據(jù)類型。
既然是一個(gè)類,那String的使用也很簡單,直接new就可以了。
String可以通過 “+ “來完成字符串的拼接,那我們來做個(gè)測(cè)試:

使用是很簡單,但是通過我們上面的測(cè)試發(fā)現(xiàn)一個(gè)問題就是,當(dāng)我們 給str + “test”后,他的hashCode發(fā)生了變化,說明了第10行的str已經(jīng)是一個(gè)新的對(duì)象了。
在String的源碼中維護(hù)了一個(gè)finalchar value[],如下:
private final charvalue[];
所以從源碼中我們可以發(fā)現(xiàn),value是final類型的,是不能被改變的,所以只要改變就只能生一個(gè)新的String對(duì)象。也可以說String類型的對(duì)象是長度不可變的,String拼接字符串每次都要生成一個(gè)新的對(duì)象,所以拼接字符串的效率也比較低。
二:StringBuffer
首先我們看下StringBuffer的源碼:
public final class StringBuffer extends AbstractStringBuilder
? ? ? ? ? ? ? ? ?implementsjava.io.Serializable, CharSequence
從源碼中我們可以看出,StringBuffer也是一個(gè)final class,也不能被繼承。既然是類,那使用也一樣直接new一個(gè)對(duì)象出來。
StringBuffer是通過.append 來完成字符串的拼接,那讓我們來做個(gè)測(cè)試

從我們的測(cè)試來看,StringBuffer和 String 類是不同的,因?yàn)镾tringBuffer被修改后并沒有產(chǎn)生新的對(duì)象,是在之前的對(duì)象上修改的。下面我們來看下部分源碼:
? ?publicStringBuffer() {
? ? ? super(16);
? ? ?}
? ??public StringBuffer append(CharSequence s) {
?????????if (s == null) s = "null";
???????? if (s instanceofString) return this.append((String)s);
???????? if (s instanceofStringBuffer) return this.append((StringBuffer)s);
? ? ? ? ? return this.append(s, 0, s.length());
?????}
?????public synchronized StringBuffer append(CharSequence s, int start, intend)
?????{
????????super.append(s, start, end);
??????? return this;
?????}
?從上面的代碼中可以看出來,StringBuffer的初始容量可以容納16個(gè)字符,當(dāng)該對(duì)象的實(shí)體存放的字符的長度大于16時(shí),實(shí)體容量就自動(dòng)增加。
StringBuffer的函數(shù)都是加了Synchronized關(guān)鍵字的,所以StringBuffer的方法是線程安全的,可以在多線程中使用。
總結(jié):
如果對(duì)字符串的改變少,使用String;
如果對(duì)字符串修改的較多或需要線程安全就用StringBuffer,
三:StringBuilder
首先我們看下StringBuilder的源碼:
public final class StringBuilder extends AbstractStringBuilder?
????????????????????????implements java.io.Serializable, CharSequence
從源碼中我們可以看出,StringBuilder和StringBuffer一樣,也是一個(gè)final class,也不能被繼承。既然是類,那使用也一樣直接new一個(gè)對(duì)象出來。
StringBuilder是通過.append 來完成字符串的拼接,那讓我們來做個(gè)測(cè)試

從上面的測(cè)試我門可以發(fā)現(xiàn)StringBuilder 類的對(duì)象能夠被多次的修改,并且不產(chǎn)生新對(duì)象。
下面我們來看下部分源碼:
?public StringBuilder(String str) {
?????????super(str.length() + 16);
?????????append(str);
????}
? ?private StringBuilder append(StringBuilder sb) {
?????????if (sb == null) returnappend("null");
?????????int len = sb.length();
?????????int newcount = count + len;
?????????if(newcount > value.length)
?????????????expandCapacity(newcount);
?????????sb.getChars(0, len, value, count);
?????????count = newcount;
?????????return this;
?????}
從源碼中我們可以發(fā)現(xiàn)它和StringBuffer 之間的最大不同在于StringBuilder 的方法不是線程安全的(不能同步訪問)。所以在單線程的環(huán)境下StringBuilder相較于StringBuffer 有速度優(yōu)勢(shì),因?yàn)樗恍枰鐾教幚怼?/p>
StringBuffer的默認(rèn)長度是16, StringBuilder的默認(rèn)長度是16+初始化傳入字符串的長度。
四:擴(kuò)容
下面是擴(kuò)容的源碼:
void expandCapacity(int minimumCapacity) {
????int newCapacity = value.length * 2 + 2;
??? if (newCapacity -minimumCapacity < 0)
??????? newCapacity = minimumCapacity;
??? if (newCapacity < 0) {
??????? if (minimumCapacity < 0) // overflow
??????????? throw new OutOfMemoryError();
??????? newCapacity = Integer.MAX_VALUE;
? ? ??}
????value =Arrays.copyOf(value, newCapacity);
?}
當(dāng)我們對(duì)StringBuffer和StringBuilder進(jìn)行append的時(shí)候,會(huì)先判斷當(dāng)前的容量是否可以放下,如果長度不夠,就會(huì)調(diào)用expandCapacity來進(jìn)行擴(kuò)容
擴(kuò)容的計(jì)算公式:現(xiàn)在的長度*2 + 2;
如果我們擴(kuò)容后的長度(newCapacity)還是放不下(minimumCapacity)的長度那就直接把所需要的長度賦值給擴(kuò)容后的長度(newCapacity = minimumCapacity);
因?yàn)楫?dāng)int的最大值 + 1就會(huì)變成負(fù)數(shù),所以我們需要在擴(kuò)容完后驗(yàn)證,newCapacity的正確性防止內(nèi)存溢出,如果超過了int的最大長度,那么就把NewCapacity 設(shè)置成Integer.MAX_VALUE,
newCapacity = Integer.MAX_VALUE;

最后把原數(shù)組copy到擴(kuò)容后的數(shù)組中:
?value = Arrays.copyOf(value,newCapacity);
總結(jié):
? ? 當(dāng)需要頻繁的字符串拼接和刪除時(shí),建議使用StringBuffer或StringBuilder
? ? 在單線程的程序中,使用String或StringBuilder
? ? 在多線程的程序中,使用StringBuffer.