skip to content

What is compile-time constant folding of string concatenation, and when does it apply?

level: middleimportance: should knowfreq 45%

answer

  1. All-constant operands -> compiler joins at compile time
  2. Folded result is a literal -> interned in the constant pool
  3. "ab" == "a" + "b" is true
  4. final var counts only if its initializer is a compile-time constant
  5. Any runtime value breaks folding -> new, non-interned String

basics

~20 s

If you join strings that are all known at compile time (literals or final constants), the compiler joins them itself and stores the single finished string. No work happens at runtime, and the result is a pooled, interned string literal.

solid answer

~50 s

When every operand of a string concatenation is a constant expression — string literals or compile-time constant variables (typically static final of a constant type) — the Java compiler evaluates the whole thing at compile time and folds it into one literal. So "Hello, " + "World" becomes the single literal "Hello, World" in the class file, with no StringBuilder or invokedynamic emitted. Because the folded value is a literal, it is interned in the string constant pool: "ab" == "a" + "b" is true. The crucial caveat is that this only works for constant expressions: a final variable counts only if its initializer is itself a compile-time constant. A non-final variable, a method call, or a value computed at runtime breaks folding, so "a" + someVar is compiled as real runtime concatenation and is not interned.

go deeper

for a junior

Recognizes that joining two literals produces a fixed string and may know == sometimes works for them.

for a middle

Defines constant folding, knows it requires all-constant operands, and explains the interning/== result.

for a senior

Articulates the final-variable initializer subtlety and why == is unreliable for content, mapping folding to constant pool behavior.

for a principal

Reasons about interning's memory and identity implications across a codebase and sets the equals()-not-== convention with the folding rationale.

## What "constant expression" means A **compile-time constant expression** is something whose value the compiler can compute while compiling, before the program ever runs. For strings that means: string **literals** (`"abc"`) and **constant variables** — a variable declared `final` whose initializer is itself a constant (e.g. `static final String SEP = "-";`). Primitives like `final int N = 3` also qualify. ## What folding does When *all* operands of a `+` concatenation are constant expressions, the compiler does the join **itself** and bakes the finished text into the `.class` file as a single string literal. So: ```java String s = "Hello, " + "World"; // compiled exactly as if you wrote "Hello, World" ``` No `StringBuilder`, no `invokedynamic`, no runtime cost. This is **constant folding**. ## Interning: the == surprise String literals live in the **string constant pool** — a shared table where identical literals are stored once ("interned"). Because a folded concatenation *is* a literal, it is pooled too. Therefore: ```java "ab" == "a" + "b" // true — both are the same pooled literal ``` This is a popular interview gotcha. Contrast with runtime concatenation, which produces a fresh, non-pooled `String`: ```java String a = "a"; // a is a variable, not a constant expression String r = a + "b"; // computed at runtime -> new object r == "ab"; // false ``` ## What breaks folding Folding requires **every** part to be constant. It is disabled if any operand is: - a **non-final** variable, - a `final` variable whose initializer is *not* a compile-time constant (e.g. `final String x = getName();`), - a method call or any runtime-computed value, - a value of a type that cannot be a constant expression. In those cases the compiler emits ordinary runtime concatenation (StringBuilder pre-9 / invokedynamic 9+), and the result is **not** interned. ## The final-variable subtlety ```java static final String A = "a"; // constant variable static final String B = "b"; // constant variable "ab" == A + B; // true: both A,B are constant exprs -> folded & interned ``` but ```java static final String C = someMethod();// final, but initializer is NOT a constant "xb" == C + "b"; // false: C is not a constant expression ``` ## Why it matters Folding makes literal-built constants free at runtime and explains otherwise-baffling `==` results. The practical rule: never use `==` to compare string *contents* — use `equals()` — because whether two equal-looking strings are the same object depends on folding/interning, not on their characters.

  • Is "a" + "b" == "ab" true or false, and why?
    True. Both operands are literals, so the compiler folds the concatenation into the single literal "ab", which is interned in the constant pool, making it the same object as the other "ab".
  • Does a final variable always make concatenation fold?
    No. Only a final variable whose initializer is itself a compile-time constant counts as a constant expression. final String x = getName() is not constant, so x + "y" is computed at runtime and not interned.

saying these in an interview costs you the question

  • Assuming any final String variable triggers folding regardless of initializer
  • Thinking a + "b" (a a non-final variable) is folded and interned
  • Using == for content comparison and trusting interning to make it work
  • Believing folding happens at runtime

context