【Java SE】Java中String的內存原理

參考筆記:

Java String 類深度解析:內存模型、常量池與核心機制_java stringx、-CSDN博客

解析java中String的內存原理_string s1 = new string("ab");內存分析-CSDN博客


目錄

1.String初識

2.字符串字面量

3.內存原理圖

4. 示例驗證

4.1 字面量直接賦值

4.2 new方式賦值

4.3 new和直接賦值混合

4.4 字符串拼接

4.4.1 +號兩邊至少有一個變量

4.4.2 +號兩邊都是字面量

4.5 intern()方法

4.5.1 示例代碼1

4.5.2 示例代碼2


1.String初識

(1)JavaString 是引用數據類型

(2)因為字符串使用比較頻繁,所以 Java 專門為字符串準備了一個字符串常量池。 Java 8 之前字符串常量池在方法區中,Java 8 之后在中,本文講的是 Java 8 之前

(3)放在字符串常量池中的好處就是省去了對象的創建過程,從而提高程序的執行效率。常量池是一種緩存技術,緩存技術是提高程序執行效率的重要手段

(4)字符串一旦創建是不可變的(String 源碼有一個屬性:private final byte[] value)

?示例:String s = "hello"

其中 "hello" 存儲在字符串常量池中,字符串常量池中的 "hello" 不可變,不能變成 "hello123"。而 s 仍然可以指向其他的字符串對象,例如 s = "xyz"

2.字符串字面量

字符串字面量:我們自己給出的字符串,也可以稱作字符串常量。如 "123","abc"

判斷方法:簡單來說就是在程序中的任何位置,只要出現帶上英文雙引號的就可以算是字符串字面量

字符串常量池規則

字符串字面量一旦出現,會先去方法區里的字符串常量池找有沒有該字符串常量。

(1)如果有,則直接返回字符串常量池中存放該字符串的空間的地址

(2)如果沒有,則在字符串常量池里面開辟一塊空間用來存放該字符串常量,并返回空間地址

示例代碼

