字面量拼接在编译期完成,字节码仅含ldc指令加载常量池字符串;变量拼接需运行时执行,字节码显式包含new stringbuilder、append和tostring三步操作。

Java 字符串字面量拼接和变量拼接在字节码层有本质区别:前者完全由编译器在 javac 阶段折叠,不生成任何运行时对象创建指令;后者必须在运行时执行,字节码中明确出现 StringBuilder 构造、append 调用和 toString 调用 三步操作。
字面量拼接:编译期合并,无 StringBuilder 指令
所有操作数都是字符串字面量(如 "a" + "b" + "c")或被 final 修饰且初始化为字面量 的变量(如 final String x = "a"; x + "b"),编译器直接计算结果并写入常量池。
- 字节码中只有一条 ldc 指令,加载合并后的完整字符串(如 ldc "abc")
- 不会出现 new、invokespecial(构造 StringBuilder)、invokevirtual(append/toString)等任何相关指令
- 生成的 String 对象来自字符串常量池,多次相同拼接复用同一地址,== 比较返回 true
变量拼接:运行时构建,字节码显式调用 StringBuilder
只要任意一个操作数是普通局部变量、方法参数、返回值或未用 final 修饰的变量(哪怕值实际不变),编译器就无法在编译期确定结果,必须推迟到运行时处理。
- 字节码中包含:new StringBuilder → dup → invokespecial StringBuilder.
- 对每个拼接项依次调用 invokevirtual StringBuilder.append
- 最后调用 invokevirtual StringBuilder.toString 返回新 String 对象
- 每次执行该表达式都会新建 StringBuilder 和新的 String 实例,对象位于堆内存,不进常量池
关键识别点:看字节码有没有 ldc 直接加载完整字符串
反编译 .class 文件后,最快速判断方式就是搜索 ldc 指令:
- 如果看到 ldc "hello world" 这类单条指令 → 是字面量拼接
- 如果看到 new、append、toString 等一连串指令 → 是含变量拼接
- 注意:final String s = "a"; s + "b" 在 JDK8+ 中仍会被优化为 ldc,因为 final 字面量是编译期常量
一个典型对比示例
以下两行 Java 代码看似相似,字节码却截然不同:
- String s = "a" + "b"; → 编译后等价于 String s = "ab";,字节码仅 ldc "ab"
- String t = s1 + "b";(s1 是普通 String 变量)→ 字节码含 new StringBuilder、append(s1)、append("b")、toString()
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











