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 实现更灵活的控制。
相关推荐
java1234_小锋1 分钟前
Spring Bean有哪几种配置方式?
java·后端·spring
柯南二号1 小时前
【后端】SpringBoot用CORS解决无法跨域访问的问题
java·spring boot·后端
每天一个秃顶小技巧2 小时前
02.Golang 切片(slice)源码分析(一、定义与基础操作实现)
开发语言·后端·python·golang
gCode Teacher 格码致知3 小时前
《Asp.net Mvc 网站开发》复习试题
后端·asp.net·mvc
Moshow郑锴5 小时前
Spring Boot 3 + Undertow 服务器优化配置
服务器·spring boot·后端
Chandler246 小时前
Go语言即时通讯系统 开发日志day1
开发语言·后端·golang
有梦想的攻城狮6 小时前
spring中的@Lazy注解详解
java·后端·spring
野犬寒鸦7 小时前
Linux常用命令详解(下):打包压缩、文本编辑与查找命令
linux·运维·服务器·数据库·后端·github
huohuopro7 小时前
thinkphp模板文件缺失没有报错/thinkphp无法正常访问控制器
后端·thinkphp
cainiao08060510 小时前
《Spring Boot 4.0新特性深度解析》
java·spring boot·后端