String類
String的基本特性
- 不可變性: String 對象一旦創建就不能被修改,所有看似修改的操作實際上都是創建新的 String 對象
- final類: String 類被聲明為 final,不能被繼承
- 基于字符數組: 內部使用
final char value[]
存儲字符數據(Java9以后改為byte[]
+ 編碼標記)
重要源碼分析
關鍵字段
直接上源碼:
/*** The value is used for character storage.** @implNote This field is trusted by the VM, and is a subject to* constant folding if String instance is constant. Overwriting this* field after construction will cause problems.** Additionally, it is marked with {@link Stable} to trust the contents* of the array. No other facility in JDK provides this functionality (yet).* {@link Stable} is safe here, because value is never null.*//**
該字段用于字符存儲。實現說明:該字段被虛擬機(VM)信任,如果String實例是常量,它會成為常量折疊的優化對象。
在構造后覆蓋此字段會導致問題。此外,該字段標記了@Stable注解以信任數組內容。目前JDK中還沒有其他設施提供此功能。
在此處使用@Stable是安全的,因為value永遠不會為null。
*/@Stable
private final byte[] value;/*** The identifier of the encoding used to encode the bytes in* {@code value}. The supported values in this implementation are** LATIN1* UTF16** @implNote This field is trusted by the VM, and is a subject to* constant folding if String instance is constant. Overwriting this* field after construction will cause problems.*//**
用于編碼value字節的編碼標識符。本實現中支持的值為:
LATIN1
UTF16實現說明:該字段被虛擬機(VM)信任,如果String實例是常量,它會成為常量折疊的優化對象。
在構造后覆蓋此字段會導致問題。
*/private final byte coder;
-
存儲機制: 從Java9開始,String內部使用
byte[]
而不是char[]
存儲字符數據,這是為了支持緊湊型字符串- Java9之前使用
char[]
(UTF-16編碼,每個字符固定2字節) - 實際大部分業務字符串僅含Latin-1字符
byte[]
可以根據內容動態選擇編碼- Latin-1:單字節存儲ASCII字符(0~255)
- UTF-16: 雙字節存儲擴展字符(如中文)
- 性能對比:
//Java 8 (char[]) "Hello" 存儲:10字節 (5 * 2字節)//Java 9+ (byte[] + Latin-1) "Hello" 存儲:5字節 + 1字節(coder標記) //coder標記下文會解釋
- 性能權衡:
- 訪問字符時需要條件判斷(檢查coder值)
- 內存節省的收益遠大于條件判斷的損耗
- Java9之前使用
-
關于
value
字段的注解:@Stable:- JDK內部注解
- 表示字段引用及其內容在初始化后永遠不變
- 比常規final更強的不變性保證:
- 普通fianl只保證引用不變
- @Stable還保證數組元素不變
-
final修飾符解析
- 修飾目標: 此處修飾的是
byte[] value
的引用(非數組內容) - 保證引用不可變(不可指向其他數組)
- 舉例:
final byte[] value = new byte[10]; value = new byte[20]; //編譯錯誤(引用不可變) value[0] = 1; //正確(數組內容可變性不由final決定)
- 與String不可變性的關系
- final是基礎保障,但不是充分條件
- 完整的不可變性需要:
- 私有字段(private)
- 不暴露內部數組(如toCharArray()返回副本)
- 所有方法不修改數組內容
- 修飾目標: 此處修飾的是
常用方法實現
equals,substring
equals()方法
源碼解析:
/*** Compares this string to the specified object. The result is {@code* true} if and only if the argument is not {@code null} and is a {@code* String} object that represents the same sequence of characters as this* object.** <p>For finer-grained String comparison, refer to* {@link java.text.Collator}.** @param anObject* The object to compare this {@code String} against** @return {@code true} if the given object represents a {@code String}* equivalent to this string, {@code false} otherwise** @see #compareTo(String)* @see #equalsIgnoreCase(String)*//*** 將此字符串與指定對象進行比較。當且僅當參數不為 null 且是表示相同字符序列的 String 對象時,結果為 true。* * 對于更精細的字符串比較,請參考 java.text.Collator。* * @param anObject 要與此字符串比較的對象* * @return 如果給定對象表示與此字符串等效的 String,則返回 true,否則返回 false* * @see #compareTo(String)* @see #equalsIgnoreCase(String)*/public boolean equals(Object anObject) {if (this == anObject) {return true;}return (anObject instanceof String aString)&& (!COMPACT_STRINGS || this.coder == aString.coder)&& StringLatin1.equals(value, aString.value);
}
- 引用相等檢查
public boolean equals(Object anObject) {
if (this == anObject) {return true;
}
- 優化作用: 相同對象直接返回,避免后續計算
- 類型檢查與模式匹配
return (anObject instanceof String aString)
- Java16引入的模式匹配語法
- 同時檢查類型和轉換類型
- 將成功轉換的對象賦值給新變量
aString
- 緊湊型字符串兼容性檢查
&& (!COMPACT_STRINGS || this.coder == aString.coder)
COMPACT_STRINGS
: 靜態final布爾值,表示是否啟用緊湊字符串- 如果未啟用緊湊字符串特性,則跳過編碼檢查
- 如果啟用,則要求兩者的
coder(編碼器)
相同
- 核心內容比較
&& StringLatin1.equals(value, aString.value);
實際的比較使用StringLatin1.equals()
,先比較長度,再逐字節比較內容
substring()方法
源碼解析:
/*** Returns a string that is a substring of this string. The* substring begins with the character at the specified index and* extends to the end of this string. <p>* Examples:* <blockquote><pre>* "unhappy".substring(2) returns "happy"* "Harbison".substring(3) returns "bison"* "emptiness".substring(9) returns "" (an empty string)* </pre></blockquote>** @param beginIndex the beginning index, inclusive.* @return the specified substring.* @throws IndexOutOfBoundsException if* {@code beginIndex} is negative or larger than the
* length of this {@code String} object.*//*** 返回此字符串的子字符串。子字符串從指定索引處的字符開始,* 延伸到該字符串的末尾。* * 示例:* <blockquote><pre>* "unhappy".substring(2) 返回 "happy"* "Harbison".substring(3) 返回 "bison"* "emptiness".substring(9) 返回 "" (空字符串)* </pre></blockquote>** @param beginIndex 開始索引(包含該字符)* @return 指定的子字符串* @throws IndexOutOfBoundsException 如果 beginIndex 為負數或大于此字符串對象的長度*/public String substring(int beginIndex) {return substring(beginIndex, length());
}
- 方法重載
- 這是一個便捷方法,委托給
substring(int beginIndex, int endIndex)
實現 - 默認
endIndex
為字符串長度length
- 參數處理
-
beginIndex
必須滿足0 <= beginIndex <= length()
-
endIndex
自動設置為字符串長度
- 邊界情況
- 當
beginIndex == length()
時,返回空字符串 - 當
beginIndex == 0
時,返回原字符串的副本
接著來了解substring(int beginIndex, int endIndex)
的實現原理
/*** Returns a string that is a substring of this string. The* substring begins at the specified {@code beginIndex} and* extends to the character at index {@code endIndex - 1}.* Thus the length of the substring is {@code endIndex-beginIndex}.* Examples:* <blockquote><pre>* "hamburger".substring(4, 8) returns "urge"* "smiles".substring(1, 5) returns "mile"* </pre></blockquote>** @param beginIndex the beginning index, inclusive.* @param endIndex the ending index, exclusive.* @return the specified substring.* @throws IndexOutOfBoundsException if the* {@code beginIndex} is negative, or* {@code endIndex} is larger than the length of* this {@code String} object, or* {@code beginIndex} is larger than* {@code endIndex}.*//*** 返回此字符串的子字符串。子字符串從指定的 beginIndex 開始,* 延伸到索引 endIndex - 1 處的字符。因此子字符串的長度為 endIndex - beginIndex。* * 示例:* <blockquote><pre>* "hamburger".substring(4, 8) 返回 "urge"* "smiles".substring(1, 5) 返回 "mile"* </pre></blockquote>** @param beginIndex 開始索引(包含該字符)* @param endIndex 結束索引(不包含該字符)* @return 指定的子字符串* @throws IndexOutOfBoundsException 如果 beginIndex 為負數,或* endIndex 大于此字符串對象的長度,或* beginIndex 大于 endIndex*/public String substring(int beginIndex, int endIndex) {int length = length();checkBoundsBeginEnd(beginIndex, endIndex, length);if (beginIndex == 0 && endIndex == length) {return this;}int subLen = endIndex - beginIndex;return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen): StringUTF16.newString(value, beginIndex, subLen);
}
- 長度獲取
int length = length();
先獲取當前字符串長度,避免多次調用
- 邊界檢查
checkBoundsBeginEnd(beginIndex, endIndex, length);
通過checkBoundsBeginEnd()方法
驗證:
beginIndex >= 0
endIndex <= length
beginIndex <= endIndex
如果不滿足則拋出IndexOutOfBoundsException
- 完整字符串優化
if (beginIndex == 0 && endIndex == length) {return this;
}
請求整個字符串時直接返回原對象
- 字串長度計算
int subLen = endIndex - beginIndex;
- 判斷不同編碼創建子串
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) : StringUTF16.newString(value, beginIndex, subLen);
利用了Java9的緊湊型字符串特性,根據不同編碼類型選擇不同的創建方式
String不可變性的原理
一、不可變性的本質定義
String 對象一旦創建,其內容永遠不可更改,所有看似修改的操作都返回新對象。
二、底層實現機制
- 存儲結構鎖定
1. // Java 8及以前private final char[] value; // final保證引用不變// Java 9+優化
@Stable
private final byte[] value; // 字節存儲+穩定性注解
private final byte coder; // 編碼標記
- 無修改接口
所有修改操作都創建新對象:
public String concat(String str) {return new String(...);
}
//不提供任何修改內部數組的方法
三、設計保障措施
- 構造防護
public String(char[] value) {
this.value = Arrays.copyOf(value, value.length); // 防御性拷貝
}
- 訪問控制
public char[] toCharArray() {
return Arrays.copyOf(value, length()); // 返回副本而非原數組
}
- 反射防護
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
field.set(str, newValue); // 拋出IllegalAccessException
四、關鍵技術支撐
- 哈希緩存優化
private int hash; // 首次調用hashCode()時計算并緩存public int hashCode() {int h = hash;if (h == 0 && !hashIsZero) {h = calculateHash();hash = h;}return h;
}
- 字符串常量池
String s2 = new String("abc"); // 堆中新對象
s2.intern(); // 返回常量池引用
String s1 = "abc"; // 常量池對象
- 線程安全保證
- 天然線程安全,無需同步
- 可安全發布(Safe Publication)
五、不可變性的核心價值
優勢領域 | 具體表現 |
---|---|
安全性 | 防止敏感數據被篡改 |
性能 | 哈希緩存、常量池復用 |
線程安全 | 無需同步自由共享 |
設計簡單性 | 消除狀態變化復雜性 |
六、典型應用場景
- 作為HashMap的Key
config.put("timeout", 30); // 依賴哈希緩存
Map<String, Object> config = new HashMap<>();
- 類加載機制
Class.forName("com.example.Test"); // 類名字符串不可變
- 網絡通信
URL url = new URL("https://example.com"); // 基礎地址安全