StringBuffer 与 StringBuilder 的源码分析与差异

StringBuffer 与 StringBuilder 的源码分析与差异

在 Java 开发中,StringBufferStringBuilder 是两个常用的字符串操作类,尤其在需要频繁拼接字符串时,它们比直接使用 String 更高效。本文将从源码角度深入分析两者的差异,探讨它们的实现原理及 API,并总结适用场景。

1. 基本概述

  • StringBuffer:线程安全的可变字符串类,诞生于 JDK 1.0,主要用于多线程环境下。
  • StringBuilder:非线程安全的可变字符串类,引入于 JDK 1.5,适用于单线程场景,性能更高。

两者的核心功能都是通过字符数组实现字符串的动态拼接,但线程安全性和性能是它们的主要区别。

2. 源码分析

2.1 继承关系与基本结构

StringBufferStringBuilder 都继承自抽象类 AbstractStringBuilder,后者封装了字符数组操作的核心逻辑:

java 复制代码
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value; // 存储字符的数组
    int count;    // 当前字符数
    // ...
}
  • value 是底层的字符数组,初始容量默认 16(无参构造)。
  • count 表示当前已使用的字符数。

StringBufferStringBuilder 只是覆写了部分方法,添加了线程安全相关的控制。

2.2 构造方法

两者的构造方法类似,默认容量为 16,源码如下:

java 复制代码
// StringBuffer
public StringBuffer() {
    super(16);
}

// StringBuilder
public StringBuilder() {
    super(16);
}

如果指定初始字符串,会在字符串长度基础上再加 16:

java 复制代码
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

2.3 核心方法:append

append 是字符串拼接的核心方法,源码在 AbstractStringBuilder 中实现:

java 复制代码
public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len); // 确保容量足够
    str.getChars(0, len, value, count);  // 复制字符
    count += len;
    return this;
}
  • 容量扩展 :如果当前数组容量不足,会调用 ensureCapacityInternal 进行扩容,扩容策略为 (value.length << 1) + 2,即当前容量翻倍加 2。
  • 字符复制 :通过 getChars 将新字符串追加到 value 数组。
StringBuffer 的线程安全

StringBufferappend 方法加了 synchronized 关键字:

java 复制代码
public synchronized StringBuffer append(String str) {
    toStringCache = null; // 清除缓存
    super.append(str);
    return this;
}

每次操作都会加锁,确保多线程环境下数据一致性。

StringBuilder 的非线程安全

StringBuilderappend 直接调用父类实现,没有同步:

java 复制代码
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

少了锁的开销,因此性能更高。

2.4 toString 方法

  • StringBuffer :维护一个 toStringCache 字段缓存上次的 toString 结果,只有在修改后才会重建:
java 复制代码
public synchronized String toString() {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache);
}
  • StringBuilder:直接构造新字符串,无缓存:
java 复制代码
public String toString() {
    return new String(value, 0, count);
}

StringBuffer 的缓存设计是为了减少多线程环境下重复构造的开销,但实际性能提升有限。

3. API 对比

两者的 API 几乎一致,主要包括:

  • append:追加字符串、字符等,支持多种重载。
  • insert:在指定位置插入数据。
  • delete:删除指定范围的字符。
  • replace:替换指定范围的字符。
  • reverse:反转字符串。
  • substring:提取子字符串。

区别在于:StringBuffer 的所有公共方法几乎都加了 synchronized,而 StringBuilder 没有。

4. 性能与适用场景

  • 线程安全
    • StringBuffer:适合多线程环境,但因同步锁开销较大,性能较低。
    • StringBuilder:非线程安全,单线程环境下效率更高。
  • 性能测试 (示例): 在单线程下,循环 10 万次拼接字符串:
    • StringBuilder:约 10ms
    • StringBuffer:约 15ms 差距主要来自锁的开销。

5. 总结

  • 选择依据
    • 多线程环境或遗留代码:用 StringBuffer
    • 单线程或性能敏感场景:用 StringBuilder
  • 源码差异
    • StringBuffer 通过 synchronized 实现线程安全,增加了性能开销。
    • StringBuilder 去掉同步,逻辑更轻量。
  • 建议 :现代开发中,单线程场景占主导,StringBuilder 是首选。若需线程安全,可结合 Collections.synchronizedListLock 实现更灵活的控制。
相关推荐
uzong3 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程4 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研4 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi4 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack6 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9657 小时前
pip install 已经不再安全
后端
寻月隐君7 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github