编译期常量折叠将纯字面量或final常量拼接直接合并为单个字符串常量,如"a"+"b"+"c"→"abc",字节码仅含ldc指令,无stringbuilder;含变量或运行时调用则退至运行时stringbuilder拼接。

这种写法在编译期会被直接合并成一个字符串常量 "abc",不生成任何运行时拼接逻辑。
编译器做的是常量折叠,不是运行时计算
Java 编译器(javac)识别出所有操作数都是字符串字面量,属于编译期可确定的常量,于是跳过 StringBuilder 创建、append、toString 等全部步骤,在字节码中只保留一条 ldc "abc" 指令——即直接从常量池加载结果字符串。
- 最终 class 文件里找不到
"a"、"b"、"c"这三个独立字符串的痕迹 - 也不会出现
StringBuilder相关的字节码(如new、invokespecial、invokevirtual append) - 该字符串自动进入字符串常量池,后续相同字面量会复用同一地址
哪些情况也适用这个优化
只要所有参与拼接的值都是编译期常量,就同样触发折叠:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
final String x = "hello"; final String y = "world"; String s = x + y;→ 折叠为"helloworld" -
"a" + 123 + true→ 折叠为"a123true"(数字和布尔字面量也参与) -
final char c = 'x'; String s = "pre" + c + "post";→ 折叠为"prexpost"
为什么不能有变量或方法调用
一旦出现任何运行时才能确定的值,编译器就无法提前算出结果,必须退到运行时处理:
-
String a = "a"; String s = a + "b";→a是普通变量,编译后转为new StringBuilder().append(a).append("b").toString() -
final String s = System.getProperty("os.name") + "test";→System.getProperty是运行时调用,无法折叠 -
final String s = new Date().toString() + "log";→ 含对象创建,同样不可折叠
这个优化带来的实际影响
不只是性能提升,还会改变语义行为:
-
"a" + "b" == "ab"返回true(指向常量池同一对象) - 如果某个
public static final String VERSION = "1.0"被其他类引用,修改后不重编译调用方,它仍用旧值(已被内联进字节码) - 反射读取该字段拿到的是编译时快照,而非当前运行时可能被绕过修改的值
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










