Java中的可变字符串

几乎每个人都知道Java中的字符串是不可变的。 最近我发现了一些可能暗示它并不总是正确的东西。 我们试试这段代码吧:

System.out.println("-------- BEFORE MODIFICATIONS --------"); String beforeTest = new String("Original"); System.out.println(beforeTest); java.lang.reflect.Field valueField = String.class.getDeclaredField("value"); valueField.setAccessible(true); valueField.set("Original", "Modified".toCharArray()); System.out.println("-------- AFTER MODIFICATIONS --------"); System.out.println(beforeTest); System.out.println("Original"); String test = new String("Original"); System.out.println(test); String test2 = new String("Original 2"); System.out.println(test2); 

输出将是:

 -------- BEFORE MODIFICATIONS -------- Original -------- AFTER MODIFICATIONS -------- Original Modified Modified Original 2 

这个技巧如何运作? JVM如何知道应该更改哪些对象以及哪些对象不应该更改? 这个伎俩的机制是什么? 为什么已创建beforeTest字符串未更改? 这个技巧真的会贬低strings are immutable原则吗?

字符串文字被嵌入到池中。 这意味着当你写作

 String s1 = "Foo"; String s2 = "Foo"; String s3 = new String("Foo"); 

s1和s2引用相同的String对象,s3引用另一个,由另一个char数组支持。

在您的代码中,您通过修改包含“原始”字符串文字实例的字符的私有字符数组来违反String的不变量。 但是因为beforeTest引用了另一个String实例,所以它没有被修改。

通过将字段保持私有到对象中,而不提供任何方法来修改此私有状态来实现不变性。 通过使用reflection,您可以破坏所有封装规则,从而可以违反不变性。