String原始碼解析(JDK1.8)
1、string類的定義
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
-
java.io.Serializable
序列化介面沒有任何方法和域,僅用於標識序列化的語意 -
Comparable
介面只有一個compareTo(T 0)介面,用於對兩個例項化物件比較大小 -
CharSequence
介面是一個只讀的字元序列。包括length(), charAt(int index), subSequence(int start, int end)這幾個API介面,值得一提的是,StringBuffer和StringBuild也是實現了改介面
2、主要變數
/**用來儲存字串 */
private final char value[];
/** 快取字串的雜湊碼 */
private int hash; // Default to 0
/** 實現序列化的標識 */
private static final long serialVersionUID = -6849794470754667710L;
一個 String 字串實際上是一個 char 陣列。
3、構造方法
String 類的構造方法很多。可以通過初始化一個字串,或者字元陣列,或者位元組陣列等等來建立一個 String 物件。
String str1 = "abc";//注意這種字面量宣告的區別,文末會詳細介紹 String str2 = new String("abc"); String str3 = new String(new char[]{'a','b','c'});
4、常用方法
- equals(Object anObject)
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; }
String 類重寫了 equals 方法,比較的是組成字串的每一個字元是否相同,如果都相同則返回true,否則返回false。
- 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;
}
String 類的 hashCode 演算法很簡單,主要就是中間的 for 迴圈,計算公式如下:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
s 陣列即原始碼中的 val 陣列,也就是構成字串的字元陣列。這裡有個數字 31 ,為什麼選擇31作為乘積因子,而且沒有用一個常量來宣告?主要原因有兩個:
①、31是一個不大不小的質數,是作為 hashCode 乘子的優選質數之一。
②、31可以被 JVM 優化,31 * i = (i << 5) - i。因為移位運算比乘法執行更快更省效能。
- charAt(int index)
public char charAt(int index) {
//如果傳入的索引大於字串的長度或者小於0,直接丟擲索引越界異常
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
//返回指定索引的單個字元
return value[index];
}
- compareTo(String anotherString) 和 compareToIgnoreCase(String str)
我們先看看 compareTo 方法:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
原始碼也很好理解,該方法是按字母順序比較兩個字串,是基於字串中每個字元的 Unicode 值。當兩個字串某個位置的字元不同時,返回的是這一位置的字元 Unicode 值之差,當兩個字串都相同時,返回兩個字串長度之差。
compareToIgnoreCase() 方法在 compareTo 方法的基礎上忽略大小寫,我們知道大寫字母是比小寫字母的Unicode值小32的,底層實現是先都轉換成大寫比較,然後都轉換成小寫進行比較。
- concat(String str)
該方法是將指定的字串連線到此字串的末尾。
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);
}
首先判斷要拼接的字串長度是否為0,如果為0,則直接返回原字串。如果不為0,則通過 Arrays 工具類(後面會詳細介紹這個工具類)的copyOf方法建立一個新的字元陣列,長度為原字串和要拼接的字串之和,前面填充原字串,後面為空。接著在通過 getChars 方法將要拼接的字串放入新字串後面為空的位置。
注意:返回值是 new String(buf, true),也就是重新通過 new 關鍵字建立了一個新的字串,原字串是不變的。這也是前面我們說的一旦一個String物件被建立, 包含在這個物件中的字元序列是不可改變的。
- indexOf(int ch) 和 indexOf(int ch, int fromIndex)
indexOf(int ch),引數 ch 其實是字元的 Unicode 值,這裡也可以放單個字元(預設轉成int),作用是返回指定字元第一次出現的此字串中的索引。其內部是呼叫 indexOf(int ch, int fromIndex),只不過這裡的 fromIndex =0 ,因為是從 0 開始搜尋;而 indexOf(int ch, int fromIndex) 作用也是返回首次出現的此字串內的索引,但是從指定索引處開始搜尋。