Programs typically contain many string literals in their code. In Java, these constants are compiled for something called a row table for efficiency. For example, if you use the string "Name: " in ten different places, the JVM (usually) has only one instance of this line and in all ten places where it is used, all links point to one instance. It saves memory.
This optimization is possible because String is immutable. If it were possible to change the line by changing it, one place would mean its change in the other nine. Therefore, any operation that changes the string returns a new instance. Therefore, if you do this:
String s = "boink"; s.toUpperCase(); System.out.println(s);
he prints boink , not boink .
Now there is one more difficult bit: several instances of java.lang.String can point to the same base char[] for their character data, in other words, they can be different representations on the same char[] , using only an array slice. Again, optimization of efficiency. The substring() method is one of the cases when this happens.
s1 = "Fred47"; //String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6 // ^........................^ s2 = s1.substring(2, 5); //String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3 // ^.........^ // the two strings are sharing the same char[]!
In your SCJP question, it all boils down to:
- The string
"Fred" taken from the String table. - Row
"47" taken from the String table. - The string
"Fred47" is created during a method call. // 1 - The string
"ed4" is created during the method call, using the same support array as "Fred47" // 2 - When the method is called, the line
"ed4" created .//3 s.toString() does not create a new one, it just returns this .
One interesting marginal case of everything: think about what happens if you have a really long line, for example, a web page taken from the Internet, let char[] be two megabytes in length. If you take the substring(0, 4) this, you get a new String that looks like four characters long, but it still shares the two megabytes of backup data. This is not all that is common in the real world, but it can be a huge waste of memory! In the rare case when you come across this as a problem, you can use new String(hugeString.substring(0, 4)) to create a String with a new small support array.
Finally, you can force a row into the row table at run time by calling intern() on it. The basic rule in this case: do not do this. Extended Rule: Do not do this unless you have used a memory profiler to make sure that this is a useful optimization.