天天看點

字元串常量池與intern方法

看了幾篇關于intern方法的文章,總結如下:

一、主要知識點:

1、使用引号聲明的字元串常量都會直接在字元串常量池中生成

2、intern方法在jdk1.6與jdk1.7中有點差別,主要原因是jdk1.7中将常量池移到了堆中

3、使用intern方法會很大程度的節省記憶體

二、Java中字元串建立的兩種方式:

Java中字元串對象建立有兩種形式,

一種為字面量形式,如String str=" hello",

另一種就是使用标準的構造對象的方法,如String str=new String("hello");

這兩種經常使用,在記憶體上面有一些差別。這一切都是JVM為了減少字元串對象重複建立,其維護了一個特殊的記憶體,這段記憶體就是字元串常量池

工作原理

當代碼中出現字面量形式建立字元串對象時嗎,JVM就會首先對這個字面量進行檢查,,如果字元串常量池中存在相同内容的字元串對象的引用,則将這個引用傳回,否則新的字元串對象就被建立,然後将這個引用放入字元串常量池中,并傳回該引用。

String str1="hello"

JVM檢測這個字面量,這裡我們認為沒有内容為"hello"的對象存在。JVM通過字元串常量池查找不到内容為"hello"的字元串對象存在,那麼會建立這個字元串對象,然後将剛建立的對象的引用放入到字元串常量池中,并且将引用傳回給變量str1。

String str2="hello"

同樣JVM還是要檢測這個字面量,JVM通過查找字元串常量池,發現内容為”droid”字元串對象存在,于是将已經存在的字元串對象的引用傳回給變量str2。注意這裡不會重新建立新的字元串對象。

驗證是否為str1和str2是否指向同一對象,我們可以通過這段代碼

System.out.println(str1 == str2);

結果為true。

String str3 = new String("hello");

當我們使用了new來構造字元串對象的時候,不管字元串常量池中有沒有相同内容的對象的引用,新的字元串對象都會建立。是以我們使用下面代碼測試一下,

String str3 = new String("hello");

System.out.println(str1 == str3);

結果如我們所想,為false,表明這兩個變量指向的為不同的對象。

三、JDK1.6與JDK1.7中intern方法的差別:

1、JDK1.6中

在JDK1.6中,常量池是放在Perm區(屬于方法區)中的,這是和堆區完全分開的。

在JDK1.6中,intern()方法的作用:

檢查字元串池裡是否存在"hello"這麼一個字元串,如果存在,就傳回池裡的字元串;如果不存在,該方法會把"hello"添加到字元串池中,然後再傳回它的引用。

(1)

public static void main(String[] args) {
		//在常量池中添加字元串hello
		String s1="hello";
		//由于常量池中已經存在"hello",是以傳回該字元串
		String s2=s1.intern();
		System.out.println(s2);       //hello
		//s1和s2都指向常量池中"hello"
		System.out.println(s2==s1);   //true
	}
           

(2)

public static void main(String[] args) {
		//在堆中建立字元串并且在常量池中添加字元串hello  因為使用引号聲明的字元串常量都會直接在字元串常量池中生成
		String s1=new String("hello");
		//由于常量池中已經存在"hello",是以傳回該字元串
		String s2=s1.intern();
		System.out.println(s2);       //hello
		//由于s1指向的字元串在堆中,而s2指向的字元串在常量池中,是以位址不同(==判斷的是位址)
		System.out.println(s2==s1);   //false
	}
           

(3)

public static void main(String[] args) {
		//在堆中建立字元串"hello world"  在常量池中分别建立"hello"與"world"
		String s1=new String("hello")+new String("World");
		//由于常量池中不存在"hello world",是以添加該字元串,并傳回引用
		String s2=s1.intern();
		System.out.println(s2);       //helloWorld
		//由于s1指向的字元串在堆中,而s2指向的字元串在常量池中,是以位址不同(==判斷的是位址)
		System.out.println(s2==s1);   //false
	}
           

注意:在jdk1.7中會輸出true,下面将介紹。

2、在JDK1.7中

在JDK1.7中,将常量池放到了堆中。

正常情況下,使用new建立的字元串對象的引用不會放入字元串常量池,但是在使用JDK1.7的情況下,如果想将這個對象的引用加入到字元串常量池,可以使用intern方法。調用intern後,首先檢查字元串常量池中是否有該對象的引用,如果存在,則将這個引用傳回給變量,否則将引用加入并傳回給變量。

public static void main(String[] args) {
		//在堆中建立字元串"hello world"  在常量池中分别建立"hello"與"world"
		String s1=new String("hello")+new String("World");
		/*
		 * 由于常量池中不存在"hello world",但是在堆中有該字元串,
		 * 是以将引用s1傳回,即s2也指向堆中的字元串
		 */
		String s2=s1.intern();
		System.out.println(s2);       //helloWorld
		//是以位址相同
		System.out.println(s2==s1);   //true
		
		String s3="helloWorld";
		//常量池中已經存在"helloWorld"的引用,是以s3也直接指向堆中的字元串
		System.out.println(s3==s1);  //true
	}
           

關于intern()方法為什麼能夠節省記憶體,參見 Java技術——你真的了解String類的intern()方法嗎