1. 程式人生 > >Java虛擬機器之String Pool

Java虛擬機器之String Pool

String Pool 是一塊用來專門存放String的堆記憶體。String類是一個特殊類,建立的物件是不可變的。建立方式可以使用 new 關鍵字建立,也可以使用雙引號 ” ” 建立。
String Pool 有助於為Java Runtime節省大量空間,但需要更多時間來建立字串以及查詢。
這裡寫圖片描述

1、使用 ” ” 建立String物件

1、直接使用 ” ” 申明

String s1 = "Cat";

使用 ” ” 建立String物件,JVM首先會在String Pool 中查詢是否已經有該String,如有,直接返回常量池中的引用。若沒有,則在String Pool 中建立該字串並返回該引用。

JVM存在編譯期優化,所以在編譯的時候,”Cat” 就被儲存在了常量池中。

2、使用 + 申明

String str = "a" + "b";

由於JVM存在編譯期優化,對於兩個直接雙引號宣告的String的 + 操作,JVM在編譯期會直接優化為“ab”一個字串。同String str = “ab”,效果是一樣的。

3、使用String 變數申明

String a = "a";  
String b = a + "b";

由於b是一個String變數,編譯期無法確定b的值,所以不會優化為一個字串。即使我們知道b的值,但JVM認為它是個變數,變數的值只能在執行期才能確定,所以不會優化。

執行期字串的 + 連線符相當於 new,故該行程式碼在Heap中建立了一個內容為 “ab” 的String物件,並返回該物件的引用。因為沒有直接雙引號宣告 ab 這2個字元的字串,故常量池中不會生成 ab 這2個字元的字串。但是會有 “a” 和 “b” 這兩個String物件,因為他們都用雙引號聲明瞭。

String a = "a";  
String b = a + "b";
String c = "ab";
System.out.println(b == c);

輸出false。

4、加 final 修飾

final String a = "a";  
String b = a + "b"
;

使用 final 修飾的變數為常量,所以在編譯期JVM能確定b的值,所以對 + 可以優化為 “ab” 2個字元的字串。所以在常量池中會同時存在 “a” 和 “ab” 。

final String a = "a";  
String b = a + "b";
String c = "ab";
System.out.println(b == c);

輸出true。

2、使用 new 建立String物件

String s3 = new String("Cat");

使用 new 來建立物件,都會在堆中建立一個新物件,然後返回該物件的引用。
對於該行程式碼生成了兩個String物件。
第一步:由於使用了 ” “,所以首先在常量池中查詢是否有了 Cat 字串,若沒有,則在常量池中建立字串 Cat,然後進行下一步;若有了,則直接進行下一步。
第二步:由於使用了new,所以JVM會在Heap(堆)中建立一個內容相同的String物件,然後返回堆中String物件的引用。

所以該行程式碼分別在常量池和堆中建立了兩個內容相同的String物件。

String s1 = "Cat";
String s3 = new String("Cat");   
System.out.println(s1==s3);

輸出false。

3、intern( ) 方法

String.intern( ) 是一個Native方法,它的作用是:如果執行時常量池中已經包含一個等於此String物件內容的字串,則返回常量池中該字串的引用;如果沒有,則在常量池中記錄Java Heap中首次出現的該字串的引用,並返回該引用。

JDK1.6 之前
當常量池中沒有該字串時,JDK16 的intern()方法的實現是在常量池中建立與此String內容相同的字串,並返回該引用。

String s4 = "test";  
System.out.println(s4 == s4.intern());        

輸出true。建立過程是先在常量池中建立字串 “test”,然後返回該引用。

String s5 = new String("test");  
System.out.println(s5 == s5.intern()); 

輸出false。由於使用了 ” “,所以會在常量池中建立 “test”,而 new 時,會在堆中新建立一個物件。s5指向堆中物件,s5.intern() 是常量池中的引用。

String s6 = new StringBuilder("a").append("b").toString();  
System.out.println(s6 == s6.intern());

輸出true。ab在常量池中沒有,但是在heap中存在,所以 intern 時,會直接返回該heap中的引用。