运行时字符串是否被实习?

Are runtime Strings interned?

这是我的代码:

public class StringExperimenter {
    public static void main(String[] args) {
        String s1 = "This is" + "limafoxtrottango";
        String s2 = "This is" + getName();
        String s3 = "This is" + getName();
        System.out.println(s1 == s2);  // prints false
        System.out.println(s3 == s2);  // prints false
    }

    private static String getName() {
        return "limafoxtrotango";
    }
}

现在,我了解到 s1 由编译器在 编译时 求值,然后 内部

但是,s2s3 呢?这两个都是在 运行时 创建的。那么,是否创建了两个不同的字符串?这些字符串是否进入常量字符串池?

因为 字符串的概念在 java 中是不可变的。 以上结果为真。 每当我们通过连接在一个字符串中附加某些内容时,都会在字符串实习生池中创建一个新引用。

因此,如果使用 equals 方法比较以上所有 3 个字符串,则其 return 为真,但如果使用 == 运算符进行比较则为假.

s1 已完全实习,即

This islimafoxtrottango

因为字符串连接发生在 编译时

其他两个需要方法调用,只有中间字符串被驻留。连接不是,因为它发生在 runtime.

您也已经注意到了,因为

System.out.println(s1 == s2);    //prints false
System.out.println(s3 == s2);    //prints false

否则会打印 true.


您可以查看 JLS(Java 语言规范)以了解有关此行为的详细信息,每个有效的 Java 实现都必须完全按照这种方式进行。

来自JLS§15.28关于常量表达式

A constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:

以下列表或多或少包含:

  • literals
  • operators like +
  • constant variables

重要的是,它不会列出任何方法调用,即使它们return一个常量值。

它还指出:

Constant expressions of type String are always "interned" so as to share unique instances, using the method String.intern.

如果您像这样从命令行编译 class:

javac.exe -g StringExperimenter.java

然后用javap.exe -v StringExperimenter.class研究生成的class文件,你会看到,生成了如下字节码:

...  
public static void main(java.lang.String...);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
    Code:
      stack=3, locals=4, args_size=1
         0: ldc           #2                  // String This islimafoxtrottango
         2: astore_1
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #5                  // String This is
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
        15: invokestatic  #7                  // Method getName:()Ljava/lang/String;
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
        21: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: astore_2
        25: new           #3                  // class java/lang/StringBuilder
        28: dup
        29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        32: ldc           #5                  // String This is
        34: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
        37: invokestatic  #7                  // Method getName:()Ljava/lang/String;
        40: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
        43: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        46: astore_3
        47: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        50: aload_1
        51: aload_2
        52: if_acmpne     59
        55: iconst_1
        56: goto          60
        59: iconst_0
        60: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        63: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        66: aload_3
        67: aload_2
        68: if_acmpne     75
        71: iconst_1
        72: goto          76
        75: iconst_0
        76: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        79: return
...  

基于此,我会说 s1 是 intern 的,而 s2 和 s3 是在运行时计算的(使用 StringBuilder)。