在JVM中存放着一个字符串池,其中保存着很多String对象,这些对象可以被共享使用。当以字符串直接创建String对象时,会首先在字符串池中查找是否存在该常量。如果不存在,则在String Pool中创建一个,然后将其地址返回。如果在String Pool中查询到已经存在该常量,则不创建对象,直接返回这个对象地址。

String str1 = new String(“abc”);

使用用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。

  • “abc” 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 “abc” 字符串字面量;
  • 而使用 new 的方式会在堆中创建一个字符串对象。

创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。

  1. public class NewStringTest {
  2. public static void main(String[] args) {
  3. String s = new String("abc");
  4. }
  5. }

使用 javap -verbose 进行反编译,得到以下内容:
image.png
在 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 中添加一个新的字符串,并返回这个新字符串的引用。

  1. String s1 = new String("aaa");
  2. String s2 = new String("aaa");
  3. System.out.println(s1 == s2);//false
  4. String s3 = s1.intern();
  5. String s4 = s2.intern();
  6. System.out.println(s3 == s4);//true

例子

  1. public static void main(String[] args) {
  2. String s1 = "abc";
  3. String s2 = new String("abc");
  4. System.out.println(s1 == s2);// false
  5. }

单独使用””引号创建的字符串都是常量,编译期就已经确定存储到String Pool中。

  1. public static void main(String[] args) {
  2. String s1 = new String("abc");
  3. String s2 = new String("abc");
  4. System.out.println(s1 == s2);// false
  5. }

使用new String(“”)创建的对象会存储到heap中,是运行期新创建的。

  1. public static void main(String[] args) {
  2. String s1 = "abc";
  3. String s2 = "ab" + "c";
  4. System.out.println(s1 == s2);// true
  5. }

使用只包含常量的字符串连接符如”aa”+”bb”创建的也是常量,编译期就能确定已经存储到String Pool中

  1. public static void main(String[] args) {
  2. String s = "a";
  3. String s1 = "abc";
  4. String s2 = s + "bc";
  5. System.out.println(s1 == s2);// false
  6. }

使用包含变量的字符串连接如”aa”+s创建的对象是运行期才创建的,存储到heap中。

  1. public static void main(String[] args) {
  2. String s1 = "ab";
  3. String s2 = "ab" + getString();
  4. System.out.println(s1 == s2);// false
  5. }
  6. private static String getString(){
  7. return "c";
  8. }

理由同上

  1. public static void main(String[] args) {
  2. String s = "a";
  3. String s1 = "abc";
  4. String s2 = s + "bc";
  5. System.out.println(s1 == s2.intern());// true
  6. }

运行期调用String的intern()方法可以向String Pool中动态添加对象。