这是我的代码:
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
由编译器在编译时进行评估,然后被 interned 。
但是,s2
和s3
呢?两者都是在运行时上创建的。那么,是否创建了两个不同的字符串?这些字符串会进入常量字符串池吗?
答案 0 :(得分:4)
s1
已被完全拘留,即
This islimafoxtrottango
因为字符串连接发生在编译时。
另外两个需要方法调用,仅中间字符串被插入。串联不是这样,因为它发生在运行时。
您还已经观察到了,
System.out.println(s1 == s2); //prints false
System.out.println(s3 == s2); //prints false
否则将打印true
。
您可以查看 JLS (Java语言规范)以获取有关此行为的详细信息,每个有效的Java实现都必须完全按照这种方式进行操作。
来自JLS§15.28的有关常量表达式的
:常量表达式是表示原始类型或
String
的值的表达式,该值不会突然完成,并且仅使用以下内容组成:
以下列表或多或少包含
:
- 文字
- 操作员喜欢+
- 常量变量
重要的是,即使它们返回了恒定值,它也不会不列出任何方法调用。
它还指出:
类型为
String
的常量表达式总是“ interem ” ,以便使用方法{{ 1}}。
答案 1 :(得分:2)
如果您从命令行这样编译类:
javac.exe -g StringExperimenter.java
然后使用javap.exe -v StringExperimenter.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是实习生,而s2和s3是在运行时计算的(使用StringBuilder
)。
答案 2 :(得分:1)
字符串的含义在Java中是不变的。 以上结果是正确的。 每当我们通过隐式在字符串中追加内容时,都会在String实习生池中创建一个新引用。
因此,如果使用 equals 方法比较以上三个字符串,则返回True,但如果使用 == 运算符进行比较,则返回false。