Java 基礎:String 類原始碼分析
String 類實現介面屬性1、私有屬性私有類 CaseInsensitiveComparatorString 構造方法1、無參建構函式2、引數為 String 型別3、引數為字元陣列4、引數為 int 陣列5、引數為位元組陣列6、引數為 StringBuilder 或 StringBuffer7、特殊的 protected 構造方法其他方法1、concat 連線函式2、getBytes3、equals 和 hashCode4、比較方法5、前字尾判定6、索引獲取7、字串擷取8、字串替換9、valueOf10、intern()方法參考連結
Java 中資料型別分為兩大類:基礎資料型別(byte,short,int,long,float,double,char,boolean)和引用型別(String 型別和基礎資料型別的包裝類),可以看出 String 型別是非常特殊的,同時也是編寫程式碼過程中使用比較頻繁的一種型別,為了更好的瞭解該型別,決心鑽研一下 String 類原始碼,希望能有所收穫。
String 類實現介面
public final class String implements Serializable, Comparable<String>,159); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-title">CharSequence
複製程式碼
從該類的宣告中我們可以看出String是final型別的,表示該類不能被繼承,同時該類實現了三個介面。
- Serializable 介面是為了實現類物件的序列化,主要是能把堆記憶體中的物件的生命週期延長,做持久化操作。當下次再需要這個物件的時候,我們不用 new了,直接從硬碟中讀取就可以了。
- Comparable 介面強行對實現它的每個類的物件進行整體排序。此排序被稱為該類的自然排序 ,類的 compareTo 方法被稱為它的自然比較方法 。實現此介面的物件列表(和陣列)可以通過 Collections.sort(和 Arrays.sort)進行自動排序。實現此介面的物件可以用作有序對映表中的鍵或有序集合中的元素,無需指定比較器。後續會講解 String 類中的 compareTo 方法。
- CharSequence 是一個介面,它只包括 length(),charAt(int index),subSequence(int start,int end)這幾個 API 介面。除了 String 實現了 CharSequence 之外,StringBuffer 和 StringBuilder 也實現了 CharSequence 介面。CharSequence 就是字元序列,String,StringBuilder 和 StringBuffer 本質上都是通過字元陣列實現的!
屬性
1、私有屬性
String 的底層是由 char 陣列構成的
private final char[] value;
int hash;
複製程式碼
由於底層 char 陣列是 final 的,所以 String 物件是不可變的,且不可被繼承。 value:是一個 private
final 修飾的 char 陣列,String 類是通過該陣列來存在字串的。 hash:是一個 private 修飾的 int
變數,用來存放 String 物件的 hashCode。
private staticfinal long serialVersionUID = -6849794470754667710L;
static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
複製程式碼
因為String實現了Serializable介面,所以支援序列化和反序列化支援。Java的序列化機制是通過在執行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常(InvalidCastException)。
私有類 CaseInsensitiveComparatorstatic CaseInsensitiveComparator Comparator<Serializable
複製程式碼
該類同樣實現了 Comparator 和 Serializable 介面,用於 String 類物件的排序。
int compare(String var1, String var2) {
int var3 = var1.length();
int var4 = var2.length();
int var5 = Math.min(var3, var4);
for(int var6 = 0; var6 < var5; ++var6) {
char var7 = var1.charAt(var6);
char var8 = var2.charAt(var6);
if (var7 != var8) {
var7 = Character.toUpperCase(var7);
var8 = Character.toUpperCase(var8);
if (var7 != var8) {
var7 = Character.toLowerCase(var7);
var8 = Character.toLowerCase(var8);
if (var7 != var8) {
return var7 - var8;
}
}
}
}
return var3 - var4;
}
複製程式碼
從原始碼來看,在該類中存在一個 compare 方法,方法對兩個物件的比較流程如下:迴圈的次數為長度最小的字串的長度;從頭開始比較每個字元,如果不相等則轉換為大寫再比較,再不相等轉換為小寫比較,最後字元之間相減。減法操作會將獲取字元的 hashCode,然後相減(漢字也是如此)。如果迴圈過程中字元比較都相等,最後返回兩個字串物件長度的差值。
String 構造方法
1、無參建構函式
public String() {
this.value = "".value;
}
複製程式碼
String s = new String();
System.out.println(s); //值為"",也就是空字串
System.out.println(s.hashCode()); //hash未賦初始值,所以預設值為0,後期再詳細講hashCode方法
複製程式碼
2、引數為 String 型別
String(String var1) {
this.value = var1.value;
this.hash = var1.hash;
}
複製程式碼
首先宣告一下 Java 的語法是允許在一個類中訪問該類的例項物件的私有屬性的。但是其他類就不可了。
3、引數為字元陣列
String(char[] var1) {
this.value = Arrays.copyOf(var1, var1.length);
}
//var2是字元陣列開始擷取的位置,從0開始;var3是擷取的長度
char[] var1, int var2,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var3) {
if (var2 < 0) {
throw new StringIndexOutOfBoundsException(var2);
} else {
if (var3 <= 0) {
if (var3 < 0) {
new StringIndexOutOfBoundsException(var3);
}
if (var2 <= var1.length) {
"".value;
return;
}
}
if (var2 > var1.length - var3) {
new StringIndexOutOfBoundsException(var2 + var3);
} else {
this.value = Arrays.copyOfRange(var1, var2, var2 + var3);
}
}
}
複製程式碼
同樣都是用字元陣列建立 String,前者是複製完整的字元陣列到 String 中的 value 值,後者是從擷取字元陣列的一部分內容複製到 String 中。使用 Arrays.copyOf 方法或 Arrays.copyOfRange 方法進行復制,建立一個新的字串物件,隨後修改的字元陣列不影響新建立的字串。
4、引數為 int 陣列
int[] var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var3) {
if (var2 < 0) {
new StringIndexOutOfBoundsException(var2);
} else {
if (var3 <= 0) {
if (var3 < 0) {
new StringIndexOutOfBoundsException(var3);
}
if (var2 <= var1.length) {
"".value;
return;
}
}
if (var2 > var1.length - var3) {
new StringIndexOutOfBoundsException(var2 + var3);
} else {
int var4 = var2 + var3;
int var5 = var3;
int var7;
int var6 = var2; var6 < var4; ++var6) {
var7 = var1[var6];
if (!Character.isBmpCodePoint(var7)) {//判斷是否為負數,正數為true
if (!Character.isValidCodePoint(var7)) {
new IllegalArgumentException(Integer.toString(var7));
}
++var5;
}
}
char[] var10 = new char[var5];
var7 = var2;
int var8 = 0; var7 < var4; ++var8) {
int var9 = var1[var7];
if (Character.isBmpCodePoint(var9)) {
var10[var8] = (char)var9;
} else {
Character.toSurrogates(var9, var10, var8++);
}
++var7;
}
this.value = var10;
}
}
}
複製程式碼
需要注意的是:作為引數的 int 陣列中值,至少需要滿足“大寫字母(A-Z):65 (A)~ 90(Z);小寫字母(a-z):97(a) ~ 122(z);字元數字(‘0’ ~ ‘9’):48(‘0’) ~ 57(‘9’)”的條件。當陣列中值為其他數字時,得到的字串結果可能為空或特殊符號。
5、引數為位元組陣列
在 Java 中,String 例項中儲存有一個 char[] 字元陣列,char[] 字元陣列是以 unicode 碼來儲存的,String 和 char 為記憶體形式。
byte 是網路傳輸或儲存的序列化形式,所以在很多傳輸和儲存的過程中需要將 byte[] 陣列和 String 進行相互轉化。所以 String 提供了一系列過載的構造方法來將一個字元陣列轉化成 String,提到 byte[] 和 String 之間的相互轉換就不得不關注編碼問題。
String(byte[] bytes, Charset charset)
複製程式碼
該構造方法是指通過 charset 來解碼指定的 byte 陣列,將其解碼成 unicode 的 char[] 陣列,構造成新的 String。
這裡的 bytes 位元組流是使用 charset 進行編碼的,想要將他轉換成 unicode 的 char[] 陣列,而又保證不出現亂碼,那就要指定其解碼方式。
通過位元組陣列構造 String 有很多形式,會使用 StringCoding.decode 方法進行解碼,按照是否指定解碼方式分的話可以分為兩種:
a、
byte[] var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var3) {
checkBounds(var1, var3);
this.value = StringCoding.decode(var1, var3);
}
byte[] var1) {
this((byte[])var1, 0, var1.length);
}
複製程式碼
這兩種構造方法沒有指定編碼格式,預設使用 ISO-8859-1 編碼格式進行編碼操作。
char[] decode(byte[] var0,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var2) {
String var3 = Charset.defaultCharset().name();
try {
return decode(var3, var0, var1, var2);
} catch (UnsupportedEncodingException var6) {
warnUnsupportedCharset(var3);
try {
return decode("ISO-8859-1", var2);
} catch (UnsupportedEncodingException var5) {
MessageUtils.err("ISO-8859-1 charset not available: " + var5.toString());
System.exit(1);
return null;
}
}
}
複製程式碼
b、
byte bytes[], Charset charset)
String(int offset,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int length, String charsetName)
複製程式碼
當構造方法引數中帶有 charsetName 或者 charset 的時候,使用的解碼的字符集就是我們指定的 charsetName 或者 charset。
6、引數為 StringBuilder 或 StringBuffer
String(StringBuffer var1) {
synchronized(var1) {
this.value = Arrays.copyOf(var1.getValue(), var1.length());
}
}
String(StringBuilder var1) {
複製程式碼
這兩個構造方法是很少用到的,平時多使用 StringBuffer.toString 方法或者 StringBuilder.toString 方法。其中 StringBuffer.toString 是呼叫 String(char[] var1,boolean var2)
;而 StringBuilder.toString 則是呼叫 7、特殊的 protected 構造方法
String(boolean var2) {
this.value = var1;
}
複製程式碼
從程式碼中我們可以看出,該方法和 String(char[] value)有兩點區別:
- 第一個區別:該方法多了一個引數:boolean var2,這個引數上述我們也提到過,在 StringBuffer.toString 方法中有使用過,當時傳值為 true。但是實際使用過程中可以通過該建構函式直接進行字串物件的生成,加入這個 var2 的可以有效區分於 String(char[] value) 方法,不加這個引數就沒辦法定義這個函式,只有引數是不同才能進行過載。
- 第二個區別:具體的方法實現不同。我們前面提到過 String(char[] value) 方法在建立 String 的時候會用到 Arrays 的 copyOf 方法將 value 中的內容逐一複製到 String 當中,而這個 String(char[] var1,boolean var2) 方法則是直接將 var1的引用賦值給 String 的 value。那麼也就是說,這個方法構造出來的 String 和引數傳過來的 char[] var1共享同一個陣列。
在網上看到說是有兩點區別,關於第二點區別,關鍵在於呼叫方呼叫該構造方法前是怎麼處理的,在 StringBuffer.toString 方法中可以詳細的看出 new String(char[] var1,boolean var2) 的使用。
synchronized String toString() {
if (this.toStringCache == null) {
this.toStringCache = Arrays.copyOfRange(this.value,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">this.count);
}
new String(this.toStringCache,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">true);
}
複製程式碼
其中 toStringCache 表示快取,用來儲存上一次呼叫 toString 的結果,如果 value 的字串序列發生改變,就會將它清空。首先判斷 toStringCache 是否為 null,如果是先將 value 通過 Arrays.copyOfRange 方法複製到快取裡,然後使用 toStringCache new一個 String。
綜上,我認為該構造方法雖然無法拿來直接使用,可是在別的地方可以使用,比如說 StringBuffer.toString 方法中。至於為何要再構建這麼一個特殊的構造方法,而不是直接使用 String(char[] value) 方法,原因在於 StringBuffer 中的 toStringCache 屬性存在,它的意義不支援在 toString 方法中直接使用 String(char[] value) 方法。(該部分僅為個人觀點,如有差錯,請指正!)
為什麼 Java 會提供這樣一個方法呢?
- 效能好:一個是直接給陣列賦值(如果 StringBuffer 的 value 字串序列不發生改變,僅需要複製一次即可),一個是逐一拷貝,當然是直接賦值快了。
- 節約記憶體:該方法之所以設定為 protected,是因為一旦該方法設定為公有,在外面可以訪問的話,如果構造方法沒有對 arr 進行拷貝,那麼其他人就可以在字串外部修改該陣列,由於它們引用的是同一個陣列,因此對 arr 的修改就相當於修改了字串,那就破壞了字串的不可變性。
- 安全的:對於呼叫他的方法來說,由於無論是原字串還是新字串,其 value 陣列本身都是 String 物件的私有屬性,從外部是無法訪問的,因此對兩個字串來說都很安全。
講到這裡,就需要講下 String 類中還有沒有其他的方法像這個建構函式那樣“效能好的、節約記憶體的、安全”。其實在 Java7 之前 String 類中也有很多這樣的方法,比如 substring,replace,concat,valueOf
等方法,實際上它們使用的是 String(int var1,char[] var3)方法來實現。
但是在 Java 7 之後,substring 已經不再使用這種“優秀”的方法了,以下是 Java8 中的原始碼:
public String substring(int var1) {
if (var1 < 0) {
new StringIndexOutOfBoundsException(var1);
} int var2 = this.value.length - var1;
if (var2 < 0) {
new StringIndexOutOfBoundsException(var2);
} return var1 == 0 ? this : 複製程式碼
Java8 中 substring 方法涉及到的 new String 原始碼如下:
複製程式碼
會對傳進來的 value 值通過 Arrays.copyOfRange 方法進行拷貝。
反觀 Java6 中 substring 方法涉及到的 new String 原始碼如下:
char[] var3) {
this.value = var3;
this.offset = var1;
this.count = var2;
}
複製程式碼
同 new String(char[] var1,boolean var2) 建構函式的第二個特點一樣,構造出來的 String 和引數傳過來的 char[] value 共享同一個陣列 。這就可能會造成記憶體洩漏問題。
看一個例子,假設一個方法從某個地方(檔案、資料庫或網路)取得了一個很長的字串,然後對其進行解析並提取其中的一小段內容,這種情況經常發生在網頁抓取或進行日誌分析的時候。
下面是示例程式碼:
String aLongString = "...averylongstring...";
String aPart = aLongString .substring(20, 40); //aPart字串共享aLongString部分資料
return aPart;
複製程式碼
在這裡 aLongString 只是臨時的,真正有用的是 aPart,其長度只有 20 個字元,但是它的內部陣列卻是從 aLongString 那裡共享的,因此雖然 aLongString 本身可以被回收,但它的內部陣列卻不能釋放。這就導致了記憶體洩漏。如果一個程式中這種情況經常發生有可能會導致嚴重的後果,如記憶體溢位,或效能下降。
新的實現雖然損失了效能,而且浪費了一些儲存空間,但卻保證了字串的內部陣列可以和字串物件一起被回收,從而防止發生記憶體洩漏,因此新的 substring 比原來的更健壯。
其他方法length() 返回字串長度
isEmpty() 返回字串是否為空
charAt(int index) 返回字串中第(index+1)個字元(陣列索引)
char[] toCharArray() 轉化成字元陣列
trim()去掉兩端空格
toUpperCase()轉化為大寫
toLowerCase()轉化為小寫
boolean matches(String regex) 判斷字串是否匹配給定的regex正則表示式
contains(CharSequence s) 判斷字串是否包含字元序列 s
String[] split(String regex,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int limit) 按照字元 regex將字串分成 limit 份
String[] split(String regex) 按照字元 regex 將字串分段
複製程式碼
1、concat 連線函式
concat(String var1) {
int var2 = var1.length();
if (var2 == 0) {
this;
} int var3 = this.value.length;
char[] var4 = Arrays.copyOf(new String(var4,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">true);
}
}
複製程式碼
int var2 = var1.length();
if (var2 == 0) {
this;
} int var3 = this.value.length;
char[] var4 = Arrays.copyOf(new String(var4,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">true);
}
}
複製程式碼
拼接 var1 會生成一個新的字串物件,對原有字串無影響。
2、getBytes
byte[] getBytes() 使用平臺的預設字符集將此 String 編碼為 byte 序列,並將結果儲存到一個新的 byte 陣列中。
byte[] getBytes(String var1) 使用指定的字符集將此 String 編碼為 byte[] getBytes(Charset var1) 使用給定的 charset 將此 String 編碼到 byte 序列,並將結果儲存到新的 byte 陣列。
void getBytes(byte[] var3,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var4) 已過時
複製程式碼
值得注意的是,在使用這些方法的時候一定要注意編碼問題。比如:String s = "你好,世界!"; byte[] bytes = s.getBytes();
這段程式碼在不同的平臺上執行得到結果是不一樣的。由於沒有指定編碼方式,所以在該方法對字串進行編碼的時候就會使用系統的預設編碼方式。
在中文作業系統中可能會使用 GBK 或者 GB2312 進行編碼,在英文作業系統中有可能使用 iso-8859-1 進行編碼。這樣寫出來的程式碼就和機器環境有很強的關聯性了,為了避免不必要的麻煩,要指定編碼方式。
3、equals 和 hashCode
equals(Object var1) {
this == var1) {
true;
} if (var1 instanceof String) {
String var2 = (String)var1;
this.value.length;
if (var3 == var2.value.length) {
char[] var4 = this.value;
char[] var5 = var2.value;
int var6 = 0; var3-- != 0; ++var6) {
if (var4[var6] != var5[var6]) {
false;
}
}
true;
}
}
false;
}
}
hashCode() {
int var1 = this.hash;
if (var1 == 0 && this.value.length > 0) {
char[] var2 = this.value;
int var3 = 0; var3 < this.value.length; ++var3) {
var1 = 31 * var1 + var2[var3];
}
this.hash = var1;
}
複製程式碼
在 Java 中基於各種資料型別分析 == 和 equals 的區別一節中講到 String 類有重寫自己的 equals 和 hashCode 方法,所以它倆需要一起講述。
首先是 equals 方法,它比較的流程是:字串物件相同(即自我比較);型別一致且長度相等時,比較字元內容是否相同。
然後是 hashCode 方法,如果 hash 值不等於 0,且 value.length 大於 0,則進行 hash 值計算。這裡重點說下 var1 == 0
這一判定條件,var1 是一個 int 型別的值,預設值為 0,因此 0 可以表示可能未執行過 hash 計算,但不能表示一定未執行過 hash 計算,原因是我們現在還不確定 hash 計算後是否會產生 0 值;
執行 hash 計算後,會不會產生值為 0 的 hash呢?根據 hash 的計算邏輯,當 val2[0] = 0 時,根據公式 var1 = 31 * var1 + val2[i];
進行計算, var1 的值等於 0。但是經過查詢 ASCII 表發現,null 的 ASCII 值為 0 。顯然 val2[0]中永遠不可能存放 null,因此 hash 計算後不會產生 0 值, var1== 0 可以作為是否進行過 hash 計算的判定條件。
最後得到計算公式為:
val2[0]*31^(n-1) + val2[1]*31^(n-2) + ... + val2[n-1]
複製程式碼
為什麼要使用這個公式,就是在儲存資料計算 hash 地址的時候,我們希望儘量減少有同樣的 hash 地址。如果使用相同 hash 地址的資料過多,那麼這些資料所組成的 hash 鏈就更長,從而降低了查詢效率。
所以在選擇係數的時候要選擇儘量長的係數並且讓乘法儘量不要溢位的係數,因為如果計算出來的 hash 地址越大,所謂的“衝突”就越少,查詢起來效率也會提高。
選擇31作為因子的原因: 為什麼 String hashCode 方法選擇數字31作為乘子
有這樣一道面試題“兩個物件的 hashCode()相同,則 equals()也一定為 true,對嗎?
”答案:不對,兩個物件的 hashCode()相同,equals()不一定 true。
String str1 = "通話";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
//結果
str1:1179395 | str2:1179395
false
複製程式碼
很顯然“通話”和“重地”的 hashCode() 相同,然而 equals() 則為 false,因為在散列表中,hashCode()相等即兩個鍵值對的雜湊值相等,然而雜湊值相等,並不一定能得出鍵值對相等。
4、比較方法
equals(Object anObject); 比較物件
contentEquals(StringBuffer sb); 與StringBuffer物件比較內容
contentEquals(CharSequence var1); 與字元比較內容
equalsIgnoreCase(String anotherString);忽略大小寫比較字串物件
compareTo(String anotherString); 比較字串
compareToIgnoreCase(String str); 忽略大小寫比較字串
regionMatches(int toffset, String other,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int ooffset,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int len)區域性匹配
boolean ignoreCase,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int len) 可忽略大小寫區域性匹配
複製程式碼
contentEquals 方法
contentEquals(CharSequence var1) {
instanceof AbstractStringBuilder) {
instanceof StringBuffer) {
synchronized(var1) {
this.nonSyncContentEquals((AbstractStringBuilder)var1);
}
} this.nonSyncContentEquals((AbstractStringBuilder)var1);
}
} else instanceof String) {
this.equals(var1);
} this.value;
int var3 = var2.length;
if (var3 != var1.length()) {
false;
} int var4 = 0; var4 < var3; ++var4) {
if (var2[var4] != var1.charAt(var4)) {
false;
}
}
true;
}
}
}
複製程式碼
String 、StringBuilder、StringBuffer 都實現了 CharSequence 介面,所以上述方法可以接收這三種型別的引數。另外 StringBuilder、StringBuffer 繼承了 AbstractStringBuilder 父類,所以它倆通過 nonSyncContentEquals 方法進行比較,注意 StringBuffer 需要考慮執行緒安全,加鎖之後再呼叫。
compareTo 方法
compareTo(String var1) {
this.value.length;
int var3 = var1.value.length;
int var4 = Math.min(var2, var3);
char[] var5 = this.value;
char[] var6 = var1.value;
int var7 = 0; var7 < var4; ++var7) {
char var8 = var5[var7];
char var9 = var6[var7];
if (var8 != var9) {
return var8 - var9;
}
}
return var2 - var3;
}
複製程式碼
通過下面這個例子進行展示:
String s1 = new String("abc");
String s2 = "abcdfg");
System.out.println(s2.compareTo(s1)); //3
System.out.println(s1.compareTo(s2)); //-3
s2 = "fghjkl");
System.out.println(s2.compareTo(s1)); //5
複製程式碼
當兩個物件內容完全一致時,返回結果為 0;在字串最小長度下,如果有不同的字元,系統則自動轉換為 int 型別做差值。
5、前字尾判定
startsWith(String var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var2) 測試此字串從指定索引開始的子字串是否以指定字首開始
startsWith(String var1) 測試此字串是否以指定的字首開始。
endsWith(String var1) 測試此字串是否以指定的字尾結束。
複製程式碼
6、索引獲取
indexOf(int var1) 返回指定字元(int 轉 char)在此字串中第一次出現處的索引。從0索引開始
int var2) 返回在此字串中第一次出現指定字元處的索引,從指定的索引開始搜尋。var2 小於字串的長度
indexOf(String var1) 返回指定子字串在此字串中第一次出現處的索引,從0索引開始。var1必須是此字串的一個連續子集,否則返回-1
indexOf(String var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var2) 返回指定子字串在此字串中第一次出現處的索引,從指定的索引開始。
lastIndexOf(int var1) 返回指定字元(char)在此字串中最後一次出現處的索引。從0索引開始
int var2) 返回在此字串中最後一次出現指定字元處的索引,從指定的索引開始搜尋。var2 小於字串的長度
lastIndexOf(String var1) 返回指定子字串在此字串中最後一次出現處的索引,從0索引開始。var1必須是此字串的一個連續子集,否則返回-1
lastIndexOf(String var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var2) 返回指定子字串在此字串中最後一次出現處的索引,從指定的索引開始。
複製程式碼
7、字串擷取
String int var1) 返回一個新的字串,它是此字串的一個子字串。從var1開始擷取,擷取長度為此字串的長度減去var1
String int var2) 返回一個新字串,它是此字串的一個子字串。從var1開始擷取,擷取長度為var2。
CharSequence subSequence(int var2) 返回一個新的字元序列,它是此序列的一個子序列。
複製程式碼
8、字串替換
replace(char oldChar,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">char newChar) 返回一個新的字串,它是通過用 newChar 替換此字串中出現的所有 oldChar 得到的。
String replaceFirst(String regex, String replacement) 使用給定的 replacement 替換此字串匹配給定的正則表示式的第一個子字串。
String replaceAll(String regex, String replacement) 使用給定的 replacement 替換此字串所有匹配給定的正則表示式的子字串。
String replace(CharSequence var1, CharSequence var2) 使用指定的字面值替換序列替換此字串所有匹配字面值目標序列的子字串。
複製程式碼
char var1,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">char var2) {
if (var1 != var2) {
int var4 = -1;
do {
++var4;
} while(var4 < var3 && var5[var4] != var1);
if (var4 < var3) {
char[] var6 = char[var3];
int var7 = 0; var7 < var4; ++var7) {
var6[var7] = var5[var7];
}
while(var4 < var3) {
char var8 = var5[var4];
var6[var4] = var8 == var1 ? var2 : var8;
++var4;
}
new String(var6,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">true);
}
}
this;
}
複製程式碼
replace 的引數可以是 char 或者 CharSequence,即可以支援字元的替換,也支援字串的替換。當引數為 char 時,是通過自己自定義的方法來更換字元;當引數為 CharSequence 時,實際呼叫的是 replaceAll 方法。replaceAll 和 replaceFirst 的引數是 regex,即基於規則表示式的替換。區別是一個全部替換,一個只替換第一個。
9、valueOf
static String valueOf(Object var0)
valueOf(char[] var0)
char[] var0,153); font-weight: bold; word-wrap: inherit !important; word-break: inherit !important;" class="hljs-keyword">int var2)
boolean var0)
int var0)
long var0)
float var0)
double var0)
複製程式碼
valueOf 都是靜態函式,不需要例項化 String 物件,直接呼叫用於將其他基本資料型別轉換為 String 型別。
10、intern()方法
native String intern();
複製程式碼
intern 方法是 Native 呼叫,它的作用是在全域性字串常量池裡尋找等值的物件的引用,如果沒有找到則在常量池中存放當前字串物件的引用並返回該引用,否則直接返回常量池中已存在的 String 物件引用。
intern 方法與常量池有著很大的聯絡,通過學習該方法的使用,便於我們瞭解記憶體分配的概念,所以該方法會在後續章節裡詳細講解。想要了解的朋友可以先了解一下常量池的知識,前往 Java 中方法區與常量池 即可。