← Home
☕Java Developer Interview Questions — Fresher to 5 YOEJava Dev InterviewNEW
🔤

String Handling

Immutability, pooling, builders · 15 questions

1

Why is String immutable in Java?

Must Know🌱 Fresher

Once a String is created, its internal character data can never change — every "modifying" method (concat, replace, toUpperCase...) returns a new String object. Java made String immutable for several compounding reasons:

  • String pool safety — literals are shared/reused across the whole program; if one reference could mutate a literal, every other reference to that literal would silently corrupt too.
  • Thread safety — an immutable object is inherently safe to share across threads with no synchronization.
  • Security — class names, file paths, network hosts, and DB URLs are passed around as Strings; if they could be mutated after a security check, that check could be bypassed.
  • Safe hashCode caching — since the content can never change, String can compute its hashCode() once and cache it, making repeated HashMap lookups on String keys fast.
String
2

What is the String constant pool?

Must Know🌱 Fresher

The String constant pool (part of the heap since Java 7) is a special cache of String objects. When you write a string literal, the JVM first checks the pool: if an equal string already exists there, it reuses that same object instead of allocating a new one — saving memory for the huge number of repeated string literals typical programs contain.

🏊 String pool vs regular heap objects
String a = "hello";        // pool: created (or reused) in the string pool
String b = "hello";        // pool: same reference as a  -> a == b is true
String c = new String("hello"); // heap: a brand-new object outside the pool -> a == c is false

Heap
+-------------------- String Pool --------------------+   +------------+
|  "hello"  <---- a                                    |   |  "hello"   | <---- c (new String(...))
|            <---- b (reused, same object as a)        |   +------------+
+-------------------------------------------------------+
StringMemory
3

What is the difference between String, StringBuilder, and StringBuffer?

Must Know🌱 Fresher
StringStringBuilderStringBuffer
Mutable?NoYesYes
Thread-safe?N/A (immutable)NoYes (synchronized methods)
PerformanceSlow for repeated modification (new object each time)FastSlower than StringBuilder (sync overhead)
When to useFixed/rarely-changing textBuilding strings on a single thread (the default choice)Building a string that's genuinely shared across threads
StringStringBuilder
4

What is the difference between a String literal and `new String(...)`?

Must Know🌱 Fresher

A literal ("hello") is looked up/interned in the string pool at compile/class-load time — equal literals share one object. new String("hello") always allocates a fresh object on the heap, outside the pool, even if an equal literal already exists — so == between them is false even though .equals() is true.

Literal vs new Stringjava
String a = "hello";
String b = "hello";
String c = new String("hello");

a == b;          // true  -- same pooled object
a == c;          // false -- c is a distinct heap object
a.equals(c);     // true  -- same content
c.intern() == a; // true  -- intern() pulls c's content back into the pool
StringMemory
5

Why is using `+` to concatenate strings in a loop bad practice, and what should you use instead?

Must Know🌱 Fresher

Each += inside the loop desugars to a new StringBuilder, append, then toString() (which copies the buffer into a new String) — every single iteration, discarding the previous StringBuilder and String immediately. For n iterations that's roughly O(n²) total character copying instead of O(n).

Bad vs goodO(n²)java
String result = "";
for (String s : items) {
    result += s; // new StringBuilder + new String, every iteration
}
Reuse one StringBuilderO(n)java
StringBuilder sb = new StringBuilder();
for (String s : items) {
    sb.append(s); // same buffer reused, amortized O(1) append
}
String result = sb.toString(); // one final copy
StringPerformance
6

What does String.intern() do?

Important📊 Mid

intern() looks up the string's content in the string pool: if an equal string is already there, it returns that pooled reference; otherwise it adds this string's content to the pool and returns it. It's the manual way to force a heap-allocated string (e.g. from new String(...), file reads, or network parsing) back into the pool so repeated equal values collapse to one object — useful when you're deduplicating a huge number of repeated strings (e.g. parsed log fields) to cut memory.

StringMemory
7

How does the `+` operator for string concatenation actually work internally?

Important📊 Mid

The compiler rewrites + concatenation into StringBuilder calls — you never actually see repeated raw allocation for a single expression made of multiple +. Roughly:

What the compiler generatesjava
String s = "a" + b + "c";
// compiles roughly to:
String s = new StringBuilder().append("a").append(b).append("c").toString();

