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中的引用。