The string pool and interning
Literals are shared, anything built at run time is not, and that is the whole == confusion.
Open this lesson in the learning hubKey points
- Every string literal is interned. The constant appears once per class file and every use points at the same object.
- javac folds constant expressions, so
"ja" + "va"is already"java"in the class file, and interned too. - Concatenating anything non-constant builds a new object every time, which is exactly when
==starts returning false. intern()returns the pooled instance for an equal string, adding it to the pool if it was not there yet.- The pool is a native hash table sized by
-XX:StringTableSize; the string objects themselves live on the ordinary heap. - Compare with
equals. The only honest use of==on strings is a deliberate identity check.
Example
public class Main {
public static void main(String[] args) {
String literal = "java";
String folded = "ja" + "va"; // two literals: javac folds this at compile time
String fresh = new String("java"); // deliberately a brand new object
String prefix = "ja"; // not final, so not a constant
String built = prefix + "va"; // built at run time, in a StringBuilder
String pooled = built.intern(); // ask the pool for the shared instance
System.out.println("literal == folded : " + (literal == folded) + " folded into the same constant");
System.out.println("literal == fresh : " + (literal == fresh) + " new String() always allocates");
System.out.println("literal == built : " + (literal == built) + " concatenated at run time");
System.out.println("literal == pooled : " + (literal == pooled) + " intern() returned the pooled one");
System.out.println();
System.out.println("equals() is true for every one of them : "
+ (literal.equals(folded) && literal.equals(fresh)
&& literal.equals(built) && literal.equals(pooled)));
System.out.println();
System.out.println("identity hashes - a different number means a different object:");
System.out.println(" literal " + Integer.toHexString(System.identityHashCode(literal)));
System.out.println(" fresh " + Integer.toHexString(System.identityHashCode(fresh)));
System.out.println(" built " + Integer.toHexString(System.identityHashCode(built)));
System.out.println(" pooled " + Integer.toHexString(System.identityHashCode(pooled)));
}
}
Literals are shared. Anything built at run time is a new object.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.