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
Stringobjects are created by the following code?
<code>String s = "a" + "b" + "c";</code>When you compare the Java source code with the decompiled bytecode, you can see that only one
Stringobject 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
finalkeyword.
Of a primitive type or
String.
Initialized at the point of declaration.
Initialized using a constant expression.
Consider the following examples:
<code>final String s1 = "hello " + "Hydra";
final String s2 = UUID.randomUUID().toString() + "Hydra";</code>The compiler can determine at compile time that
s1equals
"hello Hydra", so
s1is a compile‑time constant. In contrast,
s2is 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
s2does not.
Another difference between compile‑time and runtime constants is class initialization. The following code demonstrates that a
finalstatic variable can become a compile‑time constant, preventing class initialization:
<code>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;
}</code>Running this prints:
<code>init class
1</code>If the variable
ais declared
final, the output changes to:
<code>1</code>Because the
finalmodifier makes
aa compile‑time constant, the class is not initialized.
Further examples illustrate the effect of
finalon string literals:
<code>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");</code>The output is:
<code>true
false</code>Here
s1is folded at compile time, while
s2is not, confirming that only
finalvariables 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:
<code>String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");</code>The output is:
<code>false
true</code>Because
s3is a compile‑time constant and is folded into the constant pool, while
s2is built at runtime using a
StringBuilder, resulting in a distinct object.
When multiple string literals are concatenated, the compiler may generate a
StringBuilderand 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.
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.