關于常量池中的String類型的數據,在JDK6中只可能是對象,在JDK7中既可以是對象也可以是引用
案例一:
String s1 = new String("1");
String s2 = "1";
System.out.println(s1 == s2);
s1
: 執行new String("1")
,JVM 首先在字符串常量池中查找或添加字面量"1"
,然后在堆內存中新建一個內容為"1"
的String
對象。s1
指向的是這個堆對象。s2
: 指向字符串常量池中已有的"1"
。結果
:s1 == s2
比較的是引用地址,一個在堆中、一個在常量池中,因此結果為false
。
案例二:
String s1 = new String("1") + new String("1");
String s2 = "11";
System.out.println(s1 == s2);
s1
: 通過兩個new String("1")
創建兩個堆中對象,然后使用StringBuilder
進行拼接,最終生成了一個新的"11"
字符串對象,存放在堆內存中。s2
:"11"
是字面量,編譯器會將其放入字符串常量池,并由s2
指向。結果
:s1
指向堆中的"11"
對象,s2
指向常量池中的"11"
,引用不同,結果為false
。
案例三:
String s1 = new String("1") + new String("1");
String s2 = s1.intern();
System.out.println(s1 == s2);
s1
: 與案例二一樣,通過拼接在堆中創建了一個"11"
的String
對象。intern()
: 在 JDK7+ 中,intern()
會將當前堆中的對象引用嘗試放入字符串常量池中。如果常量池中尚未存在"11"
,則將該對象引用添加進去,并返回這個引用。s2
: 返回的是字符串常量池中的引用,而由于常量池中原本沒有"11"
,此時存入的是s1
指向的堆對象本身。結果
:s1 == s2
,兩者引用相同,結果為true
。
JVM冷啟動時,String str = “1”
執行流程:
-
JVM 首先會去常量池中查找有沒有字面量
"1"
:- 若沒有,JVM 會在堆中創建一個字符串對象
"1"
,并將它的引用記錄進常量池; - 若已存在,JVM 直接復用已有對象的引用。
- 若沒有,JVM 會在堆中創建一個字符串對象
-
局部變量
str
會從常量池中取出引用,指向對應的字符串對象。
所以即使沒有顯式地寫
new
,JVM 也可能創建對象,但前提是該字面量還未進入常量池。