Introduction to Strings in Java — Immutability, String Pool & Equality

3 minute read

String is arguably the most used class in Java. It represents an immutable sequence of characters. Understanding how strings work in memory is critical for writing correct, efficient Java code — and it’s a favourite interview topic.


Creating Strings

String Literal (Preferred)

String greeting = "Hello, World!";

Literals are stored in the String pool (a special area of the heap). If two variables point to the same literal, they share the same object.

Using new Keyword

String greeting = new String("Hello, World!");

This always creates a new object on the heap, bypassing the pool.

Visualising the Difference

String a = "Java";            // goes into String pool
String b = "Java";            // reuses same pool object
String c = new String("Java"); // new object on heap

System.out.println(a == b);   // true  — same pool reference
System.out.println(a == c);   // false — different objects
System.out.println(a.equals(c)); // true — same content

The String Pool (String Interning)

The JVM maintains a pool of unique string literals to save memory:

Heap
┌──────────────────────────────┐
│  String Pool                 │
│  ┌────────┐  ┌────────────┐ │
│  │ "Java" │  │ "Hello"    │ │
│  └────────┘  └────────────┘ │
│                              │
│  Regular Heap                │
│  ┌──────────────────┐       │
│  │ new String("Java")│      │
│  └──────────────────┘       │
└──────────────────────────────┘

You can manually add a string to the pool with intern():

String s = new String("Java");
String interned = s.intern();      // returns pool reference
System.out.println(interned == "Java"); // true

String Immutability

Once a String object is created, its content cannot change. Every “modification” creates a new String:

String s = "Hello";
s.concat(" World");          // creates new String, but result is discarded!
System.out.println(s);       // "Hello" — unchanged

String result = s.concat(" World");
System.out.println(result);  // "Hello World"

Why Immutable?

Reason Explanation
Thread safety Immutable objects are inherently thread-safe — no synchronisation needed
String pool Sharing is safe only because strings can’t be modified
Security Strings used for passwords, URLs, class names can’t be tampered with
Hash code caching String caches its hash code — safe because it never changes
Performance The JVM makes heavy optimisations around immutability

== vs .equals()

This is the #1 Java interview question about strings.

Operator What It Compares
== Reference equality — are they the same object?
.equals() Content equality — do they contain the same characters?
String a = "Hello";
String b = "Hello";
String c = new String("Hello");

a == b          // true  (same pool object)
a == c          // false (different objects)
a.equals(b)     // true  (same content)
a.equals(c)     // true  (same content)

Rule: Always use .equals() to compare string content. Use == only when you intentionally want reference comparison.

Null-Safe Comparison

String input = null;
// input.equals("test")  ← NullPointerException!
"test".equals(input)     // false — safe

Or use Objects.equals():

import java.util.Objects;
Objects.equals(input, "test");  // false, null-safe

Strings Are Objects

String is a class in java.lang, not a primitive:

String s = "Java";
System.out.println(s.getClass().getName()); // java.lang.String
System.out.println(s.length());             // 4
System.out.println(s instanceof Object);    // true

But Java gives strings special syntax (no new required for literals, + concatenation) that makes them feel like primitives.


String Concatenation

Using +

String full = "Hello" + ", " + "World!";

The compiler optimises literal concatenation at compile time.

In Loops — Beware!

// BAD — creates a new String object each iteration
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i + " ";    // O(n²) overall!
}

Use StringBuilder instead (covered in a later chapter).


Common String Operations (Preview)

String s = "  Hello, World!  ";

s.length()                  // 19
s.trim()                    // "Hello, World!"
s.toLowerCase()             // "  hello, world!  "
s.charAt(7)                 // 'W'
s.contains("World")         // true
s.startsWith("  Hello")     // true
s.substring(2, 7)           // "Hello"
s.replace("World", "Java")  // "  Hello, Java!  "
s.split(",")                // ["  Hello", " World!  "]

We’ll cover all methods in detail in the next chapter.


Common Mistakes

Mistake Why It’s Wrong Fix
Using == to compare strings Compares references, not content Use .equals()
Expecting concat() to modify the original Strings are immutable Assign the result
Using + in loops O(n²) — creates many objects Use StringBuilder
Not handling null before .equals() NullPointerException Put literal first or use Objects.equals()

Key Takeaways

  1. Strings are immutable — every modification creates a new object.
  2. String literals are stored in the String pool; new String() creates a separate heap object.
  3. Use .equals() for content comparison, not ==.
  4. Immutability provides thread safety, security, and enables the String pool.
  5. Avoid + concatenation in loops — use StringBuilder.

What’s Next?

Next chapter: String Methods — a comprehensive guide to every essential String method with examples.

Updated: