关于常量池中的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 也可能创建对象,但前提是该字面量还未进入常量池。