前言
相信看到這篇文章的人,對jvm的內存結構都有一定的瞭解。
String 有屬於自己的常量池,1.8以前字符串常量池都是在方法區,1.8以後雖然方法區從堆內存中挪出去了(改名爲元空間),但是常量池依然保留在堆內存中。
一張 jvm 的內存結構圖幫助理解(1.8)
字符串創建過程分析
我們日常實例化字符串的時候,一般有以下兩種方式:
String a = "eric";
String a = new String("lk");
採用 String a = "lk";
這種方式創建字符串的時候:
- 虛擬機會先去字符串常量池搜索一下,有沒有對應的字符串對象
- 如果有,直接返回常量池的對象引用
- 如果沒有,則會在字符串常量池中創建一個對應的字符串,並返回其引用
結果總結:這種方式創建的字符串一定返回的是常量池存在的字符串引用。
採用 String a = new String("lk");
這種方式創建字符串的時候:
- 虛擬機也是先去字符串常量池中搜索一下,有沒有對應的字符串
- 如果有,則直接在堆內存中創建一個的字符串對象,並返回堆中對象的引用;
- 如果沒有,則會在字符串常量池中創建一個字符串對象,然後再在堆內存中創建一個字符串對象,並返回堆中對象的引用
結果總結:與上對比, 這種方式返回的一定是堆中對象的引用;但是常量池中沒有,它順帶着也會在常量池中補上對應的字符串。
intern 方法介紹
有了以上知識背景,理解 intern()
就不難了,它是 String
的一個 native
方法,實現暫且不談。
官方註釋翻譯功能(1.8):
當調用 intern方法時,如果池已經包含一個等於此String對象的字符串(用equals(oject)方法確定),則返回池中的字符串。否則,將此String對象(引用)添加到池中,並返回此String對象的引用。
結合實例
String s = new String("123"); // 1
String a = s.intern(); // 2
以上邊的代碼爲例,
-
創建一個內容爲
"123"
的字符串 s -
判斷字符串常量池中是否有
"123"
字符串Y. 如果有,則返回其地址 ---- 通過上述,我們可以瞭解到,其實常量池中已經有了
N. 如果沒有,則將堆內存中 “123” 對象的引用存儲在常量池中,並返回其堆內存的引用
以上是1.7及其以後,intern
的實現方式,它在1.7 以前的操作稍微不太一樣:
在字符串以 String a = new String("123");
這種方式創建的時候,如果創建的時候,會直接把堆內存中對應的字符串對象拷貝到字符串常量池中(1.7之後拷貝的是字符串對象的引用)
問題
@Test
public void test(){
// 1
String a = new String("abc"); // 1.1
a.intern(); // 1.2
String b = "abc"; // 1.3
System.out.println(a == b);
// 2
String c = new String("12")+new String("3"); // 2.1
c.intern(); // 2.2
String d = "123"; // 2.3
System.out.println(c == d);
}
結果
爲什麼第一是個false,第二是true呢?
圖解
對應第一個比較
-
1.1 通過
String a = new String("abc");
實例化的符串對象,順帶着把常量池也實例化出來了(對應綠的操作) -
1.2
a.intern();
由於常量池中已經存在"abc"
了,此步相當於沒有任何操作 ~ -
1.3
String b = "abc";
直接返回"abc"
對應引用 ( 對應藍色的操作)
對應第二個比較
-
2.1 通過
String c = new String("12")+new String("3");
實例化的符串對象,這裏需要注意:通過+
進行拼接的時候,實際上是通過StringBuilder
來拼接,然後轉換爲String
對象,所以最後轉化生成的 “123” 不會出現在字符串常量池中 (對應綠的操作) -
2.2
a.intern();
有最後生成 “123” ,調用 intern() 方法之後,就會把a在堆中的引用拷貝到常量池中。(對應紅色的操作) -
2.3
String b = "abc";
由於常量池已經拷貝過了,直接返回對應引用 ( 對應藍色的操作)
巨人的肩膀:
- 看完這篇JVM,阿里面試官都不怕!看完就能拿offer
- 字符串常量池深入解析
- 深入解析String#intern
- 更加刁鑽面試題:5 個刁鑽的 String 面試題! 看完本博客,應該就沒啥問題了
簡單總結,如有紕漏,歡迎指出!