深入理解intern()


首先來看JDK1.6

 

顯然JDK1.6及其以前版本常量池是放在 Perm 區(屬於方法區)中的,熟悉JVM的話應該知道這是和堆區完全分開的。

 

1.6中intern方法的作用:

比如String s = new String("SEU_Calvin"),再調用s.intern(),此時返回值還是字符串"SEU_Calvin",表面上看起來好像這個方法沒什么用處。但實際上,在JDK1.6中它做了個小動作:檢查字符串池里是否存在"SEU_Calvin"這么一個字符串,如果存在,就返回池里的字符串;如果不存在,該方法會把"SEU_Calvin"添加到字符串池中,然后再返回它的引用。

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
 
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

那么來看這段代碼

使用引號聲明的字符串都是會直接在字符串常量池中生成的,而 new 出來的 String 對象是放在堆空間中的。所以兩者的內存地址肯定是不相同的,即使調用了intern()方法也是不影響的。

所以兩個都是false

 

然后JDK1.7及其以后版本

 

還是剛才的代碼

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
 
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);  

 結果是在JDK1.7以及以后版本中答案卻是false true

第一到四行代碼運行過程:

第一行:new String(“1”) 中的“1”,那么首先檢查常量池是否有“1”,如果沒有則在堆上創建“1”,並將堆上“1”的引用添加到常量池,如果有,則堆上新建“1”存儲常量池“1”的引用

然后new String 則在堆上創建另一個不同於第一個在堆上“1”的“1”,並將棧上的s指向該1

第二行:檢查常量池中是否有“1”,如果沒有就將s指向堆中的“1”的引用存入常量池,如果有返回常量池“1”的引用

String s2=“1”引用賦值,則直接檢查常量池中是否有1,如果有則s2指向常量池中的1(即堆中的第一個“1”)                  若沒有則在堆上創建“1”,並將常量池存入該“1”的引用

顯然s2指向的是之前new String(“1”)中的1(堆中的第一個“1”)

第四步   s指向的是堆中第二個“1” s2指向堆中第一個“1”   固為false

 

第四行到第8行代碼分析:

第一行:堆上先建一個“1” ,其引用至常量池   s3指向堆中“11” 此時常量池只有“1”,無“11”

s3.intern()將s3的“11”引用加入到常量池中

s4=“11”檢查常量池是否有11  如果有(常量池中的“11”)為s3的引用

則將s4指向常量池中的“11”(就是s3) 所以s3=s4

 

 

但需要注意以下

String s1 = new String("1") + new String("1")    (變量+...)和String s2 = "1"+"1"(常量加常量)不同

s1在賦值階段 調用StringBuilder在上創建對象。  而s2是檢查常量池,從而決定在堆上創建還是直接引用常量池中的變量

final修飾的串,final修飾的串在編譯時就會被替換成常量(即 final s1就看成“11”常量)

 

 

圖文部分來自:

https://blog.csdn.net/seu_calvin/article/details/52291082

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM