首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java String 兩種實例化方式深度解析:堆與棧的記憶體模型

     編輯:Java 相關知識

前言

String str = "abc" 與 String str = new String("abc") 看似等價,實則在記憶體分配、物件建立、效能特性上有本質差異。本文從 JVM 記憶體模型(棧/堆/方法區)、字串常量池、享元模式、不可變特性等維度深度剖析。

一、JVM 記憶體區域速覽

區域存放內容特性
棧基本型別區域變數、物件參考、方法呼叫框架速度最快(僅次暫存器)、大小/生命週期固定、執行緒私有
堆所有 new 出來的物件實例、陣列動態分配、GC 管理、存取較慢、全執行緒共享
方法區/常量池類別中繼資料、常量、字串常量池邏輯上屬堆一部分、JDK 7+ 字串常量池移至堆

二、基本型別與包裝類型

  • 8 種基本型別:int, short, long, byte, float, double, boolean, char — 直接存字面值於棧,無物件實例。
  • 包裝類別:Integer, String, Double 等 — new 建立於堆,JDK 5+ 支援自動裝箱/拆箱。

三、字面值賦值 String str = "abc" 內部機制

  1. 編譯期將 "abc" 放入 字串常量池(Class 檔常量表 → 類載入時駐留)。
  2. 執行期:str 參考變數於棧建立。
  3. JVM 查詢常量池是否已有 "abc":
    • 有:直接回傳該常量池物件參考給 str(共享)。
    • 無:在常量池建立新字串物件,回傳參考。

驗證程式碼

String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);  // true,同一常量池物件

s1 = "bcd";
System.out.println(s1 == s2);  // false,s1 指向新常量 "bcd"
System.out.println(s2);        // abc,s2 不受影響

String s3 = s1;
String s4 = "bcd";
System.out.println(s1 == s4);  // true,常量池共享

四、new String("abc") 強制建立新物件

  • 無論常量池是否有 "abc",堆上必定建立新 String 實例。
  • 字面值 "abc" 仍會在常量池建立/駐留(若不存在)。
  • 兩者 == 比較永遠為 false(不同記憶體位址)。
String s1 = new String("abc");
String s2 = "abc";
System.out.println(s1 == s2);  // false

String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s3 == s4);  // false,兩個獨立堆物件

五、String 不可變性與效能陷阱

  • 不可變:任何「修改」操作(concat、replace、substring、賦新值)都會建立新物件,原物件等待 GC。
  • String str = "abc"; str = "bcd"; 實際上是建立新參考指向常量池 "bcd",非修改原物件。
  • 頻繁字串拼接請使用 StringBuilder (非同步) 或 StringBuffer (同步)。

六、享元模式與常量池

字串常量池是 Flyweight Pattern 經典應用:共享相同字面值減少記憶體佔用。String.intern() 可手動將堆上字串駐入常量池(JDK 7+ 若常量池已存在則返回池中參考,否則將堆物件參考存入池)。

七、比較最佳實踐

場景正確做法錯誤做法
比較字串內容str1.equals(str2) 或 Objects.equals(str1, str2)str1 == str2
比較是否同一物件str1 == str2str1.equals(str2) (效能較差)
忽略大小寫比較str1.equalsIgnoreCase(str2)手動 toLowerCase 再 equals

八、總結與建議

  1. 優先使用字面值 String s = "abc":享受常量池共享、編譯期常量折疊、效能最佳。
  2. 避免無謂 new String():除非刻意需要獨立物件(極少見),否則增加堆壓力與 GC 負擔。
  3. 理解參考與物件關係:變數是參考,== 比參考位址,equals 比內容。
  4. 可變字串用 StringBuilder:迴圈拼接、大量修改場景必備。
  5. JDK 7+ 常量池移至堆:PermGen 移除,intern() 行為調整,大量駐留需關注堆大小。

「清醒認識『建立參考』與『建立物件』的差異,是排除難以發現 Bug 的關鍵。」

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved