相同:底層均采用字符數組value來保存字符串
區別:String類的value數組有final 修飾,指向不可改,同時private 未提供修改value數組的方法。StringBuilder類的value數組沒有final修飾,可以改變指向,且可以擴容,擴容通過新建字符數組完成。
首先分析String的源碼:

可以看到String類有final修飾,所以String類不能被繼承。這保證對String對象方法的調用確實運行的是String類的方法,而不是經其子類重寫后的方法。往下看,是value數組,該字符數組被用於存儲String的字符串,即”abcd”在String里存儲為value[]:’a’, ’b’, ‘c’, ‘d’ 的字符數組,有final修飾,表示value指向的數組不變,但是數組里的值可變,但是有private修飾,且String類並沒有提供修改value數組內容的方法,所以String對象的值一旦賦值就不可變。
例如:下列字符數組c為final修飾,表示c的指向不變,但修改c的內容是可行的。

賦值前后的內存變化:
賦值前:

賦值后:

第一部分:構造函數
接下來看看常用的String類的構造函數:
首先空串 ”” 和 “abcd” 都是字符串對象

再看這兩個構造函數:

對於以下代碼:
String str1 = new String(); String str2 = new String(“abcd”);
在構造函數中分別對應:
this.value = “”.value; this.value = “abcd”.value;
是將 “” 的value值賦予str1的value,將 “abcd” 的value所指對象賦給str2的value:


再看另一個常用的構造函數:

該函數使用數組復制函數,將參數數組c復制給str3的字符數組value
再看看數組復制函數Arrays.copyOf():

其中參數original是要被復制的字符數組,newLength是要復制的長度,也是新數組的長度。函數返回復制完的新數組。
char[] c = {‘a’, ‘b’, ‘c’, ‘d’}; String str3 = new String(c);
的內存分析如下:
第二部分 常用函數
- length()函數,返回的是字符數組value的長度

2.判空isEmpty() 函數,利用字符數組長度是否為0
3.重寫的 equals函數
首先判斷是否為同一個對象,若不是,再判斷是否是String對象,強制轉為String類型后,判斷兩個String對象的value數組是否長度相同,接下來對兩個value字符數組的內容從0開始向后比較。即equal()為真的情況是: 兩個對象相同;都是String對象且value字符數組內容相同。

函數為真的兩種情況:
情況一:指向同一對象
情況二:均為String對象且value數組內容一致
4.compareTo() 函數
即在均存在字符的情況下,返回第一個不相同字符之差,前lim個字符相同(即長字符串的前lim個恰好是短字符串)則返回兩個字符串長度之差。
5.substring 函數
substring(int)
該函數返回從給定參數位置起到字符串結束的新字符串。如果給定從0開始,則返回原本的String對象,否則返回一個新的String對象。
substring(int, int):


如果要獲取的子串是從0到最后,則返回原本的String對象,否則返回一個新的String對象。
6.字符替換 replace(char, char);
函數先判斷替換字符和被替換字符是否相同,若相同則返回原String對象,不同則先找到要替換的字符位置,接下來創建一個新的字符數組,將保存替換后的字符串,最后返回一個新的String對象。
7.toString函數
因為已經是String對象,所以返回自身。
接來下看StringBuilder類的源碼:
StringBuilder類也是由final修飾,即也不能被繼承。同時該類繼承了抽象父類AbstractStringBuilder.

第一部分 構造函數
StringBuilder strb = new StringBuilder();
可以看到構造函數對父類構造函數傳的參數為16,分析父類對應的構造函數:

父類創建了一個長度為16的字符數組,將value指向該新數組,那么value是什么呢?

即value也是字符數組,和String類不同的是,該字符數組沒有final 修飾,即value的指向可以改變,同時權限為默認,即同類和同包可用,該類和StringBuilder在同一個包中。同時使用count變量記錄字符個數(不同於value數組長度,value長度是容量)。
另一個構造函數:
StringBuilder strb = new StringBuilder(20);
即通過父類構造函數,創建一個新的字符數組。該數組長度為參數值。
傳入字符串的構造函數:
StringBuilder strb = new StringBuilder(“abcd”);
初始StringBuilder的value數組容量設置為傳入字符串長度加上16,再通過append函數將str寫入,分析append函數:

先調用父類的append方法:

ensureCapacityInternal函數:參數是當前對象的value中字符長度與傳入字符串長度之和,也即是value的容量最小值。
如果需要的容量最小值大於目前value容量,調用擴容函數enpandCapacity函數。
enpandCapacity函數源碼:
擴容的新容量為當前value的容量2倍加2,如果擴容后的容量還是比需要的最小容量小,則直接擴容為需要的最小容量,再將當前value內容復制給一個新的長度為newCapacity的字符數組,再將value指向這個擴容后的新數組。即擴容是通過開辟新數組完成的,返回的也是新創建的新數組。
接着,append函數執行完ensureCapacityInternal函數后,this對象的value數組已經指向一個擴容后的新數組,並且之前的value數組里的值也復制到新的value數組中,接下來執行getChars函數:
getChars函數源碼:
即該函數是將調用的string對象的value數組從srcBegin到srcEnd復制給目標數組dst,從dst數組的第dstBegin位置開始。
append函數中執行完str.getChars函數后就將參數str的內容追加到StringBuilder對象的value數組后面,再更新count值,返回調用對象。
內存可以描述為:
第二部分 常用函數
- 刪除函數
deleteCharAt(int)
看父類的對應函數

即刪除索引為index處的字符。通過調用數組復制函數來完成,將索引后面的內容依次復制到從索引開始的位置上,即通過覆蓋的原理完成,更新count。
2.插入函數insert(int, char)

父類對應函數:

即將字符c 插入到索引offset位置上,首先確保value數組容量足夠,然后通過數組復制,將索引位置開始全部向后移一位,再將索引位置賦值c,更新count。
3.toString函數
返回一個新創建的String對象,內容為value保存的字符。
