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 实现更灵活的控制。
相关推荐
橘猫云计算机设计2 小时前
基于Springboot的自习室预约系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·毕业设计
秋书一叶2 小时前
SpringBoot项目打包为window安装包
java·spring boot·后端
pwzs3 小时前
Spring MVC 执行流程全解析:从请求到响应的七步走
java·后端·spring·spring mvc
小兵张健3 小时前
互联网必备职场知识(4)—— 共情沟通能力
后端·产品经理·运营
AskHarries4 小时前
使用 acme.sh 自动更新 SSL 证书的指南
后端
Chandler244 小时前
Go:反射
开发语言·后端·golang
pwzs4 小时前
深入浅出 MVCC:MySQL 并发背后的多版本世界
数据库·后端·mysql
盒子69104 小时前
go for 闭环问题【踩坑记录】
开发语言·后端·golang
刘大猫265 小时前
Arthas monitor(方法执行监控)
人工智能·后端·监控
追逐时光者6 小时前
MongoDB从入门到实战之MongoDB简介
后端·mongodb