
之前寫過一篇關(guān)于 String 類、StringBuilder 和 StringBuffer 的基本介紹,今天從 String 類的部分源碼來看 String 類(本文基于 JDK 1.8)
- String 類的實(shí)例化
- 常用方法
- String 類的不可變
- 總結(jié)
1. String 類的構(gòu)造器
先看看 String 類中定義的一個常量和一個變量:
/** The value is used for character storage. */
//存儲字符
private final char value[];
/** Cache the hash code for the string */
//字符串的哈希值
private int hash; // Default to 0
翻了以下源碼,發(fā)現(xiàn)從 100 - 600 行左右都是寫的構(gòu)造器,數(shù)不勝數(shù),這里就選幾個常見的吧:
先來說說連開發(fā)者都覺得沒用的構(gòu)造器吧:
//初始化一個 String 對象,它代表了一個空的字符序列
//我們平常直接就 String str = "";就好了
public String() {
this.value = "".value;
}
//初始化一個和參數(shù)一模一樣的對象,除非你是要保留副本,不然沒什么用處
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
然后是根據(jù)字符數(shù)組來進(jìn)行初始化的構(gòu)造器:
/**
* 用一個字符數(shù)組當(dāng)前所儲存的字符來初始化字符串,
* 后續(xù)對數(shù)組的修改將不會對字符串有影響,感覺在做
* String 類的題目時,較常用到這個方法
*/
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
//截取字符數(shù)組中的某一部分來初始化
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
最后是根據(jù) StringBuffer 和 StringBuilder 來進(jìn)行初始化的構(gòu)造器:
//StringBuffer 是線程安全的,所以需要加鎖
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
這里還有個很有意思的包私有構(gòu)造器(看不懂):
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
后來去 Google 了一下有關(guān)字符串共享的相關(guān)內(nèi)容,還是不太明白,可能是因?yàn)槌A砍氐脑?,在常量池中存放著字符串的引用,就會?dǎo)致字符串被共享,這個構(gòu)造器可能就是共享時的構(gòu)造吧(待確定),文末給出寫文時查詢這一部分內(nèi)容的鏈接,這一部分內(nèi)容等下一篇講述常量池時一起說說
2. 常用方法
1. 基本的
常用的 length(), charAt(), isEmpty() 等等都是根據(jù)根據(jù)字符數(shù)組來做操作的,就看其中一個吧:
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
2. hashCode()
這個方法是重寫了 Object 類的 hashCode:
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;
}
在 哈希值等于 0 且字符串不為空時,才計算哈希值,否則就返回 0,計算方法就是 for 循環(huán)里的,舉個簡單的例子算一下:


字符串 ab 的哈希值就是 97 * 32 + 98 得出的
通過對字符串的哈希值的比較,能夠得出兩個字符串的值是否相同,但是卻不能用來比較兩個對象的引用:

3. join()
這個方法以前還都沒聽過,這次剛好看見了,就說一下用法:
//第一個參數(shù)時分隔符,第二個是可變參數(shù)
public static String join(CharSequence delimiter, CharSequence... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
// Number of elements not likely worth Arrays.stream overhead.
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
這里引出另一個類:StringJoiner,這里不介紹,如果只有一個字符串,就不會添加分隔符,若有多個,則在每兩個拼接的字符串中添加分隔符:

如果要拼接多個字符,并且要添加分隔符,用 join() 會方便很多
4. equals()
== 和 equals() 的區(qū)別相信都應(yīng)該知道了,前者比較引用(引用相同,值自然就相同),后者只比較值:
public boolean equals(Object anObject) {
//如果引用相同,就直接返回 true
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;
}
3. String 類的不可變
一直都在說 String 類是不可變的,究竟是怎么實(shí)現(xiàn)呢?
首先,String 類本身被聲明為 final:public final class String,不能被繼承,所以不可能重寫 String 類的方法(只讀),整個 String 類的核心:private final char value[] 也被聲明為 final
其次,在源碼中的返回值為 String 類型的方法,如果方法中修改了字符串的結(jié)構(gòu),在最后都是 return new String(xxx) 這樣子,如果沒有修改字符串結(jié)構(gòu),就返回這個字符串,也就是所說的:任何修改字符串結(jié)構(gòu)的操作都會產(chǎn)生一個新的 String 對象
4. 總結(jié)
其實(shí)對于 String 類,最重要的點(diǎn)就是不可變,至于其它的一些操作,感興趣的可以去翻一下源碼(3100 行,不多),注釋寫的非常清楚,很多操作其實(shí)伴隨注釋看一遍就可理解),就沒有寫在這篇文章里
最近開始著手準(zhǔn)備明年的春招了,所以剛好寫博客復(fù)習(xí)一下 Java 的一些基礎(chǔ)知識,下一篇會介紹一下常量池
參考資源:
很早之前的一篇文章,JDK 版本還沒到 1.8,所以看一看就好了http://chunlong.github.io/blog/2013/05/23/something-about-string/