String的intern()方法詳解

1、先看一個例子

String str1 = new String("A")+ new String("B");
System.out.println(str1 == str1.intern());
System.out.println(str1 == "AB");

JDK1.8的輸出結果:

true
true

上邊例子加一行代碼其餘不變:

String str2 = "AB";
String str1 = new String("A")+ new String("B");
System.out.println(str1 == str1.intern());
System.out.println(str1 == "AB");

JDK1.8的輸出結果:

false
false

只是定義了一個str2,應該和str1沒有什麼關係吧,但是爲什麼會影響到最終的結果呢?
這時候就需要我們深入瞭解intern()方法了!!!

2、深入瞭解intern()方法

JDK1.6以及以前版本中,常量池是放在 Perm 區(屬於方法區)中的,熟悉JVM的話應該知道這是和堆區完全分開的。JDK1.7後,常量池被放入到堆空間中,這導致intern()函數的功能不同,具體怎麼個不同法,且看看下面例子,分析圖是直接粘貼過來的:

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.6以及以下:false false
JDK1.7以及以上:false true

再分別調整上面代碼2.3行、7.8行的順序:

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);

輸出結果:

JDK1.6以及以下:false false
JDK1.7以及以上:false false
2.1、JDK1.6分析

JDK1.6及以下版本intern()的作用:檢查常量池裏是否存在這個字符串,如果存在,就返回池裏的字符串;如果不存在,該方法會把這個字符串添加到常量池中,然後再返回它的引用。
在這裏插入圖片描述
先看第一段代碼:

  • String s = new String("1")會在常量池中創建常量“1”,堆中創建一個String對象指向常量池的“1”,棧中的s指向堆中的String對象
  • s.intern()會返回常量池中的地址,在這裏沒有什麼意義
  • String s2 = "1"s2指向常量池中的“1”
  • System.out.println(s == s2)常量池中地址和堆中的地址是不一樣的所以爲false
  • String s3 = new String("1") + new String("1")其中new String("1")創建和第一行s創建是一樣的。字符串的拼接用到了StringBuilder,這裏不多做介紹。最終會新創建一個存放“11”的String對象(注意這個時候的“11”是在堆內存中),s3指向堆中的String對象
  • s3.intern()去常量池查找“11”發現不存在,將“11”複製到常量池中
  • String s4 = "11"s4會指向常量池中的“11”
  • System.out.println(s3 == s4)常量池中地址和堆中的地址是不一樣的所以爲false
    第二段代碼和第一段基本相同,只有s3.intern()由於在String s4 = "11"之後執行,所以會在常量池找到“11”,所以不會進行復制
2.1、JDK1.7+分析

JDK1.7以及以上的版本intern()的作用:檢查常量池裏是否存在這個字符串,如果存在,就返回池裏的字符串;如果不存在,該方法會把這個字符串在堆內存中的地址添加到常量池中,然後再返回這個地址。
先看第一段代碼的情況:
在這裏插入圖片描述

  • String s = new String("1")會在常量池中創建常量“1”,堆中創建一個String對象指向常量池的“1”,棧中的s指向堆中的String對象
  • s.intern()會返回常量池中的地址,在這裏沒有什麼意義
  • String s2 = "1"s2指向常量池中的“1”
  • System.out.println(s == s2)常量池中地址和堆中的地址是不一樣的所以爲false
  • String s3 = new String("1") + new String("1")其中new String("1")創建和第一行s創建是一樣的。字符串的拼接用到了StringBuilder,這裏不多做介紹。最終會新創建一個存放“11”的String對象(注意這個時候的“11”是在堆內存中),s3指向堆中的String對象
  • s3.intern()去常量池查找“11”發現不存在,將“11”在堆內存中的地址放到常量池中
  • String s4 = "11""11"在常量池中存在,所以s4會指向常量池中“11”對應的引用地址(也就是s3的地址)
  • System.out.println(s3 == s4)s3和s4中的地址是一樣的所以爲true

再看第二段代碼:
在這裏插入圖片描述

  • String s = new String("1")會在常量池中創建常量“1”,堆中創建一個String對象指向常量池的“1”,棧中的s指向堆中的String對象
  • String s2 = "1"s2指向常量池中的“1”
  • s.intern()會返回常量池中的地址,在這裏沒有什麼意義
  • System.out.println(s == s2)常量池中地址和堆中的地址是不一樣的所以爲false
  • String s3 = new String("1") + new String("1")其中new String("1")創建和第一行s創建是一樣的。字符串的拼接用到了StringBuilder,這裏不多做介紹。最終會新創建一個存放“11”的String對象(注意這個時候的“11”是在堆內存中),s3指向堆中的String對象
  • String s4 = "11"在常量池創建“11”,s4指向常量池
  • s3.intern()由於“11”已經存在,會返回常量池中的地址,在這裏沒有什麼意義
  • System.out.println(s3 == s4)常量池中地址和堆中的地址是不一樣的所以爲false

3、總結

看完這些再看開篇時候的例子就很清楚了吧。最後總結以下:
intern()的作用:檢查常量池裏是否存在這個字符串,如果存在,就返回池裏的字符串;如果不存在,JDK1.6及以下版本會把這個字符串添加到常量池中,JDK1.7以及以上的版本則把這個字符串在堆內存中的地址添加到常量池中。

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