TL;DR: Don't do that. That code is (ab)using reflection to violate the specification. According to the JLS, String
values cannot change. That code changes the value of the string by accessing its undocumented private internal structures. Bad idea.
What's going on:
Equivalent string literals in a class are all combined so that at runtime, they all refer to the same string object in memory; they're written to the class's "constant pool." So all occurrences of "IMMUTABLE"
in that class actually refer to the same instance of String
. That means that in effect, that last line
System.out.println("IMMUTABLE");
...is equivalent to
System.out.println(someString);
...since the literal and the variable refer to the same object once the class is loaded.
Since the code (ab)uses reflection to overwrite the private undocumented value of the string object, naturally you see that updated state in all the places the object is used.
It would also happen if you moved your System.out.println("IMMUTABLE");
line into a separate class and ensured that that class was loaded before your code modified someString
. That's because when a class is loaded, the strings in its constants pool are interned so that equivalent string constants in different classes end up referring to the same String
object. This isn't something you'd want to rely on, not least because strings can (these days) get chucked out of the intern pool, but in a simple case you'd probably observe it.
So for instance, suppose you had this separate class:
public class Separate {
public static void show() {
System.out.println("IMMUTABLE");
}
}
And you modified your code to use it, but only after modifying someString
:
public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
String someString = "IMMUTABLE";
// ...
System.out.println(someString); // prints NOTREALLY
Separate.show();
}
You'd get
NOTREALLY
IMMUTABLE
...because by the time Separate
is loaded, you've already modified the version of the string that's in the intern pool, so when we load Separate
it's not a match and ends up referring to a different String
object.
But if you ensure that Separate
is loaded before you modify it:
public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
Separate.show(); // <================ Note
String someString = "IMMUTABLE";
// ...
System.out.println(someString); // prints NOTREALLY
Separate.show();
}
you get this bit of fun:
IMMUTABLE
NOTREALLY
NOTREALLY
...because as of when Separate
is loaded, its "IMMUTABLE"
is equivalent to the one in the intern pool, so Separate
uses that String
instance — which the code in your main
then modifies.