String intern 方法精準詳解,有圖有真相

前言

相信看到這篇文章的人,對jvm的內存結構都有一定的瞭解。

String 有屬於自己的常量池,1.8以前字符串常量池都是在方法區,1.8以後雖然方法區從堆內存中挪出去了(改名爲元空間),但是常量池依然保留在堆內存中。

一張 jvm 的內存結構圖幫助理解(1.8)
在這裏插入圖片描述


字符串創建過程分析

我們日常實例化字符串的時候,一般有以下兩種方式:

String a = "eric";

String a = new String("lk");

採用 String a = "lk"; 這種方式創建字符串的時候:

  1. 虛擬機會先去字符串常量池搜索一下,有沒有對應的字符串對象
  2. 如果有,直接返回常量池的對象引用
  3. 如果沒有,則會在字符串常量池中創建一個對應的字符串,並返回其引用

結果總結:這種方式創建的字符串一定返回的是常量池存在的字符串引用。


採用 String a = new String("lk"); 這種方式創建字符串的時候:

  1. 虛擬機也是先去字符串常量池中搜索一下,有沒有對應的字符串
  2. 如果有,則直接在堆內存中創建一個的字符串對象,並返回堆中對象的引用;
  3. 如果沒有,則會在字符串常量池中創建一個字符串對象,然後再在堆內存中創建一個字符串對象,並返回堆中對象的引用

結果總結:與上對比, 這種方式返回的一定是堆中對象的引用;但是常量池中沒有,它順帶着也會在常量池中補上對應的字符串。


intern 方法介紹

有了以上知識背景,理解 intern() 就不難了,它是 String 的一個 native 方法,實現暫且不談。

官方註釋翻譯功能(1.8):

當調用 intern方法時,如果池已經包含一個等於此String對象的字符串(用equals(oject)方法確定),則返回池中的字符串。否則,將此String對象(引用)添加到池中,並返回此String對象的引用。


結合實例

String s = new String("123"); // 1
String a = s.intern(); // 2

以上邊的代碼爲例,

  1. 創建一個內容爲 "123" 的字符串 s

  2. 判斷字符串常量池中是否有 "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"; 由於常量池已經拷貝過了,直接返回對應引用 ( 對應藍色的操作)



巨人的肩膀:

  1. 看完這篇JVM,阿里面試官都不怕!看完就能拿offer
  2. 字符串常量池深入解析
  3. 深入解析String#intern
  4. 更加刁鑽面試題:5 個刁鑽的 String 面試題! 看完本博客,應該就沒啥問題了

簡單總結,如有紕漏,歡迎指出!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章