在JVM中存放着一个字符串池,其中保存着很多String对象,这些对象可以被共享使用。当以字符串直接创建String对象时,会首先在字符串池中查找是否存在该常量。如果不存在,则在String Pool中创建一个,然后将其地址返回。如果在String Pool中查询到已经存在该常量,则不创建对象,直接返回这个对象地址。
String str1 = new String(“abc”);
使用用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。
- “abc” 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 “abc” 字符串字面量;
- 而使用 new 的方式会在堆中创建一个字符串对象。
创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。
public class NewStringTest {
public static void main(String[] args) {
String s = new String("abc");
}
}
使用 javap -verbose 进行反编译,得到以下内容:
在 Constant Pool 中,#19 存储这字符串字面量 “abc”,#3 是 String Pool 的字符串对象,它指向 #19
这个字符串字面面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3
将 String Pool 中的字符串对象作为 String 构造函数的参数。
String str2 = “abc”;
JVM首先会在String Pool中查找是否存在”abc”对象,如果已经有则不创建,没有的话则在String Pool中创建一个对象。
intern()
字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中。
当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2);//false
String s3 = s1.intern();
String s4 = s2.intern();
System.out.println(s3 == s4);//true
例子
public static void main(String[] args) {
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);// false
}
单独使用””引号创建的字符串都是常量,编译期就已经确定存储到String Pool中。
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);// false
}
使用new String(“”)创建的对象会存储到heap中,是运行期新创建的。
public static void main(String[] args) {
String s1 = "abc";
String s2 = "ab" + "c";
System.out.println(s1 == s2);// true
}
使用只包含常量的字符串连接符如”aa”+”bb”创建的也是常量,编译期就能确定已经存储到String Pool中
public static void main(String[] args) {
String s = "a";
String s1 = "abc";
String s2 = s + "bc";
System.out.println(s1 == s2);// false
}
使用包含变量的字符串连接如”aa”+s创建的对象是运行期才创建的,存储到heap中。
public static void main(String[] args) {
String s1 = "ab";
String s2 = "ab" + getString();
System.out.println(s1 == s2);// false
}
private static String getString(){
return "c";
}
理由同上
public static void main(String[] args) {
String s = "a";
String s1 = "abc";
String s2 = s + "bc";
System.out.println(s1 == s2.intern());// true
}
运行期调用String的intern()方法可以向String Pool中动态添加对象。