深入理解字串的底層儲存方式
引言
以下討論的,包括圖示,都是基於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時,若常量池沒有已存在字串時,是建立一個原字串的副本,而不是索引。