引言:
相信大多數(shù)面試都會問到關于Java中的String、StringBuffer赋焕、StringBuilder之間的區(qū)別参歹。
可能很多人會直接通過百度查看直接最后結果,但今天從源碼維度談談為什么三者會有各自的區(qū)別和特點隆判。
一犬庇、String、StringBuffer侨嘀、StringBuilder三者之間的類結構關系
Stringl類
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
StringBuffer
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{}
StringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{}
String臭挽、StringBuffer、StringBuilder 三者的類關系圖
可以發(fā)現(xiàn)StringBuffer 與StringBuilder是繼承了AbstractStringBuilder抽象類笆豁。
二郎汪、String部分源碼解讀
這里就針對String類核心關鍵代碼解讀,final 數(shù)組闯狱、多個構造方法煞赢、部分主要方法。
① final 數(shù)組 與部分構造方法
private final char value[];
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
從上面的構造方法可以看出哄孤,實際上String類是將內容存放在final value[]
數(shù)組中照筑。
因為value[]數(shù)組是final的,所以這也就代表著在給對象賦值新的值時瘦陈,因final value[] 常量凝危,所以每次賦值都是產(chǎn)生新對象。例如:
String str = "abc";
str = "bcd";
如果執(zhí)行以下代碼操作晨逝,jvm方法區(qū)中是生成兩個對象后蛾默,再進行計算獲得最后的內容。
String str = "abc";
str = str + "def";
三捉貌、StringBuffer支鸡、StringBuilder部分源碼解讀
為什么要將StringBuffer和StringBuilder一起解讀冬念? 因為兩個源碼幾乎一樣,不一樣的是StringBuffer的操作方法用了synchronized字段牧挣。
① 數(shù)組 與部分構造方法
StringBuffer類
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
private transient char[] toStringCache;
static final long serialVersionUID = 3388685877147921107L;
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
StringBuilder類
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
StringBuffer 和StringBuilder類都是繼承了AbstractStringBuilder類急前,同時構造方法中都傳入了(+16)長度給父類。下面繼續(xù)跟進下AbstractStringBuilder類瀑构。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
從抽象類AbstractStringBuilder解讀裆针,帶有數(shù)組常量value ,并將對應內容存在了value中寺晌。
② StringBuffer與StringBuilder的其他方法
StringBuffer類的其他方法据块,如圖所示都加入了synchronized字段
StringBuilder類的其他方法,如圖所示沒有加入了synchronized字段
③ 重點講解下append方法
StringBuffer和StringBuilder有很多方法折剃,因為常用的append操作另假,所以這里重點介紹下append()方法。
先看下StringBuffer怕犁、StringBuilder的源代碼
// StringBuffer類
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
// StringBuilder類
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuffer和StringBuilder都重寫了父類AbstractStringBuilder的方法边篮,并且同時調用了父類的append()方法。
接著跟進查看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(count + len);
這個語句里兩個操作戈轿,a. 為數(shù)組value[]擴容 b. 將舊數(shù)組搬移到新擴容數(shù)組中。 - 是
str.getChars(0, len, value, count);
這個語句是通過調用String.getChars()方法阵子,正式將value數(shù)組與str數(shù)組相連接思杯。
④ 進一步解讀ensureCapacityInternal(count + len);
與 str.getChars(0, len, value, count);
A. 解讀ensureCapacityInternal(count + len);
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
這個ensureCapacityInternal()確認容量內部方法通過判斷新的容量大小是否大于以有容量。如果打算申請的容量大于已有容量挠进,則進行 newCapacity(minimumCapacity)
容量申請 + Arrays.copyOf()
數(shù)組復制兩個操作色乾。接著跟進查看 newCapacity(minimumCapacity)
容量申請方法。
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
創(chuàng)建新容量大小newCapacity()方法:
- 首先领突,自動擴容的容量newCapacity 大小為
(value.length << 1) + 2
舊長度x2 + 2暖璧,也就是如果舊容量是16,則自動擴容的容量是 16x2 +2 = 34 君旦。 - 接著澎办,如果自動擴容的容量newCapacity 小于 預期申請的minCapacity ,則將預期申請的minCapacity賦值給自動擴容的容量newCapacity金砍。
- 然后局蚀,同時還進行了容量上下限大小判斷
newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0
如果自動申請容量超過private int hugeCapacity(int minCapacity) { if (Integer.MAX_VALUE - minCapacity < 0) { // overflow throw new OutOfMemoryError(); } return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE; }
Integer.MAX_VALUE public static final int MAX_VALUE = 0x7fffffff;
值后拋異常。 - 最后恕稠,返回 自動申請的容量大小琅绅。
舊數(shù)組拷貝Arrays.copyOf()
在經(jīng)過上面容量申請確認后,則將舊數(shù)組拷貝到新容量的數(shù)組中谱俭。
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;
}
B. 解讀 str.getChars(0, len, value, count);
str.getChars()調用的是String的getChars()方法奉件,其實就是System.arraycopy()系統(tǒng)數(shù)組拷貝方式宵蛀。
直接上代碼:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
總結:
String類存放在常量final value[]數(shù)組中,StringBuffer县貌、StringBuilder存放在變量value[]中术陶,String對象一但創(chuàng)建后對象是不能變更的(重新賦值話每次都是重新創(chuàng)建對象和賦值操作),而StringBuffer煤痕、StringBuilder是可以變更的梧宫。故此,運行速度:StringBuilder > StringBuffer > String
StringBuffer 方法有synchronized 摆碉,故此線程安全塘匣。StringBuffer方法沒有synchronized,故此線程不安全巷帝。兩者沒有其他差異忌卤。
- String: 不可變字符序列-常量
- StringBuffer:可變字符序列-變量、效率低楞泼、線程安全
- StringBuilder:可變字符序列-變量驰徊、效率搞、線程不安全
String | StringBuffer | StringBuilder |
---|---|---|
String是不可變常量 final value[] | StringBuffer變量 value[] | StringBuilder變量 value[] 數(shù)組 |
String每次操作都會生成新的String對象堕阔,不僅效率低下棍厂,而且浪費空間 | StringBuffer 操作不會產(chǎn)生新的對象。StringBuffer自身帶有緩沖容量超陆,當字符串大小沒有超過容量時牺弹,不會分配新容量;當字符串超過容量時时呀,會自動增加容量张漂。 | StringBuilder 操作不會產(chǎn)生新的對象。StringBuilder 自身帶有緩沖容量退唠,當字符串大小沒有超過容量時鹃锈,不會分配新容量荤胁;當字符串超過容量時瞧预,會自動增加容量。 |
不可變 | 可變 | 可變 |
- | 線程安全 | 線程不安全 |
- | 支持多線程操作字符串 | 只支持單線程操作字符串 |