This question already has an answer here:
- Is a Java string really immutable? 15 answers
Java string pool coupled with reflection can produce some unimaginable result in Java:
import java.lang.reflect.Field;
class MessingWithString {
public static void main (String[] args) {
String str = "Mario";
toLuigi(str);
System.out.println(str + " " + "Mario");
}
public static void toLuigi(String original) {
try {
Field stringValue = String.class.getDeclaredField("value");
stringValue.setAccessible(true);
stringValue.set(original, "Luigi".toCharArray());
} catch (Exception ex) {
// Ignore exceptions
}
}
}
Above code will print:
"Luigi Luigi"
What happened to Mario?
You changed it, basically. Yes, with reflection you can violate the immutability of strings... and due to string interning, that means any use of "Mario" (other than in a larger string constant expression, which would have been resolved at compile-time) will end up as "Luigi" in the rest of the program.
This kinds of thing is why reflection requires security permissions...
Note that the expression
str + " " + "Mario"
does not perform any compile-time concatenation, due to the left-associativity of+
. It's effectively(str + " ") + "Mario"
, which is why you still seeLuigi Luigi
. If you change the code to:... then you'll see
Luigi Mario
as the compiler will have interned" Mario"
to a different string to"Mario"
.To explain the existing answers a bit more, let's take a look at your generated byte code (Only the
main()
method here).Now, any changes to the content's of that location will affect both the references (And any other you give too).
Another related point: you can make use of the constant pool to improve the performance of string comparisons in some circumstances, by using the
String.intern()
method.That method returns the instance of String with the same contents as the String on which it is invoked from the String constants pool, adding it it if is not yet present. In other words, after using
intern()
, all Strings with the same contents are guaranteed to be the same String instance as each other and as any String constants with those contents, meaning you can then use the equals operator (==
) on them.This is just an example which is not very useful on its own, but it illustrates the point:
This little trick isn't worth designing your code around, but it is worth keeping in mind for the day when you notice a little more speed could be eked out of some bit of performance sensitive code by using the
==
operator on a string with judicious use ofintern()
.String literals are stored in the string pool and their canonical value is used. Both
"Mario"
literals aren't just strings with the same value, they are the same object. Manipulating one of them (using reflection) will modify "both" of them, as they are just two references to the same object.You just changed the
String
of String constant poolMario
toLuigi
which was referenced by multipleString
s, so every referencing literalMario
is nowLuigi
.You have fetched the
char[]
namedvalue
field from classString
Make it accessible.
You changed
original
String
field toLuigi
. But original isMario
theString
literal and literal belongs to theString
pool and all are interned. Which means all the literals which has same content refers to the same memory address.Basically you have changed the Mario of
String
pool which got reflected in all the referencing fields. If you createString
Object
(i.e.new String("Mario")
) instead of literal you will not face this behavior because than you will have two differentMario
s .It was set to Luigi. Strings in Java are immutable; thus, the compiler can interpret all mentions of
"Mario"
as references to the same String constant pool item (roughly, "memory location"). You used reflection to change that item; so all"Mario"
in your code are now as if you wrote"Luigi"
.