字符串的Java不变性,带有+=&引用;操作人员
Java中存在关于字符串不变性的问题,问题的作者实际上为此重新分配了引用 然而,有一个引人注目的例子,似乎没有重新分配字符串:字符串的Java不变性,带有+=&引用;操作人员,java,string,immutability,Java,String,Immutability,Java中存在关于字符串不变性的问题,问题的作者实际上为此重新分配了引用 然而,有一个引人注目的例子,似乎没有重新分配字符串: String s = "hello"; s += " world"; 您将其视为字符串的实际修改。在家里试试 我很确定这是一种语法糖,编译器会将其翻译成与以下相同的语义: String s = "hello"; s = s + " world"; 有人能证实这个事实吗?错了 x+=y只是x=x+y的缩写 它仍然是一个常规的赋值操作,并且不会修改任何现有实例。我既不能
String s = "hello";
s += " world";
您将其视为字符串的实际修改。在家里试试
我很确定这是一种语法糖,编译器会将其翻译成与以下相同的语义:
String s = "hello";
s = s + " world";
有人能证实这个事实吗?错了
x+=y
只是x=x+y
的缩写它仍然是一个常规的赋值操作,并且不会修改任何现有实例。我既不能确认也不能否认它。如果一个优化编译器可以向自己证明,没有其他线程可以“看到”s的初始(pre-
+=
)值,那么它就可以自由地优化级联,并将代码编译为
String s = "hello world";
编译器对于如何将源代码转换为字节码有很大的自由度,只要遵循内存模型。它是一个新对象,原始对象不会被修改 这: 翻译成这样:
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
}
publicstaticvoidmain(java.lang.String[]);
代码:
0:ldc#2//字符串你好
2:astore_1
3:new#3//class java/lang/StringBuilder
6:dup
7:invokespecial#4//方法java/lang/StringBuilder。”“:()V
10:aload_1
11:invokevirtual#5//方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14:ldc#6//字符串世界
16:invokevirtual#5//方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19:invokevirtual#7//方法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//方法java/io/PrintStream.println:(Ljava/lang/String;)V
30:返回
}
这将创建两个
字符串
并将它们添加到一个新的字符串
对象中。为那些无法读取Java字节码的人扩展skynorth的答案,给出以下源代码:
public static void main(String... args) {
String s = "hello";
s += "world";
}
在编译的类文件上运行时,您会得到以下Java代码:
public static void main(String args[]) {
String s = "hello";
s = (new StringBuilder(String.valueOf(s))).append("world").toString();
}
这是不言自明的,与skynort发布的字节码完全对应。确认。在
+=
之后,堆中会有太多字符串,“hello”和“hello world”。前者没有参考,很快就会被垃圾回收。后者被引用,将一直存在到发布。在第二行,s是一个新对象。好吧,不是“错”。我想说“确认”:DIt最好包含一些来自StringBuilder.java的源代码,以强调事实上是一个新的字符串对象。(定义toString()
)<代码>返回新字符串(值,0,计数)代码>我不确定这是否正确<代码>“hello world”是一个常量字符串表达式,正如“hello”+“world”
一样。但我不认为OP的代码片段算作一个。因此,如果您后来测试了s==“hello world”
(故意使用==而不是equals),那么连接是否得到了优化就无关紧要了(对字符串进行内部处理)。我不确定的是,使用+
sugar创建的常量字符串是否需要作为新对象,或者是否只允许作为新对象。
public static void main(String args[]) {
String s = "hello";
s = (new StringBuilder(String.valueOf(s))).append("world").toString();
}