What Happens When Concatenating Strings with + Operator?
When you use + to join strings, Java creates a new string from two (or more) source strings. Since String is immutable, the original strings don't change.
π’ Junior Level
When you use + to join strings, Java creates a new string from two (or more) source strings. Since String is immutable, the original strings donβt change.
Example:
String s1 = "Hello";
String s2 = "World";
String result = s1 + " " + s2; // "Hello World"
Important point: If you concatenate strings in a loop via +, a new string is created on each iteration. This is very inefficient.
// BAD β creates N new String objects
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // new String on every iteration!
}
// GOOD β one StringBuilder object
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
Rule: + is convenient for simple expressions (1-2 strings), but forbidden in loops.
When + hurts readability
Complex expressions with nested method calls:
"User: " + user.getName() + " (" + user.getAge() + " years)" β better with StringBuilder or formatting.
π‘ Middle Level
How it works
Behavior depends on whether values are known at compile time:
Constant concatenation (Compile-time Constant Folding):
String s = "Hello" + " " + "World";
// Compiler turns this into:
String s = "Hello World"; // One string in bytecode, 0 operations at runtime
Variable concatenation (Runtime):
String a = getName();
String b = getLastName();
String full = a + " " + b;
What happens βunder the hoodβ depends on the Java version (see Senior section).
Typical mistakes
-
Mistake:
+in a loop Solution:StringBuilderoutside the loop -
Mistake:
split(".")β dot is a regex metacharacter Solution:split("\\.")orsplit(Pattern.quote(".")) -
Mistake:
null+ string Solution: Result will be"nullWorld". This is not an error, but can be surprising.
Approach comparison
| Scenario | Approach | Efficiency |
| ββββββββββββ- | βββββββββββ- | βββββββββββ |
| "A" + "B" + "C" | + | Great (constant folding) |
| a + b + c (variables, 1 line) | + | Great (optimized) |
| Loop with += | StringBuilder | + β O(nΒ²), StringBuilder β O(n) |
| Formatting | String.format() or StringBuilder| Depends on context |
π΄ Senior Level
Internal Implementation β Evolution
Java 5-8: StringBuilder codegen
The compiler replaced a + b + c with:
new StringBuilder().append(a).append(b).append(c).toString()
Problem: bytecode is βhardcodedβ in .class. A new optimization strategy would require recompilation.
JEP 280 (Java 9) introduced invokedynamic for concatenation. By default, Java 9 uses BC_SB, and in Java 11+ the strategy can be changed via StringConcatFactory.
String full = a + " " + b;
Bytecode:
0: aload_1 // a
1: aload_2 // " "
2: aload_3 // b
3: invokedynamic #7 // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
8: astore 4
invokedynamic references StringConcatFactory. On first call, the bootstrap method generates optimal code for this specific concatenation.
StringConcatFactory strategies:
- MH_LF (MethodHandle with LambdaForm): Generates a MethodHandle for concatenation
- BC_SB (Bytecode StringBuilder): Generates bytecode with StringBuilder
- BH_SB (Bootstrap method with StringBuilder β compact): Optimized variant
Architectural Trade-offs
invokedynamic pros:
- JVM chooses strategy at runtime (adaptivity)
- New strategies can be added without recompilation
- Fewer allocations β JVM knows all arguments and allocates
byte[]of the right size immediately
Cons:
- Bootstrap overhead on first call (~1-5ΞΌs)
- Harder to debug bytecode
Edge Cases
-
null concatenation:
"val: " + nullβ"val: null".StringConcatFactoryandStringBuildercallString.valueOf(null)β"null". -
Mixed types:
"Value: " + 42βStringConcatFactoryknows theinttype and uses optimal conversion without creating an intermediateString. -
Loops:
invokedynamicoptimizes only one expression. A loop still creates a new concatenation context on each iteration:for (int i = 0; i < n; i++) s += i; // Each iteration: invokedynamic β new byte[] β new String // Total: O(nΒ²) allocations
Performance
| Scenario | Java 8 (StringBuilder) | Java 9+ (invokedynamic) | | βββββ- | βββββββ- | ββββββββ | | 3 variables | ~30ns | ~15ns | | 10 variables | ~80ns | ~30ns | | 3 variables + int| ~35ns | ~18ns | | Loop 10K iters | O(nΒ²) | O(nΒ²) (not optimized!) |
Production Experience
Scenario: Logging log.info("User " + user.getId() + " action " + action) β 100K calls/sec:
- Java 8: 100K
new StringBuilder()β ~30ms CPU - Java 9+: invokedynamic with direct allocation β ~15ms CPU
- If log is filtered (level < INFO): both variants waste CPU. Solution:
if (log.isInfoEnabled())
Best Practices for Highload
- For simple concatenations (1 line of code):
+β readable and efficient - In loops: always
StringBuilderwithinitialCapacity - For formatting:
String.format()is convenient but slower (pattern parsing). Alternatives:MessageFormat, text blocks (Java 15+), or manual StringBuilder - Lazy evaluation: if concatenation is expensive and may not be needed β defer it
π― Interview Cheat Sheet
Must know:
+operator creates a NEW string β String is immutable, originals donβt change- Constant folding:
"A" + "B" + "C"β"ABC"at compile time, 0 operations at runtime - Java 5-8: compiler replaces
a + b + cwithnew StringBuilder().append(a).append(b).append(c).toString() - Java 9+:
invokedynamic+StringConcatFactoryβ JVM chooses optimal strategy at runtime - In loops
+creates O(nΒ²) allocations β useStringBuilder null + "text"β"nulltext"β not an error, but can be surprising
Frequent follow-up questions:
- Why is
+in a loop bad? β Each iteration creates a new StringBuilder + new String. For 1000 iterations: 1000 StringBuilder + 1000 String = O(nΒ²). - What is
invokedynamicfor concatenation? β In Java 9+ the compiler generatesinvokedynamic makeConcatWithConstants. On first call, JVM chooses optimal strategy (MH_LF, BC_SB, BH_SB). - Does the compiler optimize loops with
+? β No.invokedynamicoptimizes only a single expression, not a loop. - What is StringConcatFactory? β A factory in
java.lang.invokethat generates optimal code for a specific concatenation.
Red flags (DONβT say):
- β β
+in a loop β is fineβ β O(nΒ²) allocations, useStringBuilder - β βCompiler optimizes
+in loopsβ β optimizes only a single expression, not a loop - β β
invokedynamicis slower thanStringBuilderβ β for a single expression β faster, for a loop β equally bad - β βConcatenation via
+modifies original stringsβ β String is immutable, always creates a new one
Related topics:
- [[8. How Java Compiler Optimizes String Concatenation]]
- [[5. When to Use StringBuilder vs StringBuffer]]
- [[4. Why String is Immutable]]