
字符串不可变性如何让 + 连接在循环中变慢
Java 中 String 是不可变的,每次用 + 拼接都会新建一个对象。在 for 循环里反复拼接,比如 str += "a",实际会生成大量中间 String 对象,触发频繁 GC,性能断崖式下降。
- 编译器对静态字符串(如
"a" + "b" + "c")会优化为常量池中的单个字面量,不产生运行时开销 - 但含变量的拼接(如
s + "x")会被编译为new StringBuilder().append(s).append("x").toString()—— 每次都新建StringBuilder - 循环中用
+=等价于每次新建StringBuilder→append→toString(),O(n²) 时间复杂度
什么时候该用 StringBuilder 而不是 +
判断依据不是“字符串多长”,而是“拼接动作是否发生在运行时循环或多次分支中”。即使只拼 3 个变量,只要逻辑路径导致拼接被重复执行,就该换 StringBuilder。
- 循环内拼接:必须用
StringBuilder,且建议预设容量(new StringBuilder(1024)),避免内部数组扩容 - 方法参数拼接后仅返回一次:如
return prefix + value + suffix,JVM 通常已优化为StringBuilder,可不改 - 日志拼接(如
log.debug("id=" + id + ", name=" + name)):若日志未开启,字符串仍会构造 —— 应改用占位符(log.debug("id={}, name={}", id, name))或延迟计算
String.concat() 和 + 的底层差异
String.concat() 是 String 类原生方法,直接操作字符数组,不经过 StringBuilder;而 + 编译后几乎总是走 StringBuilder 路径(Java 9+ 对两字符串拼接有少量优化,但仍非直接数组拷贝)。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
-
"a".concat("b"):分配一个新数组,复制两次源数组内容,无额外对象开销 -
"a" + "b":编译期优化为字面量,无运行时行为;但s1 + s2(变量)编译为new StringBuilder().append(s1).append(s2).toString() -
concat()只支持单个字符串参数,无法链式调用,灵活性差,日常少用
Java 9+ 的 String 内部表示变化对连接的影响
Java 9 把 String 的底层从 char[] 改为 byte[] + coder(标识 Latin-1 或 UTF-16),节省内存。但这不影响拼接逻辑,只影响 concat() 和 StringBuilder.toString() 的内部拷贝效率。
立即学习“Java免费学习笔记(深入)”;
- 纯 ASCII 字符串拼接更快(Latin-1 编码下每个字符占 1 字节,拷贝量减半)
- 含中文等字符时自动切到 UTF-16 模式,与 Java 8 行为一致
- 不要因为这个改动去刻意“拆分拼接”来适配编码 —— JVM 已透明处理,手动干预反而易出错

















