String的Intern方法

jdk6 和 jdk7 下 intern 的區別

  相信不少 JAVA 程序員都作作相似 String s = new String("abc")這個語句建立了幾個對象的題目。 這種題目主要就是爲了考察程序員對字符串對象的常量池掌握與否。上述的語句中是建立了2個對象,第一個對象是」abc」字符串存儲在常量池中,第二個對象在JAVA Heap中的 String 對象。java

1
2
3
4
5
6
7
8
9
10
11
public  static  void  main(String[] args) {
     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);
}

  打印結果是程序員

  • jdk6 下false false
  • jdk7 下false true

  具體爲何稍後再解釋,而後將s3.intern();語句下調一行,放到String s4 = "11";後面。將s.intern(); 放到String s2 = "1";後面。是什麼結果呢spa

1
2
3
4
5
6
7
8
9
10
11
public  static  void  main(String[] args) {
     String s = new  String( "1" );
     String s2 = "1" ;
     s.intern();
     System.out.println(s == s2);
 
     String s3 = new  String( "1" ) + new  String( "1" );
     String s4 = "11" ;
     s3.intern();
     System.out.println(s3 == s4);
}

  打印結果爲:code

  • jdk6 下false false
  • jdk7 下false false

1.jdk6中的解釋

  注:圖中綠色線條表明 string 對象的內容指向。 黑色線條表明地址指向。對象

  如上圖所示。首先說一下 jdk6中的狀況,在 jdk6中上述的全部打印都是 false 的,由於 jdk6中的常量池是放在 Perm 區中的,Perm區和正常的 JAVA Heap 區域是徹底分開的。上面說過若是是使用引號聲明的字符串都是會直接在字符串常量池中生成,而 new 出來的 String 對象是放在 JAVA Heap 區域。因此拿一個 JAVA Heap 區域的對象地址和字符串常量池的對象地址進行比較確定是不相同的,即便調用String.intern方法也是沒有任何關係的。blog

2.jdk7中的解釋

  在 Jdk6 以及之前的版本中,字符串的常量池是放在堆的Perm區的,Perm區是一個類靜態的區域,主要存儲一些加載類的信息,常量池,方法片斷等內容,默認大小隻有4m,一旦常量池中大量使用 intern 是會直接產生java.lang.OutOfMemoryError:PermGen space錯誤的。在 jdk7 的版本中,字符串常量池已經從Perm區移到正常的Java Heap區域了。爲何要移動,Perm 區域過小是一個主要緣由,固然據消息稱jdk8已經直接取消了Perm區域,而新創建了一個元區域。應該是jdk開發者認爲Perm區域已經不適合如今 JAVA 的發展了。正式由於字符串常量池移動到JAVA Heap區域後,再來解釋爲何會有上述的打印結果。ci

  • 在第一段代碼中,先看 s3和s4字符串。String s3 = new String("1") + new String("1");,這句代碼中如今生成了2最終個對象,是字符串常量池中的「1」 和 JAVA Heap中的 s3引用指向的對象。中間還有2個匿名的new String("1")咱們不去討論它們。此時s3引用對象內容是」11″,但此時常量池中是沒有 「11」對象的。
  • 接下來s3.intern();這一句代碼,是將 s3中的"11"字符串放入String 常量池中,由於此時常量池中不存在"11"字符串,所以常規作法是跟 jdk6 圖中表示的那樣,在常量池中生成一個"11"的對象,關鍵點是 jdk7 中常量池不在Perm區域了,這塊作了調整。常量池中不須要再存儲一份對象了,能夠直接存儲堆中的引用。這份引用指向s3引用的對象。 也就是說引用地址是相同的。
  • 最後String s4 = "11"; 這句代碼中」11″是顯示聲明的,所以會直接去常量池中建立,建立的時候發現已經有這個對象了,此時也就是指向s3引用對象的一個引用。因此s4引用就指向和s3同樣了。所以最後的比較 s3 == s4 是 true。
  • 再看s和 s2 對象。String s = new String("1"); 第一句代碼,生成了2個對象。常量池中的「1」 和 JAVA Heap 中的字符串對象。s.intern(); 這一句是 s 對象去常量池中尋找後發現 「1」 已經在常量池裏了。
  • 接下來String s2 = "1"; 這句代碼是生成一個 s2的引用指向常量池中的「1」對象。 結果就是 s 和 s2 的引用地址明顯不一樣。圖中畫的很清晰。

  • 來看第二段代碼,從上邊第二幅圖中觀察。第一段代碼和第二段代碼的改變就是 s3.intern(); 的順序是放在String s4 = "11";後了。這樣,首先執行String s4 = "11";聲明 s4 的時候常量池中是不存在「11」對象的,執行完畢後,「11「對象是 s4 聲明產生的新對象。而後再執行s3.intern();時,常量池中「11」對象已經存在了,所以 s3 和 s4 的引用是不一樣的。
  • 第二段代碼中的 s 和 s2 代碼中,s.intern();,這一句日後放也不會有什麼影響了,由於對象池中在執行第一句代碼String s = new String("1");的時候已經生成「1」對象了。下邊的s2聲明都是直接從常量池中取地址引用的。 s 和 s2 的引用地址是不會相等的。

小結

  從上述的例子代碼能夠看出 jdk7 版本對 intern 操做和常量池都作了必定的修改。主要包括2點:開發

  • 將String常量池從Perm區移動到了Java Heap區
  • String#intern 方法時,若是存在堆中的對象,會直接保存對象的引用,而不會從新建立對象。
相關文章
相關標籤/搜索