JDK原始碼分析系列---String,StringBuilder,StringBuffer
1.String
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { //儲存字元,final修飾 private final char value[]; //快取hash code,預設0 private int hash; //序列號 private static final long serialVersionUID = -6849794470754667710L; //宣告可序列化欄位 private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; }
1.1 基本屬性
- char value[],用來儲存字串物件的字元陣列
- int hash,用來快取字串的hash code,預設值為0
- long serialVersionUID,用來序列化的序列版本號
- ObjectStreamField[],可序列化類的欄位說明
1.2 常用構造器
public String() {
this.value = "".value;
}
初始化新建立的物件,表示空字串""。請注意,此建構函式是不需要使用的,因為字串是不可變的
String str = new String(); 本質上是建立了一個空的字元陣列,str的長度為0
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
初始化新建立的物件,表示和引數一樣的字串,換句話說是建立了和引數一樣的物件副本,除非需要顯示的宣告副本,否則該建構函式是不需要的,因為字串是不可變的
public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } }
把StringBuffer的內容複製到String物件中,隨後修改StringBuffer物件的值,並不會影響String物件
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
把StringBuilder的內容複製到String物件中,隨後修改StringBuilder的值,並不會影響String物件;
此建構函式是為了把StringBuilder轉移到String物件,但是推薦使用StringBuilder的toString()方法,因為執行更快
1.3 常用方法
//返回字串的長度
public int length() {
return value.length;
}
//比較兩個String值是否相等
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
//生成hash code
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
equals方法的判斷流程:
- 首先判斷兩個物件是否相同,若相同返回true;若不同,下一步
- 判斷引數是否為String物件,若不是,返回false;若是,下一步
- 判斷兩個String的長度是否相等,若不是,返回false;若是,下一步
- 按字元陣列索引依次比較字元,如果有任一不相同,返回false,否則返回true
1.2 為什麼說String是不可變物件?
- 儲存字元的陣列value[]是final修飾的,值不可更改.
2. AbstractStringBuilder
可變的字元序列,StringBuilder和StringBuffer都繼承了該類,要了解StringBuilder和StringBuffer首先先了解AbstractStringBuilder.
2.1 基本屬性
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
}
- char[] value:儲存字元的陣列
- int count:使用的字元的數量
2.2 構造器
/**
* 無參構造器,用於子類序列化
*/
AbstractStringBuilder() {
}
/**
* 指定字元陣列容量
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
2.3 常用方法
/**
* 返回字元的數量
*/
@Override
public int length() {
return count;
}
/**
* 返回當前可儲存字元的最大數量,即容量
*/
public int capacity() {
return value.length;
}
/**
* 保證當前容量大於等於指定的最小數量minimumCapacity,會呼叫擴容方法
*/
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
/**
* 擴容,只有minimumCapacity大於當前容量,才會copy陣列擴容
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
/**
* 當前物件拼接字串str
* 如果引數為null,那麼最終字串為"null",如果引數型別為boolean,那麼返回的是"true"或"false"
* 例1: "abc".append(null) = "abcnull"
* 例2: "abc".append("def") = "abcdef"
*/
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;
}
3. StringBuilder
可變的字元序列,非執行緒安全,StringBuilder和StringBuffer的實現方法很相似,區別在於是否執行緒安全,在單執行緒的情況下可使用StringBuilder,因為它比StringBuffer執行更快.StringBuilder繼承了AbstractStringBuilder類.
3.1 基本屬性
繼承父類
3.2 構造器
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
/**
*序列號
*/
static final long serialVersionUID = 4383685877147921099L;
/**
* 預設容量16
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* 指定初始容量
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* 把String字串初始化到物件中,容量變為str的長度+16
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
/**
* 把字元初始化到物件中,容量變為字元的長度+16
*/
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
3.3 常用方法
同父類
4. StringBuffer
可變的字元序列,執行緒安全,StringBuffer繼承了AbstractStringBuilder類.
4.1 基本屬性
繼承父類,同時還有以下屬性
public final class StringBuffer extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
/**
* 最後一次呼叫toString返回值的快取
* 當StringBuffer被修改時該快取被清除
*/
private transient char[] toStringCache;
/** 序列號 */
static final long serialVersionUID = 3388685877147921107L;
}
4.2 構造器
同StringBuilder
4.3 常用方法
與StringBuilder方法基本相同,區別在於在StringBuilder的方法上加了synchronized鎖. 不同的地方還有以下這種情況
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
每次呼叫toString的時候,都會建立一個快取toStringCache,在每次修改物件的時候清空快取,但是這裡的快取具體什麼作用呢?大家可以在評論區留言!
5.總結
綜上所述,可以得出相同點是:都可以建立字串,不同在於String是不可變的,不存線上程安全問題;StringBuilder和StringBuffer字串是可變的,StringBuilder執行緒不安全,適合單執行緒使用,StringBuffer執行緒安全,適合多執行緒;對於頻繁需要字串的拼接操作時,不建議使用String,因為每次都需要建立一個String對