Introduction to Strings in Java — Immutability, String Pool & Equality
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
- Strings are immutable — every modification creates a new object.
- String literals are stored in the String pool;
new String()creates a separate heap object. - Use
.equals()for content comparison, not==. - Immutability provides thread safety, security, and enables the String pool.
- Avoid
+concatenation in loops — useStringBuilder.
What’s Next?
Next chapter: String Methods — a comprehensive guide to every essential String method with examples.