The catch: this rewrite happens per statement, not across a whole loop. Soresult += x inside a loop creates a new StringBuilder every iteration — see the "concatenation in a loop" question for why that's expensive.

StringCompiler
8

Why is String such a popular HashMap key, and how is its hashCode cached?

Important📊 Mid

Strings make great map keys because they're immutable (a key can't change out from under the map after insertion, which would corrupt the bucket it's stored in) and because String caches its computed hashCode in a private field the first time it's computed — so repeated lookups of the same string object (or even equal string content that's already had its hash computed once) skip recomputing the hash walk over every character.

Cached hashCode (conceptually)java
public final class String {
    private int hash; // 0 until first computed, then cached
    public int hashCode() {
        int h = hash;
        if (h == 0 && !value.isEmpty()) {
            for (char c : value) h = 31 * h + c;
            hash = h;
        }
        return h;
    }
}
StringHashMap
9

What is the difference between String.equals() and String.compareTo()?

Important🌱 Fresher

equals() answers a yes/no question — are the two strings' contents identical (case-sensitive)? compareTo() returns an int for lexicographic ordering — negative if this string sorts before the argument, positive if after, zero if equal (which implies equals() would also be true). Use equals() for equality checks; use compareTo() (or a Comparator) for sorting.

String
10

Is StringBuilder thread-safe? Why does StringBuffer still exist?

Important🌱 Fresher

No — StringBuilder's methods aren't synchronized, so concurrent appends from multiple threads can corrupt its internal buffer (lost updates, out-of-order writes, even exceptions from internal array resizing races). StringBuffer predates StringBuilder and synchronizes every method to be thread-safe — but that locking is pure overhead in the overwhelmingly common case of building a string on a single thread, which is why StringBuilder was introduced (Java 5) as the faster unsynchronized default. Reach for StringBuffer only if the same builder instance is genuinely mutated by multiple threads (rare — most code should just build the string on one thread and hand off the finished, immutable result).

StringConcurrency
11

How does String.substring() work internally, and what was the old memory-leak gotcha?

Advanced📊 Mid

Before Java 7 update 6, substring() shared the same underlying char[] as the original string, storing only a new offset/length — O(1) and no copying, but it meant a tiny substring of a huge string kept the entire huge backing array alive in memory as long as the substring reference existed (a classic accidental memory leak). Since that fix, substring() always copies the needed characters into a new, appropriately-sized array — O(n) in the substring length, but no hidden retention of unrelated data.

StringMemory
12

What are "compact strings" introduced in Java 9?

Advanced📊 Mid

Before Java 9, String stored characters as a char[] — 2 bytes per character (UTF-16) even for plain ASCII/Latin-1 text, which is the overwhelming majority of real-world strings. Java 9's compact strings store the data as a byte[] plus a coder flag: LATIN1 (1 byte/char) when every character fits in Latin-1, or UTF16 (2 bytes/char) when it doesn't — cutting typical heap usage for strings roughly in half with no source-level API changes.

StringJava 9Memory
13

Can Strings be used in switch statements, and how does that work internally?

Optional📊 Mid

Yes, since Java 7. Internally the compiler doesn't add string comparison logic to the bytecode switch itself (JVM switch only works on int-like values) — instead javac rewrites a string switch into a switch on each case string's hashCode() (to pick a branch quickly), followed by an equals() call inside that branch to guard against hash collisions before actually matching.

StringSwitch
14

Why does String immutability matter for security and reliability?

Optional📊 Mid

Class names passed to reflective loading, file paths, DB connection URLs, and network hostnames are all typically Strings. If String were mutable, code could pass a value that passes a security check (e.g. "this path is inside the allowed directory"), then have a reference to that same object mutated afterward — the operation would then act on a different, unchecked value while believing it already validated it (a time-of-check to time-of-use style bug). Immutability removes this entire class of bug: once validated, a String's content is guaranteed to stay exactly what was checked.

StringSecurity
15

What is the difference between String and CharSequence?

Optional📊 Mid

CharSequence is an interface describing "a readable sequence of char values" — String, StringBuilder, StringBuffer, and CharBuffer all implement it. Coding against CharSequence in a method signature lets callers pass any of these without forcing a conversion to String first (which would allocate) — useful for APIs that only need to read characters, not use String-specific behavior like pooling or split().

StringCharSequence