钩子:一段把程序拖慢的代码
先做个实验,循环拼接字符串一万次:
String s = "";
for (int i = 0; i < 10000; i++) {
s = s + "a"; // 拼接
}
这段代码能跑,但和用 StringBuilder 的版本比,实测慢了近百倍(拼接次数越多差距越恐怖,10 万次时就是几百倍)。问题出在哪?
根源一句话:String 是不可变的(immutable)。
核心:String 不可变是什么意思
String s = "橙码";
s = s + "酱"; // 你以为 s "变了"?
你以为的:s 这个字符串长出了"酱"这个字。
实际发生的:
"橙码"这个对象从头到尾没变过;s + "酱"创建了一个全新的字符串对象"橙码酱";- s 只是改了指向(引用换了个对象),旧对象原地不动。

所以循环拼接一万次 = 创建一万个临时字符串对象,大部分转眼就成了垃圾。这就是慢 200 倍的账单。
解药:StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append("a"); // 始终在同一个对象里追加,不建新对象
}
String result = sb.toString();
StringBuilder 内部维护一个可扩容的字符数组,append 是往自己身上追加,对象还是那个对象。第 16 篇讲过的 return this 链式调用,就是它——现在你知道为什么它能一直 .append 下去了。
使用口诀:
- 循环里拼接字符串 → 必须用 StringBuilder(性能红线);
- 就拼两三下 →
+随便用,编译器会优化,可读性优先。
为什么要把 String 设计成不可变?
面试高频,两个理由够用:
- 安全:如果 String 可变,
s 传给某个方法后被内部偷偷改了内容——文件路径、数据库连接串、用户名全都可能被篡改。不可变 = 传谁都不怕; - 省内存 + 当锁用:字符串常量池(相同字面量共用一个对象)只在不可变的前提下成立;HashMap 的 key 大量用 String,key 若可变,存进去就找不到了——这条下下篇讲 HashMap 时会呼应。
本文示例代码均已本地实测通过,可直接运行。
对应文件:StringDemo.java
橙码酱,分享更有用的技术知识,助你成为更优秀的开发者。