Why Java Creates Only One String Object: Understanding Constant Folding
This article explains how Java's compiler optimizes string concatenation through constant folding, the rules that define compile‑time constants, and why only a single String object is created in many seemingly multiple‑object scenarios.
First, consider this common interview question: how many String objects are created by the following code? String s = "a" + "b" + "c"; When you compare the Java source code with the decompiled bytecode, you can see that only one String object is created. The reason is that during compilation the compiler applies an optimization called constant folding , which evaluates compile‑time constant expressions and replaces them with their results.
For a value to be a compile‑time constant, it must satisfy all of the following conditions:
Declared with the final keyword.
Of a primitive type or String.
Initialized at the point of declaration.
Initialized using a constant expression.
Consider the following examples:
final String s1 = "hello " + "Hydra";
final String s2 = UUID.randomUUID().toString() + "Hydra";The compiler can determine at compile time that s1 equals "hello Hydra", so s1 is a compile‑time constant. In contrast, s2 is not a compile‑time constant because its initializer is not a constant expression; it is a runtime constant.
Only the constant "hello Hydra" appears in the constant pool; the string for s2 does not.
Another difference between compile‑time and runtime constants is class initialization. The following code demonstrates that a final static variable can become a compile‑time constant, preventing class initialization:
public class IntTest1 {
public static void main(String[] args) {
System.out.println(a1.a);
}
}
class a1 {
static {
System.out.println("init class");
}
public static int a = 1;
}Running this prints:
init class
1If the variable a is declared final, the output changes to: 1 Because the final modifier makes a a compile‑time constant, the class is not initialized.
Further examples illustrate the effect of final on string literals:
final String h1 = "hello";
String h2 = "hello";
String s1 = h1 + "Hydra";
String s2 = h2 + "Hydra";
System.out.println(s1 == "helloHydra");
System.out.println(s2 == "helloHydra");The output is:
true
falseHere s1 is folded at compile time, while s2 is not, confirming that only final variables initialized with constant expressions become compile‑time constants.
Oracle’s documentation lists many forms of constant expressions, including literals, casts, unary operators (except ++ and --), arithmetic operators, and shift operators.
Java literals (e.g., 1L, 11.1f, 'h', "Hydra", true) are stored in the constant pool. When the same literal appears again, the JVM reuses the existing entry.
Another example shows different results for concatenated strings:
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");The output is:
false
trueBecause s3 is a compile‑time constant and is folded into the constant pool, while s2 is built at runtime using a StringBuilder, resulting in a distinct object.
When multiple string literals are concatenated, the compiler may generate a StringBuilder and invoke toString(), creating a new object that is not the same as the constant‑pool literal.
All the code examples were tested on Java 1.8.0_261-b12.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
