stringbuilder性能优势源于可变字符数组和智能扩容策略:底层char[]可原地修改,扩容公式为old×2+2,非线程安全使其单线程拼接高效,但多线程需手动同步或改用stringbuffer。

StringBuilder 的性能优势,根植于它可变的底层结构和智能的扩容策略,而不是靠“魔法”。它不创建新对象,只在原数组上操作;容量不够时,也不是简单翻倍,而是有一套兼顾效率与内存的计算逻辑。
底层是可变字符数组,不是复制再建
String 用 final char[] 存字符,一旦初始化就不能改引用,所有操作都返回新对象。StringBuilder 则不同,它继承自 AbstractStringBuilder,内部是普通 char[] value(JDK 9+ 改为 byte[] + 编码标识),数组本身可被直接写入、覆盖、扩展。配合 int count 记录当前有效长度,length() 返回的就是 count,capacity() 返回的是 value.length —— 两者分离,支撑了真正的“原地修改”。
初始容量默认16,但别硬背,要看构造方式
不同构造方式决定起始数组大小:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- StringBuilder():初始容量为 16
- StringBuilder(int capacity):直接按传入值分配数组
- StringBuilder(String str):容量 = str.length() + 16(预留空间,避免刚拼接就扩容)
扩容不是简单×2,而是“旧×2+2”再兜底
当 append 等操作导致 count + 新增长度 > value.length 时,触发扩容。核心逻辑分三步:
- 先算最小需容量:minimumCapacity = count + 新增字符数
- 再算新容量:newCapacity = oldCapacity × 2 + 2;如果这个值仍小于 minimumCapacity,就直接取 minimumCapacity
- 最后校验上限:不能超过 Integer.MAX_VALUE - 8,超限抛 OutOfMemoryError
扩容本质是 Arrays.copyOf(value, newCapacity) —— 一次数组复制,开销虽有,但远小于频繁新建 String 对象带来的 GC 压力。
非线程安全换来性能,用错场景反而拖后腿
StringBuilder 没加任何同步机制,所有方法都是普通实例方法。这意味着:
- 单线程高频拼接(如日志组装、JSON生成)—— 它是首选
- 多线程共享同一个 StringBuilder 实例 —— 必须手动加锁,否则结果不可预测;此时不如换用 StringBuffer,或每个线程独享一个 StringBuilder
- 仅拼接少量固定字符串(如 "a" + "b" + "c")—— 编译器会优化成常量,用 StringBuilder 反而多此一举
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










