詳解String類不可變的底層原理

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值)
      • 內存節省的收益遠大于條件判斷的損耗
  • 關于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);
}
  1. 引用相等檢查
public boolean equals(Object anObject) {
if (this == anObject) {return true;
}
  • 優化作用: 相同對象直接返回,避免后續計算
  1. 類型檢查與模式匹配
return (anObject instanceof String aString)
  • Java16引入的模式匹配語法
    • 同時檢查類型和轉換類型
    • 將成功轉換的對象賦值給新變量aString
  1. 緊湊型字符串兼容性檢查
&& (!COMPACT_STRINGS || this.coder == aString.coder)
  • COMPACT_STRINGS: 靜態final布爾值,表示是否啟用緊湊字符串
    • 如果未啟用緊湊字符串特性,則跳過編碼檢查
    • 如果啟用,則要求兩者的coder(編碼器)相同
  1. 核心內容比較
&& 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());
}
  1. 方法重載
  • 這是一個便捷方法,委托給substring(int beginIndex, int endIndex)實現
  • 默認endIndex為字符串長度length
  1. 參數處理
  • beginIndex必須滿足0 <= beginIndex <= length()

  • endIndex自動設置為字符串長度

  1. 邊界情況
  • 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);
}
  1. 長度獲取
int length = length();

先獲取當前字符串長度,避免多次調用

  1. 邊界檢查
checkBoundsBeginEnd(beginIndex, endIndex, length);

通過checkBoundsBeginEnd()方法驗證:

  • beginIndex >= 0
  • endIndex <= length
  • beginIndex <= endIndex

如果不滿足則拋出IndexOutOfBoundsException

  1. 完整字符串優化
if (beginIndex == 0 && endIndex == length) {return this;
}

請求整個字符串時直接返回原對象

  1. 字串長度計算
int subLen = endIndex - beginIndex;
  1. 判斷不同編碼創建子串
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)	:	StringUTF16.newString(value, beginIndex, subLen);

利用了Java9的緊湊型字符串特性,根據不同編碼類型選擇不同的創建方式

String不可變性的原理

一、不可變性的本質定義
String 對象一旦創建,其內容永遠不可更改,所有看似修改的操作都返回新對象。

二、底層實現機制

  1. 存儲結構鎖定
1. // Java 8及以前private final char[] value; // final保證引用不變// Java 9+優化
@Stable 
private final byte[] value; // 字節存儲+穩定性注解
private final byte coder; // 編碼標記  
  1. 無修改接口
    所有修改操作都創建新對象:
public String concat(String str) {return new String(...);
}
//不提供任何修改內部數組的方法

三、設計保障措施

  1. 構造防護
public String(char[] value) {
this.value = Arrays.copyOf(value, value.length); // 防御性拷貝
}
  1. 訪問控制
public char[] toCharArray() {
return Arrays.copyOf(value, length()); // 返回副本而非原數組
}  
  1. 反射防護
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
field.set(str, newValue); // 拋出IllegalAccessException  

四、關鍵技術支撐

  1. 哈希緩存優化
private int hash; // 首次調用hashCode()時計算并緩存public int hashCode() {int h = hash;if (h == 0 && !hashIsZero) {h = calculateHash();hash = h;}return h;
}
  1. 字符串常量池
String s2 = new String("abc"); // 堆中新對象
s2.intern(); // 返回常量池引用
String s1 = "abc"; // 常量池對象
  1. 線程安全保證
    • 天然線程安全,無需同步
    • 可安全發布(Safe Publication)

五、不可變性的核心價值

優勢領域具體表現
安全性防止敏感數據被篡改
性能哈希緩存、常量池復用
線程安全無需同步自由共享
設計簡單性消除狀態變化復雜性

六、典型應用場景

  1. 作為HashMap的Key
config.put("timeout", 30); // 依賴哈希緩存
Map<String, Object> config = new HashMap<>();
  1. 類加載機制
Class.forName("com.example.Test"); // 類名字符串不可變
  1. 網絡通信
URL url = new URL("https://example.com"); // 基礎地址安全

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87809.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87809.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87809.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

GIT: 一個用于視覺與語言的生成式圖像到文本轉換 Transformer

