Java 中 String、StringBuffer 和 StringBuilder

Java 中 String 、StringBuffer 和 StringBuilder

这三个类都是 java.lang 包下的字符串处理类,但它们在设计理念、内部实现和适用场景上存在显著差异。

1. 全面对比表
比较维度 String StringBuffer StringBuilder
可变性 不可变(Immutable) 内容一旦创建无法修改 可变(Mutable) 支持原地修改 可变(Mutable) 支持原地修改
线程安全 天然线程安全(不可变对象) 线程安全 关键方法(如 append、insert)加了 synchronized 锁 非线程安全 无任何同步机制
性能 修改操作最慢(频繁创建新对象) 中等(有锁开销,但比 String 好) 最高(无锁,单线程下最优)
内部存储 底层是 final char[] value(JDK 7+) JDK 8 前是 char\[\],不可扩容 底层是 char[] value,可动态扩容 底层是 char[] value,可动态扩容(实现几乎相同)
容量扩容机制 无(固定长度) 默认容量 16,扩容时新容量 = (旧容量 * 2) + 2 同 StringBuffer:默认 16,扩容时新容量 = (旧容量 * 2) + 2
内存占用 每次修改产生新对象 + 新 char\[\],GC 压力大 共享同一 char\[\],内存高效 同 StringBuffer,内存高效
字符串常量池 支持常量池缓存(字面量可复用) 不支持常量池 不支持常量池
引入版本 JDK 1.0 JDK 1.0 JDK 1.5(为单线程优化而新增)
继承关系 继承 AbstractStringBuilder,实现 CharSequence、Serializable、Comparable 继承 AbstractStringBuilder,实现 Appendable 等 继承 AbstractStringBuilder,实现 Appendable 等
方法同步 无需同步 大部分修改方法(如 append、delete)都是 synchronized 无 synchronized
典型使用场景 常量字符串、键值存储、配置信息 多线程频繁拼接(如日志记录、共享缓冲) 单线程频繁拼接(如 JSON 构建、循环拼接)------现代项目首选
toString() 实现 返回自身(缓存优化 JDK 7+) 新建 String 对象(调用 Arrays.copyOf) 同 StringBuffer
2. 底层实现深入剖析
  • String 的不可变性:

    java 复制代码
    // JDK 源码简化版
    public final class String {
        private final char[] value;  // final 修饰,不可重新赋值
        private final int hash;      // 缓存 hashCode
    }

    任何"修改"操作(如 concat、replace)都会 new String() 并复制 char\[\],原对象不变。

  • StringBuffer / StringBuilder 的可变性 :

    两者都继承自 AbstractStringBuilder:

    java 复制代码
    abstract class AbstractStringBuilder {
        char[] value;      // 非 final,可扩容
        int count;         // 当前长度
    }
    • append 等操作直接操作 value 数组。
    • 扩容时:Arrays.copyOf 创建更大数组,复制旧内容。

    关键区别 :StringBuffer 的公共方法加了 synchronized:

    java 复制代码
    // StringBuffer 示例
    public synchronized StringBuffer append(String str) { ... }
    
    // StringBuilder 示例
    public StringBuilder append(String str) { ... }  // 无 synchronized
3. 性能对比实测(循环 10 万次拼接)
java 复制代码
public class Test {
    public static void main(String[] args) {
        int times = 100000;

        // String
        long start = System.nanoTime();
        String s = "";
        for (int i = 0; i < times; i++) {
            s += i;
        }
        System.out.println("String 用时: " + (System.nanoTime() - start) / 1e6 + " ms");

        // StringBuilder
        start = System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < times; i++) {
            sb.append(i);
        }
        System.out.println("StringBuilder 用时: " + (System.nanoTime() - start) / 1e6 + " ms");

        // StringBuffer
        start = System.nanoTime();
        StringBuffer sbf = new StringBuffer();
        for (int i = 0; i < times; i++) {
            sbf.append(i);
        }
        System.out.println("StringBuffer 用时: " + (System.nanoTime() - start) / 1e6 + " ms");
    }
}

典型运行结果(JDK 17,普通电脑):

  • String:约 3000~5000 ms(极慢,产生海量临时对象)
  • StringBuilder:约 10~20 ms
  • StringBuffer:约 15~30 ms(略慢于 Builder,因锁开销)

可见,频繁修改时 StringBuilder 性能遥遥领先。

4. 编译器优化小秘密
  • 单次 + 操作:JVM 会自动优化为 StringBuilder:

    java 复制代码
    String result = "a" + "b" + "c";  // 编译后相当于 new StringBuilder().append("a").append("b").append("c").toString()
  • 循环中 + 操作:不会跨循环优化,仍建议手动用 StringBuilder。

5. 选择指南(实战总结)
  1. 内容几乎不修改 → 用 String(最安全、支持常量池)。

  2. 多线程 + 频繁修改 → 用 StringBuffer(虽慢点但安全)。

  3. 单线程 + 频繁修改 → 必须用 StringBuilder(性能最佳,99% 场景适用)。

  4. 已知长度大 → 提前指定容量,避免扩容:

    java 复制代码
    StringBuilder sb = new StringBuilder(10000);  // 预分配
  5. 现代替代方案 :复杂场景可考虑 String.join()、String.format() 或流式操作,但核心拼接仍推荐 StringBuilder。

6. 常见误区澄清
  • 误区:StringBuffer 完全过时了 → 错!在多线程共享同一缓冲时仍有价值。
  • 误区:StringBuilder 线程不安全就不能用 → 大多数业务是单线程,安全使用即可。
  • 误区:String 完全不能用于拼接 → 小量拼接没问题,大量必须避免。
相关推荐
今年下半年6 分钟前
【防重放】Spring Boot 通过自定义注解实现接口防重放
java·spring boot·自定义注解·防重放
念越9 分钟前
题库、练习与错题本代码阅读
开发语言·数据结构
WeiXin_DZbishe10 分钟前
基于Node.js的马拉松赛事管理系统的设计与实现-计算机毕设 附源码【课程设计】90211
java·javascript·vue.js·python·php·课程设计
l1t11 分钟前
几种数据库软件count(distinct)和count()from group by子查询性能测试
开发语言·数据库
熊猫钓鱼>_>20 分钟前
大学图论第 6 讲:网络流与二分图匹配——对偶性让瓶颈自己现形
开发语言·算法·面试·职场和发展·图论·校招·面经
方便面不加香菜21 分钟前
C++ 关于多态的知识点讲解
开发语言·c++
Wang's Blog27 分钟前
Java 项目部署之 Docker工具快速入门: 使用 Docker 镜像命令练习
java·docker
企业数字化笔记35 分钟前
多租户系统从哪里开始设计?租户身份、数据隔离、权限与套餐的整体方案
java·spring boot·后端·junit
jimy141 分钟前
C++ 类成员函数的内联定义(inline)详解
开发语言·c++
tiger从容淡定是人生42 分钟前
从 Gemini 的任务示例出发:AI 能否从知识中推导工作流?
java·人工智能·aigc·知识图谱·创业创新·零知识证明