可變性
首先我們看下String的原始碼
/** The value is used for character storage. */
private final char value[];
複製程式碼
由此可以看出,String類中使用final關鍵字字元陣列來儲存字串,所以是不可變的。
注:在Java中,final關鍵字可以用來修飾類、方法和變數(包括成員變數和區域性變數)
1、當用final修飾一個類時,表明這個類不能被繼承。也就是說,如果一個類你永遠不會讓他被繼承,就可以用final進行修飾。
2、final修飾的方法不能被重寫。
3、當final修飾一個基本資料型別時,表示該基本資料型別的值一旦在初始化後便不能發生變化;如果final修飾一個引用型別時,則在對其初始化之後便不能再讓其指向其他物件了,但該引用所指向的物件的內容是可以發生變化的。
再來看下StringBuilder,StringBuffer的原始碼
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
...
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
...
}
複製程式碼
由原始碼可以看出,StringBuilder、StringBuffer都繼承自AbstractStringBuilder類,我們接下來再看下AbstractStringBuilder類的原始碼
/**
* The value is used for character storage.
*/
char[] value;
複製程式碼
由原始碼可以看出,StringBuilder、StringBuffer在AbstractStringBuilder中也是使用字元陣列儲存字串的,但是這兩種都是可變的。
執行緒安全性
String中的物件是不可變的,也可以理解為常量,執行緒安全的。
接下來我們繼續看下StringBuffer原始碼,我在這隨機擷取了原始碼中的一些方法
/**
* @throws StringIndexOutOfBoundsException {@inheritDoc}
* @since 1.2
*/
@Override
public synchronized String substring(int start, int end) {
return super.substring(start, end);
}
/**
* @throws StringIndexOutOfBoundsException {@inheritDoc}
*/
@Override
public synchronized StringBuffer insert(int offset, Object obj) {
toStringCache = null;
super.insert(offset, String.valueOf(obj));
return this;
}
/**
* @since 1.4
*/
@Override
public int indexOf(String str) {
// Note, synchronization achieved via invocations of other
StringBuffer methods
return super.indexOf(str);
}
複製程式碼
由原始碼可以看出StringBuffer對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的。
再來看下StringBuilder原始碼中的一些方法
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
/**
* @throws StringIndexOutOfBoundsException {@inheritDoc}
*/
@Override
public StringBuilder insert(int index, char[] str, int offset,
int len)
{
super.insert(index, str, offset, len);
return this;
}
/**
* @throws StringIndexOutOfBoundsException {@inheritDoc}
*/
@Override
public StringBuilder replace(int start, int end, String str) {
super.replace(start, end, str);
return this;
}
複製程式碼
由原始碼可以看出,StringBuilder並沒有對方法進行加同步鎖,所以是非執行緒安全的。
效能
每次對 String 型別進行改變的時候,都會生成一個新的 String 物件,然後將指標指向新的 String 物件。
StringBuffer 每次都會對 StringBuffer 物件本身進行操作,而不是生成新的物件並改變物件引用。
相同情況下使用 StringBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的效能提升,但卻要冒多執行緒不安全的風險。
1、操作少量的資料 : String
2、單執行緒操作字串緩衝區下操作大量資料 : StringBuilder
3、多執行緒操作字串緩衝區下操作大量資料 : StringBuffer