public class demo {public static void main(String[] args) {String s1 = "123";String s2 = new String("456");//"456"String s3 = "12";String s4 = "k";String s5 = s3+s4;//"12K"String s6 = s3+"馬";//"12馬"String s7 = "s"+"abc";//"sabc"}
}

經過上述代碼,字符串常量池中有字符串:"123","456","12","k","馬","s","abc","sabc"

這些都是字符串字面量,但是字符串常量池中不會有 "12k"?、"12馬"(后面會作解釋)

3.內存原理圖

4. 示例驗證

4.1 字面量直接賦值

注:s1 == s2 比較的是 s1s2 的引用地址是否相同, s1.eauals(s2) 比較的是 s1s2 的內容是否相同

示例代碼?

public class demo {public static void main(String[] args){String s1="12";//字符串字面量12會先在方法區中的字符串常量池中找,//發現沒有同內容的字符串常量,那么就開辟一個新的空間,存放12//然后再把這個地址賦值給s1String s2="12";//字符串字面量12會現在方法區中的字符串常量池中找,//發現已經存在了字符串常量12了,此時無需再區開辟空間//只需要把已經存在的字符串常量的地址賦值給s2就行了//s1與s2指向的是同一個字符串常量的地址,所以s1==s2,輸出trueSystem.out.println(s1==s2);}
}

示例代碼內存原理圖

4.2 new方式賦值

注:Java 開發中很少使用 new 的方式給 String 賦值,因為在堆中會產生不必要的內存分配,直接使用字面量賦值更高效

示例代碼

public class demo {public static void main(String[] args) {String s1 = new String("123");String s2 = new String("123");//只要有new就會在堆內存中開辟空間//字符串字面量在字符串常量池中開辟的空間的那個地址值會存放到開辟的堆內存中//s1,s2指向的都是自己堆內存中開辟的空間,并沒有直接指向字符串常量池的"123"的那個地址//因此s1與s2進行 == 比較,輸出為falseSystem.out.println(s1 == s2);//s1與s2內容相同,輸出為trueSystem.out.println(s1.equals(s2));}
}

示例代碼內存原理圖?

4.3 new和直接賦值混合

示例代碼

public class demo {public static void main(String[] args) {String s1=new String("123");String s2="12"+"3";//會在字符串常量池開辟"123","12","3"的空間,//"123"在字符串常量池中開辟的空間地址賦值到了s1中開辟的堆空間中,s1指向的是堆空間地址//"123"在字符串常量池中開辟的空間地址直接賦值給了s2//因此,s1與s2進行 == 比較,輸出為falseSystem.out.println(s1==s2);}
}

示例代碼內存原理圖

4.4 字符串拼接

4.4.1 +號兩邊至少有一個變量

如果 + 號兩邊至少有一個是變量,則用 + 拼接生成的新的字符串不會被放到字符串常量池中,只會存放到堆中

示例代碼

public class demo {public static void main(String[] args) {//字符串常量池中創建"123","456"String s1 = "123";String s2 = "456";//s3="123456"是拼接而來,所以"123456"不在字符串常量池中,存放在堆中String s3 = s1 + s2;//字符串常量池中創建"123456"//s4的引用是字符串常量池中存放"123456"的地址String s4 = "123456";//s3與s4的引用不同,所以輸出為falseSystem.out.println(s3 == s4);//s3與s4的內容相同,輸出為trueSystem.out.println(s3.equals(s4));}
}

4.4.2 +號兩邊都是字面量

如果 + 號兩邊都是字符串字面量(常量),編譯器會進行自動優化。在編譯階段進行拼接。 +兩邊的字符串字面量、拼接后的新字符串都會被放到字符串常量池中,返回的引用也是來自字符串常量池

示例代碼

public class demo {public static void main(String[] args) {//字符串常量池中創建"123"、"456"、"123456"//返回字符串常量池中存放"123456"的地址String s1 = "123"+"456";//字符串常量池中已存在"456"//返回字符串常量池中存放"456"的地址String s2 = "456";//s2與"456"的引用相同,所以輸出為trueSystem.out.println(s2 == "456");//字符串常量池中已存在"123456"//返回字符串常量池中存放"123456"的地址String s3 = "123456";//s1與s3的引用相同,所以輸出為trueSystem.out.println(s1 == s3);}
}

示例代碼內存原理圖?

4.5 intern()方法

?intern() 檢查當前該字符串字面量是否已經存放于字符串常量池中

(1)存在:直接返回字符串常量池中存放該字符串字面量的空間地址
(2)不存在:將新的字符串字面量添加到常量池中,并返回引用

4.5.1 示例代碼1

示例代碼

public class demo {public static void main(String[] args) {String s1 = new String("123");//在字符串常量池開辟"123"的空間//"123"在字符串常量池中開辟的空間地址賦值到了s1中開辟的堆空間中,s1指向的是堆空間地址//字符串常量池中已有"123",調用intern()返回其在字符串常量池中的引用地址String s2 = new String("123").intern();//字符串常量池中已有"123",返回其在字符串常量池中的引用地址String s3 = "123";System.out.println(s1==s2);//falseSystem.out.println(s2==s3);//trueSystem.out.println(s1==s3);//false}
}

示例代碼內存原理圖?

4.5.2 示例代碼2

4.4.1 提到,如果 + 號兩邊至少有一個是變量,則用 + 拼接生成的新字符串不會被放到字符串常量池中,只會存放到堆中

這種場景下就可以用 intern() 方法來將 + 拼接生成的新字符串手動添加到字符串常量池中,并且返回的引用就來自字符串常量池

示例代碼?

public class demo {public static void main(String[] args) {String s1 = "hello";String s2 = "world";//拼接生成的"helloworld",存放在堆中String s3 = s1 + s2;//手動將拼接生成的"helloworld"添加到字符串常量池中,并返回引用String s4 = s3.intern();//字符串常量池中已有"helloworld",返回其在字符串常量池中的引用地址String s5 = "helloworld";//輸出trueSystem.out.println(s4==s5);}
}

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

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

相關文章

Prometheus + Grafana 監控

Prometheus Grafana 監控 官網介紹:Prometheus 是一個開源系統 監控和警報工具包最初由 SoundCloud 構建。自 2012 年成立以來,許多 公司和組織已經采用了 Prometheus,并且該項目具有非常 活躍的開發人員和用戶社區。它現在是一個獨立的開源…

【Python爬蟲(95)】Python爬蟲進階:構建大型垂直領域爬蟲系統

【Python爬蟲】專欄簡介:本專欄是 Python 爬蟲領域的集大成之作,共 100 章節。從 Python 基礎語法、爬蟲入門知識講起,深入探討反爬蟲、多線程、分布式等進階技術。以大量實例為支撐,覆蓋網頁、圖片、音頻等各類數據爬取,還涉及數據處理與分析。無論是新手小白還是進階開發…

Node.js定義以及性能優化

Node.js Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行時,廣泛用于構建高性能的網絡應用。以下是一些常見的 Node.js 面試題及其解答,幫助你準備面試: 1. 什么是 Node.js? Node.js 是一個基于 Chrome V8 引擎的 JavaSc…

開源|Documind協同文檔(接入deepseek-r1、支持實時聊天)

Documind 🚀 項目介紹 Documind 一個支持實時聊天和接入deepseek-r1模型AI助手的協同文檔編輯項目 前端:NextJS React TailwindCSS ShadcnUl Tiptap Zustand后端:NextJS Convex Liveblocks Clerk項目預覽:Documind 預覽…

JVM內存模型詳解:各個區域的作用與原理

引言 Java虛擬機(JVM)是Java程序運行的核心環境,它負責管理程序的內存、執行字節碼以及提供跨平臺的支持。理解JVM的內存模型對于編寫高效、穩定的Java程序至關重要。本文將詳細介紹JVM的內存模型,并深入探討各個內存區域的作用和…

機器學習之集成學習思維導圖

學習筆記—機器學習-集成學習思維導圖 20250227,以后復習看(周老師的集成學習) PS:圖片看不清,可以下載下來看。 往期思維導圖: 機器學習之集成學習Bagging(隨機深林、VR-樹、極端隨機樹&…

【http://noi.openjudge.cn/】4.3算法之圖論——1538:Gopher II

[【http://noi.openjudge.cn/】4.3算法之圖論——1538:Gopher II] 題目 查看提交統計提問 總時間限制: 2000ms 內存限制: 65536kB 描述 The gopher family, having averted the canine threat, must face a new predator. The are n gophers and m gopher holes, each at di…

Apache Spark中的依賴關系與任務調度機制解析

Apache Spark中的依賴關系與任務調度機制解析 在Spark的分布式計算框架中,RDD(彈性分布式數據集)的依賴關系是理解任務調度、性能優化及容錯機制的關鍵。寬依賴(Wide Dependency)與窄依賴(Narrow Dependency)作為兩種核心依賴類型,直接影響Stage劃分、Shuffle操作及容…

【計算機網絡】TCP協議相關總結,TCP可靠性的生動講解

TCP 可靠性 確保快遞不丟、不亂、不過載 機制作用(快遞類比)防止的問題檢驗和檢查包裹是否損壞,損壞就重新發數據出錯序列號給每個包裹編號,按順序整理亂序、重復確認應答每送到一件,就讓收件人簽收丟失滑動窗口控制…

Go基于協程池的延遲任務調度器

原理 通過用一個goroutine以及堆來存儲要待調度的延遲任務,當達到調度時間后,將其添加到協程池中去執行。 主要是使用了chan、Mutex、atomic及ants協程池來實現。 用途 主要是用于高并發及大量定時任務要處理的情況,如果使用Go協程來實現每…

杰發科技AC7801——滴答定時器獲取時間戳

1. 滴答定時器 杰發科技7801內部有一個滴答定時器,該定時器是M0核自帶的,因此可以直接用該定時器來獲取時間戳。 同樣,7803也可以使用該方式獲取時間戳。 2. 滴答定時器原理 SysTick是一個24位的遞減計數器,它從預設的重裝載值…

湖倉一體概述

湖倉一體之前,數據分析經歷了數據庫、數據倉庫和數據湖分析三個時代。 首先是數據庫,它是一個最基礎的概念,主要負責聯機事務處理,也提供基本的數據分析能力。 隨著數據量的增長,出現了數據倉庫,它存儲的是…

第十五屆藍橋杯單片機組4T模擬賽三(第二套)

本套試題在4T平臺中的名字為第15屆藍橋杯單片機組模擬考試三,不知道哪套是4T的模擬賽,所以兩套都敲一遍練練手感。 為了代碼呈現美觀,本文章前面的各個模塊在main函數中的處理函數均未添加退出處理,在最后給出的完整代碼中體現。 …

CT技術變遷史——CT是如何誕生的?

第一代CT(平移-旋轉) X線球管為固定陽極,發射X線為直線筆形束,一個探測器,采用直線和旋轉掃描相結合,即直線掃描后,旋轉1次,再行直線掃描,旋轉180完成一層面掃描,掃描時間3~6分鐘。矩陣象素256256或320320。僅用于顱腦檢查。 第二代CT (平移-旋轉) 與第一代無質…

Virtual Box虛擬機安裝蘋果Monterey和big sur版本實踐

虛擬機安裝蘋果實踐,在Windows10系統,安裝Virtual Box7.1.6,安裝虛擬蘋果Monterey版本Monterey (macOS 12) 。碰到的主要問題是安裝光盤不像Windows那么容易拿到,而且根據網上很多文章制作的光盤,在viritualBox里都無法…

dify基礎之prompts

摘要:在大型語言模型(LLM)應用中,Prompt(提示詞)是連接用戶意圖與模型輸出的核心工具。本文從概念、組成、設計原則到實踐案例,系統講解如何通過Prompt解鎖LLM的潛能,提升生成內容的…

【學寫LibreCAD】0 仿寫LibreCAD簡介

一、LibreCAD 核心模塊: 核心模塊(Core) 功能:處理 CAD 的核心邏輯,如幾何計算、圖形對象管理、坐標系轉換等。關鍵組件: 圖形對象:如直線、圓、圓弧、多段線等。數學工具:向量、矩…

HTML元素,標簽到底指的哪塊部分?單雙標簽何時使用?

1. 標簽&#xff08;Tag&#xff09; vs 元素&#xff08;Element&#xff09; 標簽&#xff08;Tag&#xff09; 標簽是 HTML 中用于定義元素的符號&#xff0c;用尖括號 < > 包裹。例如 <img> 是標簽。元素&#xff08;Element&#xff09; 元素是由 標簽 內容…

Android APK組成編譯打包流程詳解

Android APK&#xff08;Android Package&#xff09;是 Android 應用的安裝包文件&#xff0c;其組成和打包流程涉及多個步驟和文件結構。以下是詳細的說明&#xff1a; 一、APK 的組成 APK 是一個 ZIP 格式的壓縮包&#xff0c;包含應用運行所需的所有文件。解壓后主要包含以…

Token相關設計

文章目錄 1. 雙Token 機制概述1.1 訪問令牌&#xff08;Access Token&#xff09;1.2 刷新令牌&#xff08;Refresh Token&#xff09; 2. 雙Token 認證流程3. Spring Boot 具體實現3.1 生成 Token&#xff08;使用 JWT&#xff09;3.2 解析 Token3.3 登錄接口&#xff08;返回…