Java StringBuffer 和 StringBuilder 类详解

在 Java 中,StringBufferStringBuilder是用于处理可变字符串的两个重要类。它们弥补了String类不可变的局限性,提供了高效的字符串操作能力。本文将深入解析这两个类的核心特性、性能差异及应用场景。

一、核心特性对比

1. 继承结构与接口实现

  • StringBuffer
    public final class StringBuffer
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence { ... }
    
     
  • StringBuilder
    public final class StringBuilder
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence { ... }
    
     

    • 相同点:均继承AbstractStringBuilder,实现SerializableCharSequence接口。
    • 不同点StringBuffer所有公开方法均被synchronized修饰,线程安全;StringBuilder非线程安全。

2. 内部实现

  • 可变字符数组:两者均使用char[]存储字符序列,但StringBuilder的方法无同步开销。
  • 自动扩容:当容量不足时,内部数组会自动扩容(默认扩容为原容量的2倍 + 2)。

二、常用方法

1. 基础操作

// 初始化
StringBuilder sb = new StringBuilder("Hello");

// 添加内容
sb.append(", World!");  // 结果:"Hello, World!"
sb.insert(5, " Java");  // 结果:"Hello Java, World!"

// 修改内容
sb.replace(6, 10, "Kotlin");  // 结果:"Hello Kotlin, World!"
sb.delete(5, 12);  // 结果:"Hello, World!"

// 反转字符串
sb.reverse();  // 结果:"!dlroW ,olleH"
 

2. 容量管理

StringBuilder sb = new StringBuilder(10);  // 初始容量10
sb.ensureCapacity(20);  // 确保容量至少为20
int capacity = sb.capacity();  // 获取当前容量
 

3. 转换为 String

String result = sb.toString();  // 生成不可变String对象
 

三、性能对比

1. 测试代码示例

// 测试环境:JDK 17,循环100万次
public class StringBuilderVsBuffer {
    public static void main(String[] args) {
        int iterations = 1_000_000;
        
        // StringBuilder测试
        long start = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < iterations; i++) {
            sb.append("a");
        }
        System.out.println("StringBuilder耗时:" + (System.currentTimeMillis() - start) + "ms");
        
        // StringBuffer测试
        start = System.currentTimeMillis();
        StringBuffer sbuffer = new StringBuffer();
        for (int i = 0; i < iterations; i++) {
            sbuffer.append("a");
        }
        System.out.println("StringBuffer耗时:" + (System.currentTimeMillis() - start) + "ms");
    }
}
 

2. 典型测试结果

操作次数StringBuilder 耗时StringBuffer 耗时
10 万次 ~5ms ~15ms
100 万次 ~10ms ~50ms
1000 万次 ~50ms ~200ms

结论:在单线程环境下,StringBuilder的性能通常比StringBuffer快 3-5 倍,主要因为无需同步开销。

四、线程安全分析

1. StringBuffer 的线程安全机制

// StringBuffer的append方法(带同步锁)
@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}
 

2. 多线程场景测试

// 模拟多线程环境下的字符串拼接
public class ThreadSafetyTest {
    private static StringBuilder sb = new StringBuilder();
    private static StringBuffer sbuffer = new StringBuffer();
    
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 10;
        Thread[] threads = new Thread[threadCount];
        
        // StringBuilder测试(线程不安全)
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    sb.append("a");
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) t.join();
        System.out.println("StringBuilder长度:" + sb.length());  // 可能小于10000
        
        // StringBuffer测试(线程安全)
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    sbuffer.append("a");
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) t.join();
        System.out.println("StringBuffer长度:" + sbuffer.length());  // 必定等于10000
    }
}
 

五、应用场景选择

1. 推荐使用 StringBuilder 的场景

  • 单线程环境下的字符串拼接(如局部变量使用)
  • 性能敏感的操作(如循环中大量拼接)
  • 无需考虑线程安全的场景

2. 推荐使用 StringBuffer 的场景

  • 多线程环境下的共享变量操作
  • 要求线程安全的字符串操作(如 Servlet 中的全局变量)
  • 与遗留代码兼容(早期版本仅提供 StringBuffer)

3. 避免使用 + 拼接字符串的场景

// 低效方式(循环中使用+)
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;  // 每次循环创建新String对象
}

// 高效方式(使用StringBuilder)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);  // 仅创建一个StringBuilder对象
}
 

六、最佳实践与注意事项

1. 初始化容量优化

// 预估容量,减少扩容次数
StringBuilder sb = new StringBuilder(1000);  // 初始容量1000
 

2. 链式调用

// 利用返回值实现链式调用
sb.append("a").append("b").insert(0, "prefix");
 

3. 转换为 String 的时机

// 避免频繁转换为String
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();  // 仅在需要时转换
 

4. Java 8 + 的 StringJoiner

// 更适合处理分隔符连接(如CSV生成)
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.add("Apple").add("Banana").add("Cherry");
String result = joiner.toString();  // 结果:"[Apple, Banana, Cherry]"
 

七、常见面试问题

  1. String、StringBuffer 和 StringBuilder 的区别?
    • String:不可变,每次操作生成新对象
    • StringBuffer:可变,线程安全,性能较低
    • StringBuilder:可变,非线程安全,性能高
  2. StringBuffer 如何保证线程安全?
    • 所有公开方法使用synchronized修饰,确保同一时间只有一个线程访问。
  3. 在循环中使用 + 拼接字符串有什么问题?
    • 会生成大量临时 String 对象,导致频繁 GC,性能低下。

八、总结

StringBuilderStringBuffer均提供高效的可变字符串操作,但适用场景不同:

  • 优先使用 StringBuilder:在单线程或无需线程安全的场景中,性能更优。
  • 使用 StringBuffer:在多线程环境下,确保线程安全。
  • 避免使用 String + 拼接:在循环或大量拼接场景中,性能最差。

合理选择字符串处理类,可显著提升 Java 程序的性能和稳定性

posted on 2025-06-30 10:43  coding博客  阅读(171)  评论(0)    收藏  举报