摘要 在本文中&#xff0c;我們設計并訓練了一個生成式圖像到文本轉換 Transformer——GIT&#xff0c;以統一視覺-語言任務&#xff0c;如圖像/視頻字幕生成和問答。雖然生成式模型在預訓練和微調之間提供了一致的網絡架構&#xff0c;但現有工作通常包含復雜的結構&#xff…

20250706-9-Docker快速入門(下)-Docker在線答疑_筆記

一、Kubernetes核心概念與集群搭建 1. 在線答疑 &#xfeff; 1&#xff09;答疑Docker需要掌握到什么程度 學習目標&#xff1a;達到入門水平即可&#xff0c;重點掌握第一章Docker入門視頻內容學習建議&#xff1a;預習時間約3-4小時&#xff0c;建議吸收視頻內容的80%學…

Node.js-http模塊

HTTP 協議 概念 HTTP&#xff08;hypertext transport protocol&#xff09;協議&#xff1b;中文叫超文本傳輸協議,是一種基于TCP/IP的應用層通信協議這個協議詳細規定了 瀏覽器 和萬維網 服務器 之間互相通信的規則。協議中主要規定了兩個方面的內容 客戶端&#xff1a;用來…

Java JDBC的初步了解

文章目錄 基本流程注冊驅動的兩種方法DriverManagerDriverManager 的核心作用核心原理自動注冊驅動的機制關鍵方法 示例代碼: 連接Mysql數據庫StatementPreparedStatement JDBC全稱Java DataBase Connectivity。 定義: JDBC 是 Java 語言中用于連接和執行 SQL 操作的標準接口。…

[netty5: ChunkedInput ChunkedWriteHandler]-源碼分析

ChunkedInput ChunkedInput<B> 是 Netty 中用于按塊讀取不定長數據流的接口&#xff0c;常配合 ChunkedWriteHandler 實現流式寫入&#xff0c;支持如文件、流、HTTP 和 WebSocket 等多種數據源。 實現類簡要說明ChunkedFile用于將常規文件按塊傳輸&#xff08;使用傳統…

QT 第十二講 --- 控件篇 LineEdit,TextEdit與ComboBox

前言&#xff1a;歡迎進入 QT 控件世界的第十二講&#xff01;在上一講《QT 第十一講 --- 控件篇 LCDnumber&#xff0c;ProgressBar與CalenderWidget》中&#xff0c;我們探索了用于信息展示和狀態反饋的控件&#xff1a;精準的數字顯示器 LCD Number、直觀的進度指示器 Progr…

VSCode遇到的一些小毛病(自動保存、運行后光標不再處于編輯區)

1. 右鍵點擊Run Code沒有觸發自動保存 1. 打開 VS Code 設置&#xff08;Ctrl ,&#xff09; 2. 搜索&#xff1a;code runner save 3. 勾選你需要的 2. 運行后光標仍然處于編輯區&#xff08;容易誤輸入&#xff09; 1. 打開 VS Code 設置&#xff08;Ctrl ,&#xff09; 2.…

Maixcam的使用2

1.單文件和項目&#xff08;多個 py 文件項目/模塊化&#xff09;# 在編寫代碼時&#xff0c;一般兩種模式&#xff0c;執行單個文件&#xff0c;或者執行一個完成項目&#xff08;包含多個 py 文件或者其它資源文件&#xff09;。 單文件模式&#xff1a;MaixVision 創建或者…

征信系統架構思想:打造商業信任基石_東方仙盟—仙盟創夢IDE

一、建設必要性在復雜的商業環境中&#xff0c;企業面臨多元交易對象與業務場景&#xff0c;準確評估合作方信用狀況及潛在價值的難度顯著增加。傳統經驗判斷和簡單背景調查存在局限性&#xff0c;難以滿足現代商業決策需求&#xff0c;因此構建科學的征信體系具有現實必要性。…

網安-XSS-pikachu

介紹 XSS&#xff0c;即跨站腳本攻擊&#xff0c;是指攻擊者利用Web服務器中的代碼漏洞&#xff0c;在頁面中嵌入客戶端腳本&#xff08;通常是一段由JavaScript編寫的惡意代碼&#xff09;&#xff0c;當信任此Web服務器的用戶訪問 Web站點中含有惡意腳本代碼的頁面&#xff…

算法入門——字典樹(C++實現詳解)

