字符串字面量拼接会被编译器直接折叠成单个常量,即纯字面量如"a"+"b"在编译期被替换为"ab"并存入字符串常量池,故string a="a"+"b"与string c="ab"指向同一对象(a==c为true),但含变量、方法调用或null则不折叠,须通过stringbuilder运行时拼接。

字符串字面量拼接会被编译器直接折叠成单个常量
Java编译器在编译期就能确定 "a" + "b" 这种纯字面量拼接的结果,所以不会生成运行时拼接逻辑,而是直接替换成 "ab"。这属于常量折叠(constant folding)的一部分,只适用于编译期可完全求值的表达式。
- 必须全是字符串字面量(
"a"、"b"、""),不能含变量、方法调用或null - 涉及
final字符串变量时,只有当该变量是编译期常量(即声明为static final String x = "a";)才参与折叠 - 一旦出现非字面量(比如
String s = "a"; s + "b"),就退化为运行时通过StringBuilder拼接
为什么 String a = "a" + "b" 和 String c = "ab" 指向同一对象
因为两者都被编译为对字符串常量池中同一个 "ab" 的引用。JVM 在类加载阶段就把所有编译期确定的字符串字面量(包括折叠后的)放入常量池,后续相同内容的字面量直接复用。
- 验证方式:用
a == c返回true,而a == (new String("a") + "b")是false - 注意:
==比较的是引用,不是内容;内容相等请用.equals() - 这个行为依赖于
javac的折叠策略和 JVM 的常量池规范,不同 JDK 版本表现一致,但不要依赖它做对象身份判断
哪些情况不会触发常量折叠
只要表达式无法在编译期静态求值,就不会折叠。常见“看似能折实则不行”的场景:
-
String x = "a"; String y = x + "b";——x是变量,即使值不变,编译器也不认为它是常量 -
final String z = someMethod(); String w = z + "c";——someMethod()调用无法在编译期执行 -
String u = null + "x";—— 编译报错:error: bad operand types for binary operator '+' -
String v = "a" + 1;—— 会折叠,因为1是字面量,且字符串与数字拼接是编译期定义的行为,结果是"a1"
反编译验证最可靠
看字节码比猜更准。用 javap -c 查看生成的指令,能清晰看到是否用了 ldc(加载常量池项)还是调用了 StringBuilder 相关方法。
立即学习“Java免费学习笔记(深入)”;
- 折叠后:只有
ldc #2(#2 指向常量池里的"ab") - 未折叠:会出现
new、dup、invokespecial、invokevirtual等构建和拼接指令 - 示例命令:
javac Test.java && javap -c Test,关注main方法的字节码输出










