Back to String & String Pool
Q4Question 4 of 12

How does String concatenation work internally?

Key Concepts
  • String + in a simple expression: compiled to invokedynamic + StringConcatFactory (Java 9+)
  • String += in a loop: creates a new String each iteration – use StringBuilder instead
  • The compiler does NOT optimize += in loops – each iteration allocates a new String
  • StringBuilder.append() modifies in place without creating new objects
  • For joining collections: use String.join() or Collectors.joining()

Definition

Simple + expressions are optimized by the compiler (Java 9+ invokedynamic). But += in loops creates a new String every iteration – use StringBuilder for loops.

Two cases:

  • Simple expression: "Hello " + name + "!" is compiled to efficient bytecode using invokedynamic (Java 9+). No manual StringBuilder needed.
  • Loop concatenation: s += "x" inside a loop creates a new String and a new StringBuilder each iteration. Extremely wasteful. Use one StringBuilder outside the loop.

Simple concatenation is like writing one line on a form – efficient. Loop concatenation with += is like printing a new form every time you add one word – wasteful. StringBuilder is a whiteboard where you keep writing without reprinting.

Understanding the Concepts

Simple expression (compiler optimizes)

JAVA
String greeting = "Hello " + name + ", balance: " + balance;
// Java 9+: compiled to invokedynamic → StringConcatFactory
// Efficient – no manual StringBuilder needed for simple expressions

Loop (compiler does NOT optimize)

JAVA
// BAD: N new Strings + N new StringBuilders
String csv = "";
for (Account a : accounts) {
    csv += a.getAccountId() + ",";  // new StringBuilder + new String each time!
}

// GOOD: one StringBuilder, append in place
StringBuilder sb = new StringBuilder();
for (Account a : accounts) {
    sb.append(a.getAccountId()).append(",");
}
String csv = sb.toString();

// BEST: use String.join or streams
String csv = accounts.stream()
    .map(Account::getAccountId)
    .collect(Collectors.joining(","));

// Or simply:
String csv = String.join(",", accountIds);

What should a senior engineer know?

  • Java 9+ invokedynamic makes simple + as fast as hand-written StringBuilder in most cases.
  • JMH benchmarking shows += in a 10,000-iteration loop is ~100x slower than StringBuilder.
  • String.format() is slow. Prefer + or StringBuilder for performance-critical code.
  • Text blocks (Java 15+): """SELECT ...""" are compile-time constants, no concatenation overhead.

What are common mistakes?

A frequent mistake is using == to compare String content instead of equals().

Wrong approach:

JAVA
package com.bank.service;

public class AccountValidator {
    public boolean validateAccountType(String type) {
        // WRONG: == compares references, not content
        if (type == "SAVINGS") {
            return true;
        }
        return false;
    }
}

Correct approach:

JAVA
package com.bank.service;

public class AccountValidator {
    public boolean validateAccountType(String type) {
        // CORRECT: equals() compares content
        if ("SAVINGS".equals(type)) {  // null-safe: literal first
            return true;
        }
        return false;
    }
}

Using the literal on the left side of equals() prevents NullPointerException when the variable is null. This is a standard practice at National Bank of India for all string comparisons in service code.

What are the edge cases?

String behavior has several surprising edge cases that can catch banking developers off guard.

JAVA
package com.bank.edge;

public class StringEdgeCases {
    public static void main(String[] args) {
        // Edge case 1: compile-time constant folding
        String a = "SAV" + "2001";       // Compile-time constant
        String b = "SAV2001";
        System.out.println(a == b);       // true (same pool entry)

        // Edge case 2: variable concatenation is NOT pooled
        String prefix = "SAV";
        String c = prefix + "2001";       // Runtime concatenation
        System.out.println(b == c);       // false (different objects)

        // Edge case 3: final variable IS a compile-time constant
        final String finalPrefix = "SAV";
        String d = finalPrefix + "2001";  // Compile-time constant
        System.out.println(b == d);       // true (same pool entry)

        // Edge case 4: substring no longer shares char array (Java 7u6+)
        String full = "AccountNumber:SAV2001";
        String sub = full.substring(14); // New String, new char array
    }
}
TEXT
true
false
true

The final keyword makes the variable a compile-time constant, which changes how the compiler handles concatenation.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  Simple: "Hello " + name    → invokedynamic (Java 9+) → efficient
  Loop:   s += "x" × 10000  → 10000 new Strings → SLOW
  Fix:    StringBuilder.append() × 10000 → 1 object → FAST
  Best:   String.join() or Collectors.joining()

Key Takeaway

Simple String + expressions are optimized by the compiler since Java 9. But += in loops creates a new String every iteration. Use StringBuilder for loops, String.join() for collections.

One sentence to remember: simple + is optimized, loop += is not – use StringBuilder or String.join for repeated concatenation.

---

Share
Use ← → arrow keys to navigate