1. 程式人生 > >深入理解字串的底層儲存方式

深入理解字串的底層儲存方式

引言

以下討論的,包括圖示,都是基於JDK1.8以上。因為JDK1.7的常量池在方法區,而不是在Java堆中

先了解字串常量在記憶體的表示方式,接著瞭解字串物件在記憶體的表示方式。在瞭解兩種字串表現方式後,String.intern()就將會很容易理解。 關於Java堆疊內容可以閱讀我的《深入理解Java虛擬機器(二) — JVM記憶體管理》進行了解。

Case1:字串常量

在這裡插入圖片描述

這是一張字串常量在記憶體中表示的圖片。由圖可知,字串常量都會被放進常量池中。‘

字串常量建立

在常量池中查詢是否有已經有存在的相同字串

  • 若有,則將變數引用指向該字串(eg:圖中的str2)

  • 若沒有,則在常量池中建立建立字串,並將引用指向它(eg:圖中的str3)

程式碼模擬

void Example(){
    String str1 = "abc";
    String str2 = "abc";
    String str3 = "sss";
    System.out.println(str1 == str2);
    System.out.println(str1 == str3);
}

結果: true false

因為str1與str2的引用是指向同一個地方,而str1與str3卻指向不同的地址

Java中的字串比較

通過String.equals(str),比較的是字串中的值是否相等

通過 == ,比較的是字元變數所引用的地址是否相同

tips:這也是比較廣義的說法,並非是精確的描述

Case2:字串物件

在這裡插入圖片描述

這是一張字串物件在記憶體中表示的圖片。由圖可知,字串物件都是作為例項放在Java堆中,而不是常量池。

字串物件建立

在Java堆中例項化一個String物件,返回變數一個相應的地址。

不同的物件,即使字串值相同,引用也是不相同的。

程式碼模擬

void Example(){
    String str1 = new String(ab);
    String str2 = new String(ab);
    
    System.out.println(str1.equals(str2)); //值相同
    System.
out.println(str1 == str2); //引用地址不同 }

結果: true false

String.intern()

方法目的: 該方法實現從常量池中取回字串物件的值。

看完這個,可能有疑問:為什麼字串物件能從常量池中返回值?!

在這裡插入圖片描述

其實,在呼叫intern的時候,它進行了以下的步驟:

檢查常量池中,有沒有已存在的相同字串

  • 若有,返回常量池的該地址
  • 若沒有,在常量池建立一個索引指向原例項地址,並返回該地址指向的字串(eg: str1)

程式碼模擬

void Example(){
    String str1 = new String(ab);
    String str2 = new String(ab);
    
    System.out.println(str1.equals(str2)); //值相同
    System.out.println(str1 == str2); //引用地址不同
    System.out.println(str1.intern() == str2.intern()); //地址相同,都是0xf0f010
}

結果 true true false

解析

  • 左邊的str1.intern()在常量池建立了一個str1的索引,返回str1的字串

  • 右邊的str2.intern()在常量池中找到str1的索引,發現值相同,所以也返回了str1的字串

Tips: 在JDK1.7以前,呼叫intern時,若常量池沒有已存在字串時,是建立一個原字串的副本,而不是索引。