字典樹&#xff08;Trie&#xff09;是處理字符串匹配的高效數據結構&#xff0c;廣泛應用于搜索提示、拼寫檢查等場景。本文將帶你從零掌握字典樹的原理與實現&#xff01; 一、什么是字典樹&#xff1f; 字典樹&#xff08;Trie&#xff09;是一種樹形數據結構&#xff0c;…

SpringBoot整合SpringCache緩存

SpringBoot整合SpringCache使用緩存 文章目錄SpringBoot整合SpringCache使用緩存1.介紹2.SpringBoot整合1.導入xml依賴2.配置yml3.使用EnableCaching啟用SpringCache4.Cacheable5.CachePut6.CacheEvict7. Caching8.CacheConfig3.其他屬性配置1.keyGenerator 屬性2. cacheManage…

WPF學習筆記(20)Button與控件模板

Button與控件模板一、 Button默認控件模板詳解二、自定義按鈕模板一、 Button默認控件模板詳解 WPF 中的大多數控件都有默認的控件模板。 這些模板定義了控件的默認外觀和行為&#xff0c;包括控件的布局、背景、前景、邊框、內容等。 官方文檔&#xff1a;https://learn.mic…

藍天居士自傳(1)

藍天居士何許人&#xff1f; 藍天居士是我的筆名&#xff0c;也可以說是號。就好像李白號青蓮居士、歐陽修號六一居士一樣。筆者本名彭昊 —— 一個有不少重名重姓者的名字。 筆者小的時候上語文課&#xff0c;無論是小學、初中抑或是高中&#xff0c;都會有魯迅&#xff08;…

短劇系統開發定制全流程解析:從需求分析到上線的專業指南

一、短劇行業數字化趨勢與系統開發必要性在短視頻內容爆發式增長的時代背景下&#xff0c;短劇作為一種新興的內容形式正在迅速崛起。數據顯示&#xff0c;2023年中國短劇市場規模已突破300億元&#xff0c;用戶規模達到4.5億&#xff0c;年增長率超過200%。這一迅猛發展的市場…

getBoundingClientRect() 詳解:精準獲取元素位置和尺寸

getBoundingClientRect() 是 JavaScript 中一個強大的 DOM API&#xff0c;用于獲取元素在視口中的精確位置和尺寸信息。它返回一個 DOMRect 對象&#xff0c;包含元素的坐標、寬度和高度等關鍵幾何信息。 基本用法 const element document.getElementById(myElement); cons…

EXCEL 基礎技巧

來源&#xff1a;WPS 官網 初步了解WPS表格-WPS學堂https://www.wps.cn/learning/course/detail/id/635.html 1、格式刷 1.1使用格式刷隔行填充顏色。 首先設置部分表格顏色&#xff0c;選中此區域&#xff0c;雙擊點擊格式刷&#xff0c;然后選中其他表格區域。 這樣就可以…

【RK3568 編譯rtl8723DU驅動】

RK3568 編譯rtl8723DU驅動 編譯源碼1.解壓rtl8723du2.修改Makefile 驗證1.加載模塊2.開啟wifi 在驅動開發中&#xff0c;驅動的編譯與集成是實現設備功能的關鍵環節。本文聚焦于基于 RK3568 處理器平臺編譯 RTL8723DU WiFi/BT 二合一模塊驅動的完整流程&#xff0c;涵蓋源碼編譯…

基于Simulink的二關節機器人獨立PD控制仿真

文章目錄 理論模型仿真窗口控制函數目標函數仿真 本文是劉金琨. 機器人控制系統的設計與MATLAB仿真的學習筆記。 理論模型 對于二關節機器人系統&#xff0c;其動力學模型為 D ( q ) q C ( q , q ˙ ) q ˙ r D(q)\ddot qC(q,\dot q)\dot q r D(q)q?C(q,q˙?)q˙?r 式…

【技術架構解析】國產化雙復旦微FPGA+飛騰D2000核心板架構

本文就一款基于飛騰D2000核心板與兩片高性能FPGA的國產化開發主板進行技術解析&#xff0c;包括系統架構、主要硬件模塊、關鍵接口及軟件環境&#xff0c;重點闡述各子系統間的數據路徑與協同工作方式&#xff0c;旨在為行業內同類產品設計與應用提供參考。 隨著國產化要求的加…