运行时字符串是否被实习?
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
由编译器在 编译时 求值,然后 内部 。
但是,s2
和 s3
呢?这两个都是在 运行时 创建的。那么,是否创建了两个不同的字符串?这些字符串是否进入常量字符串池?
因为 字符串的概念在 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
)。
这是我的代码:
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
由编译器在 编译时 求值,然后 内部 。
但是,s2
和 s3
呢?这两个都是在 运行时 创建的。那么,是否创建了两个不同的字符串?这些字符串是否进入常量字符串池?
因为 字符串的概念在 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 methodString.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
)。