I've written this as a community wiki as I don't know if it's right and don't understand the details anyway.
What appears to happen is that when a string literal is encountered at runtime, the JVM checks the string pool (using equals
) to see if the string is already there. If it isn't there, a new instance is used. This object (either the new one or the one that was already in the string pool) is the one that will be used from now on for all string literals in that class that are the same.
Consider this example:
public class MyClass {
public MyClass() {
try {
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set("true", f.get("false"));
} catch (Exception e) {
}
}
public static void main(String[] args) {
System.out.println(true); // 1
new MyClass();
System.out.println(true); // 2
System.out.println("true"); // 3
printTrue();
OtherClass.printTrue();
}
public static void printTrue() {
System.out.println("true"); // 4
}
}
public class OtherClass {
static void printTrue() {
System.out.println("true"); // 5
}
}
This prints:
true
false
false
false
true
My explanation:
In line 1, the JVM encounters the literal "true"
in the PrintStream
class. A new string is added to the pool. Then new MyClass()
is invoked. Inside this constructor, the JVM encounters the string literal "true"
in the MyClass
class. This string is already in the pool, so the instance in the pool is the one that will be used, but crucially it is also the one that will later be used in lines 3 and 4. Then the array backing this string is modified. Lines 2, 3 and 4 therefore all print false
. Next, OtherClass.printTrue()
is invoked and the JVM encounters the string literal "true"
for the first time in OtherClass
. This string is not equal
to the one in the pool because the one in the pool now has backing array [f, a, l, s, e]
. Therefore a new string instance is used and true
is printed at line 5.
Now suppose we comment out line 1:
// System.out.println(true); // 1
This time the output is:
true
false
false
true
Why does line 2 produce a different result? The difference here is the literal "true"
is not encountered in the PrintStream
class until after the backing array has been modified. So the "wrong" string is not the one used in the PrintStream
class. However, lines 3 and 4 continue to print "false"
for the same reason as above.
System.out.println("true")
printsfalse
. – Narton"true"
should give the same instance. It's late, so I could be missing something really obvious. – RobotizeSystem.out.println(true)
before you set the field, then it works like we'd expect. Basically, it looks like the writer has its own cache-or-something of the string. – NartonSystem.out.println("true")
? – Jaddanmain
method to beSystem.out.println(true); new MyClass(); System.out.println(true);
then I gettrue
false
. – Nartontrue
the first time, then you need to print it the second time. Usingtrue
and then"true"
doesn't work and it looks like it has something to do when the literal will be changed and when theprint
class will be called, since this causes the JVM to evaluate if"true"
is already in the pool, which isn't the case anymore (according toequals
). If you useprint(true)
, thenMyClass
will alter the literal of thatPrintStream
class and if you useprint("true")
, then the literal of your test class. – Jaddan"true"
is still in the pool; it's just that after thef.set
call, itschar[] value
points to{'f', 'a', 'l', 's', 'e'}
– NartonhashCode
andequals
. The first one is the same, the second one isn't. – Jaddanprintln(true)
both times. The first and third statement in my example are identical, and yet they print out "true" and "false" respectively. If you remove the first call, then the remaining call prints "true". In other words, the side effects ofnew MyClass()
only seem to be affected if you first class-load PrintStream. I think you're right that when the classloader does its work, it uses hashCode/equals. – NartonSystem.out.println(true); MyClass m = new MyClass(); System.out.println("true");
work ... OPs "hack" only works it theprintln
class gets an equal value (I mean boolean in both times, or the String literal). – Jaddan