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 实现更灵活的控制。
相关推荐
Asthenia04127 分钟前
深入浅出分析JDK动态代理与CGLIB动态代理的区别
后端
追逐时光者39 分钟前
C#/.NET/.NET Core技术前沿周刊 | 第 32 期(2025年3.24-3.31)
后端·.net
uhakadotcom40 分钟前
轻松掌握XXL-JOB:分布式任务调度的利器
后端·面试·github
小杨40441 分钟前
springboot框架项目实践应用十三(springcloud alibaba整合sentinel)
spring boot·后端·spring cloud
程序员一诺1 小时前
【Python使用】嘿马python数据分析教程第1篇:Excel的使用,一. Excel的基本使用,二. 会员分析【附代码文档】
后端·python
神奇侠20241 小时前
快速入手-基于Django-rest-framework的serializers序列化器(二)
后端·python·django
Asthenia04121 小时前
基于Segment-Mybatis的:分布式系统中主键自增拦截器的逻辑分析与实现
后端
Asthenia04121 小时前
Seata:为微服务项目的XID传播设计全局的RequestInterceptor-将XID传播与具体FeignClient行为解耦
后端
无奈何杨1 小时前
Docker/Compose常用命令整理总结
后端
搬砖的阿wei2 小时前
从零开始学 Flask:构建你的第一个 Web 应用
前端·后端·python·flask