在 Java 中有很多关于字符串不变性的问题,问题的作者实际上重新分配了参考。
然而,有一个值得注意的情况,似乎没有重新分配字符串:
String s = "hello";
s += " world";
您将其视为对字符串的实际修改。在家里试试。
我很确定这是某种语法糖,并被编译器翻译成具有与以下相同语义的东西:
String s = "hello";
s = s + " world";
有人可以证实这个事实吗?
错误的。
x += y
只是x = x + y
.
它仍然是一个常规的赋值操作,它不会修改任何现有的实例。
它是一个新对象,原始对象不会被修改;
这个:
public static void main(String[] args) {
String s = "hello";
s += " world";
System.out.println(s);
}
翻译成这样:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hello
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String world
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
}
这会创建两个String
s 并将它们添加到一个新String
对象中。
我既不能确认也不能否认。如果优化编译器可以向自己证明没有其他线程可以“看到” +=
s 的初始(pre-)值,那么它将可以自由地优化连接并将代码编译为等价于
String s = "hello world";
编译器对于它们如何从源代码转换为字节码有很大的自由度,只要它们遵循内存模型。
为无法阅读 Java 字节码的人扩展 skynorth 的答案,给出以下源代码:
public static void main(String... args) {
String s = "hello";
s += "world";
}
在编译的类文件上运行JAD 反编译器时,您会得到以下 Java 代码:
public static void main(String args[]) {
String s = "hello";
s = (new StringBuilder(String.valueOf(s))).append("world").toString();
}
这是不言自明的,并且与 skynort 发布的字节码完全对应。