Java基礎面試題(5)----String,StringBuffer,StringBulider的區別
問題
- 說明String和StringBulider的區別?
- 說明StringBulider和StringBuffer的區別
解析
1. 首先String和StringBuilder的區別
在java中提供了三個類String,StringBuffer,StringBuilder來表示和操作字串,字串就是多少個字元的組合,這三個類都是使用final修飾的類,因此建立物件是在方法區中進行,儲存時間比較長,耗費小部分記憶體,提高效率。 類關係圖如下 String的內容是不可變的,String的底層是使用了final修飾的字元陣列,首先我們來看一下String拼接方法concat();
//String的成員變數使用了final修飾 private final char value[]; //String進行拼接的方法 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); }
- 首先定義一個final修飾的字元陣列,是一個常量,當我們使用字串拼接的時候,實際對新增的str進行復制,重新建立靜態字元陣列,增加char buf[]陣列長度,重新建立String的物件。所以字串內容不可變,在進行拼接的時候,實際上是物件的重新建立String物件。
——————————————————————————————————————————
- 我們在來看StringBuilder的字串拼接的方法append(); 首先StringBuilder繼承 AbstractStringBuilder類,成員變數定義的是沒有final修飾的字串陣列 PS:關鍵字transient解釋
StringBuilder的append();方法
//這裡是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; } /** * This method has the same contract as ensureCapacity, but is * never synchronized. * append方法的呼叫,英文的意思是,這個方法結構和ensureCapacity相同 * 父類的ensureCapacity方法,實際是執行緒不安全的 * */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) //如果輸入的資料長度大於定義的陣列長度 expandCapacity(minimumCapacity); } //再次呼叫方法,實現對字元陣列擴容,擴容大小為輸入字串長度*2+2 /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ 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); } //最終實現 ,建立新的str,呼叫的是本地方法,無法看到原始碼 str.getChars(0, len, value, count);
因此,我們可以的出結論,在進行字串拼接的時候:
- String類中成員變數是final修飾的字元陣列,建立後長度確定,拼接的時候,需要重新建立新的物件的方法,效率低。
- StringBuilder類中成員變數是沒有修飾的字元陣列,當我們呼叫append();方法進行拼接的時候,呼叫父類的方法對陣列擴容,本地方法實現字串的拼接。
StringBuffer和StringBuilder的區別
StringBuilder
- 上面我們已經看到,在對StringBuilder進行擴容的時候,呼叫AbstractStringBuilder父類的方法,通過上面的擴容原始碼可以指代是執行緒不安全的,因此可以實現快速的新增,但是當我們使用多執行緒進行資料新增的時候,會出現無序的問題。
—————————————————————————————————————————— StringBuffer
- 首先StringBuffer的建立物件的時候,字元陣列長度不確定,這一點與StringBuilder相同,如下:
private transient char[] toStringCache;
PS:這裡我們對transient關鍵字簡單的說明
-
StringBuffler類是實現了序列化介面的Seriailzable關鍵字,在進行網路傳輸和持久化的時候,保證物件的有序。可以進行有效讀取。
-
然而在實際開發過程中,我們常常會遇到這樣的問題,這個類的有些屬性需要序列化,而其他屬性不需要被序列化,打個比方,如果一個使用者有一些敏感資訊(如密碼,銀行卡號等),為了安全起見,不希望在網路操作(主要涉及到序列化操作,本地序列化快取也適用)中被傳輸,這些資訊對應的變數就可以加上transient關鍵字。換句話說,這個欄位的生命週期僅存於呼叫者的記憶體中而不會寫到磁盤裡持久化。
-
java 的transient關鍵字為我們提供了便利,你只需要實現Serilizable介面,將不需要序列化的屬性前新增關鍵字transient,序列化物件的時候,這個屬性就不會序列化到指定的目的地中。
-
我們再來看StringBuffer中的方法
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
* @see #length()
*/
@Override
public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
toStringCache = null;
value[index] = ch;
}
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
基本所有的操作方法都使用關鍵字synchronized修飾,保證執行緒的安全。
最後,得出的結論是:
從執行效率來講:
String<StringBuffer<StringBuilder
從執行緒安全來講
StringBuffer執行緒安全 StringBuilder執行緒不安全