1. 程式人生 > >String的intern()方法詳解

String的intern()方法詳解

官方API:
intern

public String intern()

返回字串物件的規範化表示形式。

一個初始時為空的字串池,它由類 String 私有地維護。

當呼叫 intern 方法時,如果池已經包含一個等於此 String 物件的字串(該物件由 equals(Object) 方法確定),則返回池中的字串。否則,將此 String 物件新增到池中,並且返回此 String 物件的引用。

它遵循對於任何兩個字串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern() 才為 true。

所有字面值字串和字串賦值常量表達式都是內部的。

返回:

一個字串,內容與此字串相同,但它保證來自字串池中。

------------------------------------------------------------------------------------------------

我們知道,一個Java程式執行後,String類會在記憶體的方法區中維護一個字串池。對一個字串呼叫intern()方法後,會先檢查池內是否有該字串,若有則返回;若沒有沒有則先建立再返回,確保返回的字串已經以字面量的形式存在於池中。

上測試樣例程式碼:

public class Test {
    public static void main(String argv[])
    {
        String s1 = "HelloWorld";
        String s2 = new String("HelloWorld");
        String s3 = "Hello";
        String s4 = "World";
        String s5 = "Hello" + "World";
        String s6 = s3 + s4;
        
        System.out.println(s1 == s2);
        System.out.println(s1 == s5);
        System.out.println(s1 == s6);
        System.out.println(s1 == s6.intern());
        System.out.println(s2 == s2.intern());
    }
}


測試結果:

false

true

false

true

false

解釋一下:s1 建立的 HelloWorld 存在於方法區中的常量池其中的字串池,而 s2 建立的 HelloWorld 存在於堆中,故第一條 false 。

s5 的建立是先進行右邊表示式的字串連線,然後才對 s5 進行賦值。賦值的時候會搜尋池中是否有 HelloWorld 字串,若有則把 s5 引用指向該字串,若沒有則在池中新增該字串。顯然 s5 的建立是用了 s1 已經建立好的字面量,故 true 。

第三個比較容易弄錯,s6 = s3 + s4; 其實相當於 s6 = new String(s3 + s4); s6 是存放於堆中的,不是字面量。所以 s1 不等於 s6 。

第四個 s6.intern() 首先獲取了 s6 的 HelloWorld 字串,然後在字串池中查詢該字串,找到了 s1 的 HelloWorld 並返回。這裡如果事前字串池中沒有 HelloWorld 字串,那麼還是會在字串池中建立一個 HelloWorld 字串再返回。總之返回的不是堆中的 s6 那個字串。

第四條也能解釋為什麼第五條是 false 。s2是堆中的 HelloWorld,s2.intern() 是字串池中的 HelloWorld 。

--------------------------------------------------------------

在多一句,如果把

String s6 = s3 + s4;
改成

String s6 = (s3 + s4).intern();

會發生什麼呢?

s6 儲存的 HelloWorld 是存放字串池中還是堆中呢?

答案是前者。

另外:

// jdk1.6
// false、true、false、true、false、true
// jdk1.7
// false、true、true、true、false、true
// jdk1.8
// false、true、true、true、false、true
String str1 = new StringBuffer("ja").append("va").toString();
String intern1 = str1.intern();
System.out.println(intern1 == str1);
System.out.println(intern1.equals(str1));
String str2 = new StringBuffer("我是").append("中國人").toString();
String intern2 = str2.intern();
System.out.println(intern2 == str2);
System.out.println(intern2.equals(str2));
String str3 = new StringBuffer("我是").append("中國人").toString();
String intern3 = str3.intern();
System.out.println(intern3 == str3);
System.out.println(intern3.equals(str3));
/**
 * "java"字串常量比較特殊,它是固定存在字串常量池中,所以都是返回了false
 * Java6中字串常量池是在永久代區,intern方法會將首次遇到的字串複製到那裡,返回的也是永久代中這個字串的引用。不建議使用這個方法。
 * Java7中字串常量池被放到了堆中,intern的實現也不再是複製例項,只是在常量池中記錄首次出現的例項引用。
 * Java8同7一致
 */

程式碼解釋:

(1)java1.6

str1、str2和str3都是指向堆記憶體的地址;

intern1、intern2和intern3都是指向永生代的常量池地址;

所以,“==”的地址比較肯定都是false,但是equals比較的都是內容,所以都是true;

(2)java1.7

str1、str2和str3都是指向堆記憶體的地址

由於“java”字串的特殊性,intern1指向的常量池的地址,所以第一個地址的比較就是false;

intern2和str2都是指向同一塊堆地址,所以第二個地址的比較就是true;

由於str3的字串已經在常量池中存在,所以intern3就和intern2、str2的地址保持一致;
 


原文:

https://blog.csdn.net/wjzhang5514/article/details/70209403 

https://blog.csdn.net/qq_39305860/article/details/79645664