Java Strings and String Pool: Literals, intern(), == and equals() with Worked Examples

Trace pooled and non-pooled Java String references, compare identity with content, and learn to count objects without hiding the assumptions.

KnowledgeGate Team

Exam prep & CS education

Updated 11 Sep 20266 min read

Two Java variables can print the same characters while == is false. A single new expression or runtime concatenation can change the answer, even when the visible text is identical. Literals, the string pool, new String(...), intern(), ==, and equals() determine reference identity, content equality, and object counts in Java.

For the broader immutability, StringBuilder, and fresher-test foundation, start with String Handling in Java: String Pool, Immutability and Fresher-Test Questions. Reference-identity problems go further: intern(), compile-time versus runtime concatenation, and assumption-bound object counts determine the answer.

Java String basics: object, literal, and pool

java.lang.String is an immutable class whose objects represent character sequences. Keep three ideas separate: "Java" is a value, a variable such as a holds a reference, and that reference points to a String object. Two references can therefore lead to objects with equal values without being the same reference.

The string pool is the JVM's canonicalisation table for interned strings. String literals and compile-time constant strings use canonical pooled references. new String(...) explicitly creates a distinct String object, while intern() returns the canonical pooled reference for the same content. It does not modify the object on which it is called.

Expression

Reference obtained

Identity implication

String a = "Java"

Canonical reference for the literal

Another use of the same literal normally reuses it

new String("Java")

Reference to a distinct object

It is not identical to the pooled literal object

a.intern()

Canonical pooled reference

If a is already canonical, the same reference is returned

This model is about reference behaviour. You do not need to imagine the pool as a physically separate memory area to solve these questions.

Java string pool worked example: trace five references

Trace this code one statement at a time:

java
String a = "Java";
String b = "Java";
String c = new String("Java");
String d = c.intern();
String e = new String("Java").intern();

a obtains the canonical reference for "Java", and b reuses it. Thus a == b is true. The new expression gives c a distinct reference, so a == c is false. Calling c.intern() returns the canonical reference without changing c; therefore a == d is true and c == d is false.

The last line first creates another distinct object, then intern() returns the canonical reference to e. Hence a == e is true. Content is equal throughout, so a.equals(c) is true.

For object counting, state the scope. Assume "Java" was not interned before this class loaded, count only String objects attributable to these statements, and exclude backing arrays and JVM helper objects. The count is 1 + 1 + 1 = 3: one canonical literal object, the object referenced by c, and the temporary object created in the final line. After assignment, e points to the canonical object, so none of these variables retains that temporary object.

Reference map: a, b, d, and e share one pooled 'Java' object while c points to a separate object, for three String objects total.

Java String comparison: == versus equals()

For objects, == compares reference identity. String.equals() compares character content. The earlier variables make the difference exact:

Check

Result

Reason

a == b

true

Both hold the canonical reference

a == c

false

c points to a distinct object

a.equals(b)

true

Both values contain Java

a.equals(c)

true

The references differ, but the content matches

Content comparison is case-sensitive: "Java".equals("java") is false. By contrast, "Java".equalsIgnoreCase("java") is true. Use equals() when the question is whether text matches. Reserve == for the rare case where reference identity itself matters, not merely because two literals happen to share a pooled reference.

Java string concatenation: compile-time pooling versus runtime objects

Compilation time changes what can be canonicalised before the program runs:

java
String base = "Knowledge";
String suffix = "Gate";
String pooled = "KnowledgeGate";
String compileTime = "Knowledge" + "Gate";
String runTime = base + suffix;
String canonical = runTime.intern();

The compiler folds the two literal operands into the existing "KnowledgeGate" constant. Therefore compileTime == pooled is true. base and suffix are non-final variables, so their concatenation happens at runtime and produces a distinct result object. Thus runTime == pooled is false, while runTime.equals(pooled) is true. Finally, canonical == pooled is true because intern() returns the canonical reference.

Assume these literals were not previously interned and count only relevant String objects. There are four: pooled "Knowledge", pooled "Gate", pooled "KnowledgeGate", and the distinct runtime result "KnowledgeGate". In arithmetic form, 3 pooled + 1 runtime = 4. Neither compileTime nor canonical adds a new String object.

Two-lane diagram: compileTime and pooled share one KnowledgeGate object, runTime is a separate object, four String objects total.

Java String immutability: what changes and what does not

Consider String original = "Java"; String changed = original.concat(" Pool");. The value of original remains "Java", changed is "Java Pool", and original == changed is false. A reference variable may be reassigned, but the content of a String object is never edited in place.

Immutability makes sharing pooled strings safe because one user cannot alter the shared value for everyone else. It also keeps a string's content-based hash stable while the string is used as a map key. Hashing and Collision Resolution extends that idea into hash functions, buckets, and collision-handling methods.

For repeated construction, use a mutable builder:

java
StringBuilder builder = new StringBuilder("Java");
builder.append(" Pool");
String built = builder.toString();

The builder changes during construction. The final value of built is exactly "Java Pool", and built is an immutable String.

Java string object-counting questions: make assumptions explicit

Interview-style object counts are meaningful only when their scope is stated:

java
String x = "red";
String y = "red";
String z = new String("red");
String p = "blue";
String q = new String("blue");

Assume neither literal was already interned by another loaded class, count only String objects introduced by this snippet, and ignore backing arrays. The answer is four objects: one pooled red, one explicit new red, one pooled blue, and one explicit new blue. The calculation is 1 + 1 + 1 + 1 = 4; y reuses pooled red and adds nothing.

The exact checks are x == y is true, x == z is false, p == q is false, x.equals(z) is true, and z.intern() == x is true. If an object-count question omits the counting scope or the pre-existing-pool assumption, it is underspecified.

Java string pool traps and interview patterns

Trap

Wrong assumption and consequence

Better action

Use == for user-entered text

Equal content may arrive in distinct objects, so the check can be false

Compare content with equals()

Write new String(literal) by default

It creates a distinct object without a useful identity requirement

Use the literal unless distinct identity is genuinely needed

Expect variable concatenation to reuse a literal reference

Runtime concatenation can create equal content at a different reference

Use equals() for the result, or intern() only for deliberate canonicalisation

Intern unbounded user data as a default optimisation

The canonicalisation table can add memory and lookup pressure

Intern only for a measured need with a controlled value domain

The three common question forms reduce to the same model: predict boolean output, count objects under stated assumptions, or separate compile-time constants from runtime expressions. Apply the reference model in that order before choosing an answer. The Coding & DSA Courses for Placements category is a useful next path for tracing object construction and reference-based structures.

Java strings and string pool: the short version and next step

Keep six rules together:

  1. Literals reuse canonical references.

  2. new String(...) creates a distinct object.

  3. intern() returns the canonical reference and does not mutate its caller.

  4. == checks identity.

  5. equals() checks content.

  6. Compile-time literal concatenation can be pooled, while runtime variable concatenation creates a distinct result.

If you are building the language from the beginning, Complete Java provides the full Java sequence. If your next target is coding-round problem solving, DSA using Java carries that Java foundation into data structures and algorithms.