JB Header

Java String Pool: String Literals vs new String()

Introduction Programs use the same strings again and again - "OK", "id", "name". Java avoids storing thousands of identical copies with a special area called the String pool. This tutorial explains the Java String pool, and the difference between string literals and new String(). We will start with what the string pool is and how literals use it. We will then see why new String() creates a separate object, and prove both with ==. Next we will look at compile-time constants and strings built at runtime, and move strings into the pool with intern(). Lastly, we will go through what all this means for your code - and the classic interview question of how many objects a line creates.

(Note - The string pool only works because strings are immutable, as explained in Java String Basics and ImmutabilityClick to read tutorial: Java String Basics and Why Strings Are Immutable.) What is the String pool? The String pool (also called the string constant pool or intern pool) is a table, kept by the JVM, of unique String objects. Since Java 7 it lives on the ordinary heap, so pooled strings which are no longer used can be garbage collected.

Every string literal in your code is pooled. When a class is loaded and a literal such as "Java" is first used, the JVM looks in the pool: if a "Java" object is already there, the literal refers to it; if not, a new object is created and added to the pool. So every "Java" literal in the whole program refers to the same object. This is safe only because strings are immutable - no one can change the shared object. Literals vs new String() new always creates a new object. So new String("Java") creates a separate String object on the heap, outside the pool, containing a copy of the pooled literal's characters. The == operator, which checks whether two references point to the same object, shows the difference.
String literals share one pooled object
//PoolDemo.java
public class PoolDemo {
  public static void main(String[] args) {
    String a = "Java";
    String b = "Java";
    String c = new String("Java");
    String d = new String("Java");

    System.out.println("a == b      : " + (a == b));
    System.out.println("a == c      : " + (a == c));
    System.out.println("c == d      : " + (c == d));
    System.out.println("a.equals(c) : " + a.equals(c));
    System.out.println("same identity (a,b), (a,c): "
        + (System.identityHashCode(a) == System.identityHashCode(b)) + ", "
        + (System.identityHashCode(a) == System.identityHashCode(c)));
  }
}
 OUTPUT of the above code
a == b      : true
a == c      : false
c == d      : false
a.equals(c) : true
same identity (a,b), (a,c): true, false
Explanation of the code
  • a and b come from the same literal, so they refer to the one pooled object - a == b is true.
  • c and d were each created with new, so each is a separate object, different from the pooled one and from each other.
  • equals() compares the characters, so a.equals(c) is true even though they are different objects.
  • System.identityHashCode() is based on the object itself, not its contents, which confirms which variables share an object.
Compile-time constants and runtime strings Which strings end up in the pool? The rule is -
  • Constant expressions are pooled. When + joins only literals and final constants, the compiler joins them itself, and the result is a single pooled literal. "Ja" + "va" is exactly the same as "Java".
  • Strings built at runtime are not pooled. Joining a non-final variable, reading input, or calling methods such as substring() or toUpperCase() creates a new, unpooled object.
Compile-time constants vs runtime strings
//ConstantsDemo.java
public class ConstantsDemo {
  public static void main(String[] args) {
    String java = "Java";
    String joinedLiterals = "Ja" + "va";        // joined by the compiler
    final String ja = "Ja";
    String joinedConstant = ja + "va";          // ja is a constant, also joined by the compiler
    String part = "Ja";
    String joinedAtRuntime = part + "va";       // part is not final: joined at runtime
    String fromMethod = "JAVA".toLowerCase().replace('j', 'J');

    System.out.println("literal + literal  : " + (java == joinedLiterals));
    System.out.println("constant + literal : " + (java == joinedConstant));
    System.out.println("variable + literal : " + (java == joinedAtRuntime));
    System.out.println("from methods       : " + (java == fromMethod));
    System.out.println("but all are equal  : " + (java.equals(joinedAtRuntime) && java.equals(fromMethod)));
  }
}
 OUTPUT of the above code
literal + literal  : true
constant + literal : true
variable + literal : false
from methods       : false
but all are equal  : true
This is exactly why comparing strings with == is so dangerous - it gives true for some equal strings and false for others, depending on how each string was made. intern() - adding a string to the pool intern() returns the pooled copy of a string. If an equal string is already in the pool, that object is returned; otherwise this string is added to the pool and returned.
Using intern()
//InternDemo.java
public class InternDemo {
  public static void main(String[] args) {
    String pooled = "JavaBrahman";
    String built = new StringBuilder("Java").append("Brahman").toString();
    System.out.println("pooled == built          : " + (pooled == built));
    System.out.println("pooled == built.intern() : " + (pooled == built.intern()));
  }
}
 OUTPUT of the above code
pooled == built          : false
pooled == built.intern() : true
intern() can save memory when a program holds very many copies of a small set of strings - for example, millions of records with the same few country names. In everyday code you rarely need it.
How many objects does this create? A classic interview question asks how many String objects this line creates -
The classic interview question
String s = new String("Hello");
The answer is up to two -
  1. the literal "Hello", created in the pool when the class is loaded and the literal is first used - unless another "Hello" literal has already put it there;
  2. the new String object created by new, which s refers to.
Running the line a second time creates only one more object - the literal is already in the pool. What this means for your code Summary In this tutorial we first saw that the String pool keeps one shared object for each distinct string literal, which is safe because strings are immutable. We then compared literals with new String(), and used == and equals() to show that new always creates a separate object. Next we saw that compile-time constant expressions are pooled while strings built at runtime are not, and used intern() to get a pooled copy. Lastly, we answered the "how many objects" interview question, and went through what the pool means for everyday code. In the next tutorial, we will compare strings properly with equals() and compareTo()Click to read tutorial: Java String Comparison: == vs equals() vs compareTo().