String
public final class String implements Serializable, Comparable<String>, CharSequence {
private final char[] value;
......
}
final修飾的String 類额湘,以及final修飾的char[] value,表示String類不可被繼承旁舰,且value只能被初始化一次锋华。這里的value變量其實就是存儲了String字符串中的所有字符。
String a = new String("aa"):代表在堆內(nèi)存中創(chuàng)建了一個字符串對象箭窜,變量a指向該對象毯焕,而該對象又指向常量池中的字符串常量aa。
String b = "bb":代表變量b直接指向常量池中的字符串常量bb磺樱,不會在堆內(nèi)存中創(chuàng)建對象纳猫。
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
我們可以看到,String類的substring方法竹捉,concat方法续担,replace方法,都是內(nèi)部重新生成一個String對象的活孩。這也就是為什么我們?nèi)绻捎肧tring對象頻繁的進行拼接物遇,截取,替換操作效率很低下的原因憾儒。
StringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
......
}
StringBuilder類繼承AbstractStringBuilder抽象類询兴,其中StringBuilder的大部分方法都是直接調(diào)用的父類的實現(xiàn)。
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
可以看出StringBuilder的默認初始容量是16起趾,并且都調(diào)用了父類的構(gòu)造方法诗舰。
public StringBuilder append(String str) {
super.append(str);
return this;
}
public StringBuilder delete(int start, int end) {
super.delete(start, end);
return this;
}
public StringBuilder insert(int offset, String str) {
super.insert(offset, str);
return this;
}
public int indexOf(String str) {
return super.indexOf(str);
}
public StringBuilder reverse() {
super.reverse();
return this;
}
再來看他的一些操作方法也都是調(diào)用了父類的實現(xiàn),接下來我們就看看父類的具體實現(xiàn)训裆。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}
char[] value沒有final修飾眶根,代表它是可以擴展的蜀铲。接下來我們重點看一下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;
}
首先判斷是否為null属百,null也是可以append進去的记劝。
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
接著判斷數(shù)組容量是否滿足此次append,不滿足的話執(zhí)行擴容:嘗試將新容量擴為大小變成2倍+2族扰,如果不夠厌丑,直接擴充到需要的容量大小,最大容量為MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8渔呵。
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value, newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
//嘗試將新容量擴為大小變成2倍+2怒竿,如果不夠,直接擴充到需要的容量大小扩氢。
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
緊接著通過getChars調(diào)native方法getCharsNoCheck實現(xiàn)真正的append
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
//首先做一些參數(shù)校驗耕驰,這里就省略了
......
getCharsNoCheck(srcBegin, srcEnd, dst, dstBegin);
}
@FastNative
native void getCharsNoCheck(int start, int end, char[] buffer, int index);
整個StringBuilder的append方法,本質(zhì)上是調(diào)用System的native方法录豺,直接將String 類型的str字符串中的字符數(shù)組朦肘,拷貝到了StringBuilder的字符數(shù)組中。
最后說下StringBuilder的toString方法:
@Override
public String toString() {
if (count == 0) {
return "";
}
return StringFactory.newStringFromChars(0, count, value);
}
這里的toString方法直接new 一個String對象巩检,將StringBuilder對象的value進行一個拷貝厚骗,重新生成一個對象示启,不共享之前StringBuilder的char[]兢哭。
以上就是StringBuilder的拼接字符串的原理分析,可以發(fā)現(xiàn)沒有像String一樣去重新new 對象夫嗓,所以在頻繁的拼接字符上迟螺,StringBuilder的效率遠遠高于String類。
StringBuffer
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
//transient關(guān)鍵字的作用:被修飾的成員屬性變量不被序列化
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);
}
}
其構(gòu)造方法和成員基本和StringBuilder一樣舍咖,唯一區(qū)別就是char[]不允許序列化矩父。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
其對應(yīng)的內(nèi)部方法都加了synchronized關(guān)鍵字,所以是線程安全的數(shù)據(jù)結(jié)構(gòu)排霉。
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, 0, count);
}
如果StringBuffer對象此時存在toStringCache窍株,在多次調(diào)用其toString方法時,其new出來的String對象是會共享同一個char[] 內(nèi)存的攻柠,達到共享的目的球订。但是StringBuffer只要做了修改,其toStringCache屬性值都會置null處理瑰钮。這也是StringBuffer和StringBuilder的一個區(qū)別點冒滩。
總結(jié)
String 類不可變,內(nèi)部維護的char[] 數(shù)組長度不可變浪谴,為final修飾开睡,String類也是final修飾因苹,不存在擴容。字符串拼接篇恒,截取扶檐,都會生成一個新的對象。頻繁操作字符串效率低下婚度,因為每次都會生成新的對象蘸秘。
StringBuilder 類內(nèi)部維護可變長度char[] , 初始化數(shù)組容量為16蝗茁,存在擴容醋虏, 其append拼接字符串方法內(nèi)部調(diào)用System的native方法,進行數(shù)組的拷貝哮翘,不會重新生成新的StringBuilder對象颈嚼。非線程安全的字符串操作類, 其每次調(diào)用 toString方法而重新生成的String對象饭寺,不會共享StringBuilder對象內(nèi)部的char[]阻课,會進行一次char[]的copy操作。
StringBuffer 類內(nèi)部維護可變長度char[]艰匙, 基本上與StringBuilder一致限煞,但其為線程安全的字符串操作類,大部分方法都采用了Synchronized關(guān)鍵字修改员凝,以此來實現(xiàn)在多線程下的操作字符串的安全性署驻。其toString方法而重新生成的String對象,會共享StringBuffer對象中的toStringCache屬性(char[])健霹,但是每次的StringBuffer對象修改旺上,都會置null該屬性值。