String中intern方法和常量池詳解

2021-09-29 17:28:06 字數 4421 閱讀 4145

intern方法的作用: 在jdk1.7版本以後,檢視常量池中有沒有該字串的常量,如果沒有,就將該字串的引用放到常量池,如果有,則返回該字串。

在jdk1.7以前,,檢視常量池中有沒有該字串的常量,如果沒有,就將該字串拷貝乙份放到常量池,如果有,則返回該常量的引用。

先看以下**,判斷下輸出是什麼?

string s = new string("1");

string s2 = "1";

system.out.println(s == s2);

system.out.println(s.intern() == s2);

輸出分別是

false

true

這裡首先要明白一點:在new string(「1」) 的時候,jvm除了會在堆中建立乙個字串物件,還會去檢視常量池裡面有沒有暫存這個物件,如果沒有,那麼會建立乙個物件,並把這個物件的引用放到常量池中。

也就是string s = new string("1");這行**建立了幾個物件? 如果常量池中沒有,那麼是建立了兩個,如果有,那就是乙個。

string s2 = "1";s2 賦值的時候,是從常量池裡面取出來的引用。

也就是 在string s2 = "1";這種字面量賦值的時候,是先去看常量池裡面有沒有這個字串物件,如果有,那麼就從常量池中返回乙個引用並賦值,如果沒有,那麼就建立乙個物件,並把引用放到常量池,並返回常量池中的引用。

具體如下

//建立乙個字串物件 ,

//然後發現常量池中沒有,

//那麼再建立另乙個乙個物件字串物件, 並將這個物件的引用放到常量池

string s = new string("1");

//先看常量池中,發現有」1「這個字串物件,

//那麼將常量池中的引用賦予s2

string s2 = "1";

//s和s2分別是指向兩個不同的物件,因此位址不同 返回false

system.out.println(s == s2);

手動嘗試將s放到常量池,

//結果發現常量池中已經有這個字串常量的引用,

// 那麼返回這個引用 ,s2也是常量池中物件的引用 ,

//是同乙個位址,因此返回true

system.out.println(s.intern() == s2);

那麼換一段**

string s = new string("1");

s.intern();

string s2 = "1";

system.out.println(s == s2);

這段** 返回false 。

如果理解了剛剛說的那個過程,就會明白,因為在new的時候,就已經在常量池中放了乙個新的物件,這個時候呼叫 s.intern(); 試圖將s放到常量池中,會發現常量池中已經有乙個物件了,所有不會將s的引用放到常量池中,會直接返回當前該常量池中暫存的這個字串物件。

那麼看這段**

string s3 = new string("1") + new string("1");

string s4 = "11";

system.out.println(s3 == s4);

返回結果是false, 因為s3 只是建立了「11」物件,並沒有放到常量池。注意,這裡和直接new string(「11」) 不一樣。

那麼如果是

string s3 = new string("1") + new string("1");

s3.intern();

string s4 = "11";

system.out.println(s3 == s4);

返回結果是true

加了s3.intern();之後, 按intern()的效果,如果常量池中沒有,那麼就將字串物件的引用放到常量池,因此 s4="11"的時候 取到的是常量池中引用,也就是s3物件的位址, 因此是true。

但是如果是在jdk1.7以前,因為呼叫intern是拷貝了乙份到常量池,因此這個s4的物件 也是另乙個的物件,返回是false。

原因是:jdk1.7以前,常量池是在永久代,放入常量池是將物件拷貝了乙份到永久代, 而1.7以後常量池是在堆中,放入常量池只是將物件的引用放了乙份到常量池裡面。

那麼問題來了,為什麼用+ 拼接的時候,和new 不一樣呢? new 的時候就已經將字串物件嘗試放入常量池了。

如果是

string s4 = "11";

string s3 ="1"+"1";

system.out.println(s3 == s4);

編譯的時候就會優化成

string s4 = "11";

string s3 ="11";

system.out.println(s3 == s4);

因此就是相同的

如果是

stringbuilder sb = new stringbuilder();

string a3 = sb.tostring();

a3.intern();

string s4 = "11";

system.out.println(a3 == s4);

這個是等同於

string s3 = new string("1") + new string("1");

s3.intern();

string s4 = "11";

system.out.println(s3 == s4);

但是tostring不會主動放到常量池裡面,直接new 則會。

其內部實現

@override

public string tostring()

tostring裡面 其實是對該string物件的value進行了copy操作

public string(char value, int offset, int count) 

if (count <= 0)

if (offset <= value.length)

}// note: offset or count might be near -1>>>1.

if (offset > value.length - count)

this.value = arrays.copyofrange(value, offset, offset+count);

}

正常的new string

例如 new string(「1」), 其在入參的時候 就已經觸發了字面量存入常量池的操作,等同於 string xx=「1」, new string(xx);

string s1 = new string(new char);

string s4 = "11";

system.out.println(s1 == s4);

輸出false

string s1 = new string(new char);

s1.intern();

string s4 = "11";

system.out.println(s1 == s4);

輸出true

tostring裡面的new 就等同於以上,建立string物件時候是不會觸發去常量池裡面的查的。

要想觸發去常量池裡面查和替換,必須要出現字面量,也就是常量,不管是作為賦值還是作為new string(「字面量」)的引數。

調整下順序:

string s4 = "11";

string s3 =new string("1") + new string("1");

s3.intern();

system.out.println(s3 == s4);

這樣子,返回的就是false了,因為string s4 = "11";在執行的時候,是先看下常量池裡面有沒有,發現沒有,就建立了乙個物件,並將該物件的引用放到了常量池裡面,而呼叫 s3.intern()的時候,發現常量池裡面已經有了常量物件,就是前一步呼叫string s4 = "11";的時候放入的。 這個時候s3和s4就已經是兩個物件了。

String中intern的方法

首先檢視官方api那個的解釋 intern public stringintern 返回字串物件的規範化表示形式。乙個初始時為空的字串池,它由類 string 私有地維護。當呼叫 intern 方法時,如果池已經包含乙個等於此 string 物件的字串 該物件由 equals object 方法確定...

String中intern的方法

intern public string intern 返回字串物件的規範化表示形式。乙個初始時為空的字串池,它由類 string 私有地維護。當呼叫 intern 方法時,如果池已經包含乙個等於此 string 物件的字串 該物件由 equals object 方法確定 則返回池中的字串。否則,將...

String拓展 intern 方法

string s1 new string 1 new string 1 s1變數記錄的位址為 new string s1.intern 在字串常量池中生成 11 如何理解 jdk6 建立了乙個新的物件 11 也就有新的位址 jdk7 此時常量池中並沒有建立 11 而是建立了乙個指向堆空間中new s...