StringBuffer 是 Java 中用于处理可变字符串的核心类之一。它与 StringStringBuilder 有密切关系,但在多线程环境下具有独特优势。


一、核心概念

1. 什么是 StringBuffer?

  • StringBuffer 是一个可变的字符序列,允许在不创建新对象的情况下修改字符串内容。
  • 它是线程安全的,所有公共方法都使用 synchronized 关键字修饰,适合多线程环境。
  • 位于 java.lang 包中,无需导入。

2. 与 String 的区别

特性 String StringBuffer
可变性 不可变(immutable) 可变(mutable)
线程安全 是(因不可变) 是(因同步方法)
性能 拼接多时性能差 拼接效率高
使用场景 常量、配置、少量拼接 大量字符串操作、多线程环境

3. 与 StringBuilder 的区别

特性 StringBuffer StringBuilder
线程安全 ✅ 是(同步) ❌ 否(非同步)
性能 相对较慢(加锁开销) 更快
适用场景 多线程 单线程

结论:单线程优先用 StringBuilder,多线程考虑 StringBuffer


二、操作步骤(非常详细)

步骤 1:导入类(可选)

// StringBuffer 在 java.lang 包中,无需显式导入
// import java.lang.StringBuffer; // 可省略

步骤 2:创建 StringBuffer 对象

方法 1:无参构造函数(默认容量 16)

StringBuffer sb = new StringBuffer();
// 初始内容为空,容量为16个字符

方法 2:指定初始容量

StringBuffer sb = new StringBuffer(32);
// 容量为32,内容为空

方法 3:基于字符串初始化

StringBuffer sb = new StringBuffer("Hello");
// 内容为 "Hello",容量 = 16 + 字符串长度(5)= 21

💡 容量自动增长机制:当内容超过当前容量时,系统自动扩容(通常为原容量*2+2)。


步骤 3:添加内容(append)

append() 是最常用的方法,支持几乎所有数据类型。

StringBuffer sb = new StringBuffer("Hello");

// 添加字符串
sb.append(" World");

// 添加数字
sb.append(2025);

// 添加布尔值
sb.append(true);

// 添加字符
sb.append('!');

// 添加其他对象(调用 toString())
sb.append(new Object()); // 如 java.lang.Object@xxxx

System.out.println(sb.toString()); 
// 输出: Hello World2025true!java.lang.Object@xxxx

步骤 4:插入内容(insert)

在指定位置插入数据。

StringBuffer sb = new StringBuffer("HelloWorld");

// 在索引 5 处插入空格
sb.insert(5, " ");

// 在索引 0 处插入前缀
sb.insert(0, "Say: ");

// 插入整数
sb.insert(10, 2025);

System.out.println(sb.toString());
// 输出: Say: Hello 2025World

⚠️ 索引从 0 开始,超出范围会抛出 StringIndexOutOfBoundsException


步骤 5:替换内容(replace)

替换指定范围的字符。

StringBuffer sb = new StringBuffer("I love Java");

// 将索引 7 到 10(不包含)的内容替换为 "Python"
sb.replace(7, 10, "Python");

System.out.println(sb.toString());
// 输出: I love Python

步骤 6:删除内容(delete / deleteCharAt)

StringBuffer sb = new StringBuffer("abcdef");

// 删除索引 2 到 4(不包含)的字符
sb.delete(2, 4); // 删除 'c','d'
// 结果: abef

// 删除指定位置的单个字符
sb.deleteCharAt(0); // 删除 'a'
// 结果: bef

步骤 7:反转字符串(reverse)

StringBuffer sb = new StringBuffer("hello");
sb.reverse();
System.out.println(sb.toString()); // 输出: olleh

步骤 8:获取长度与容量

StringBuffer sb = new StringBuffer("Hi");

int length = sb.length();     // 实际字符数:2
int capacity = sb.capacity(); // 当前缓冲区容量:18(16+2)

System.out.println("长度: " + length);
System.out.println("容量: " + capacity);

步骤 9:转换为 String

StringBuffer sb = new StringBuffer("Final Result");
String result = sb.toString();
// 用于输出、存储或传递

三、常见错误

错误 原因 解决方案
StringIndexOutOfBoundsException 索引越界(负数或超出长度) 检查索引是否在 [0, length()] 范围内
忘记调用 toString() 直接打印 StringBuffer 对象 使用 .toString() 转换
在单线程中误用 StringBuffer 性能浪费 单线程改用 StringBuilder
频繁扩容影响性能 初始容量太小 预估大小并设置初始容量

四、注意事项

  1. 线程安全:多线程共享时无需额外同步。
  2. 不要过度使用:单线程环境下性能低于 StringBuilder
  3. 🔢 容量管理:频繁扩容影响性能,建议预设合理容量。
  4. 🔄 方法链调用:所有修改方法返回 this,支持链式调用。
    sb.append("a").append("b").insert(0, "Start");
    
  5. 🧹 及时清理:长期持有大字符串缓冲区可能导致内存浪费。

五、使用技巧

技巧 1:链式编程

StringBuffer sb = new StringBuffer()
    .append("Name: ").append(name)
    .append(", Age: ").append(age)
    .append(", Active: ").append(isActive);

技巧 2:预设容量提升性能

// 预估最终字符串长度约为 1000
StringBuffer sb = new StringBuffer(1000);

技巧 3:清空 StringBuffer

sb.setLength(0); // 清空内容,保留容量
// 或
sb.delete(0, sb.length());

技巧 4:检查是否为空

if (sb.length() == 0) {
    System.out.println("缓冲区为空");
}

六、最佳实践与性能优化

✅ 最佳实践

场景 推荐做法
单线程字符串拼接 使用 StringBuilder
多线程共享字符串缓冲 使用 StringBuffer
已知字符串最终长度 设置初始容量
多次拼接操作 避免使用 +,使用 append()
构建 SQL 或 JSON 使用 StringBuffer/StringBuilder

🚀 性能优化建议

  1. 避免默认构造函数(除非不确定大小)

    // 不推荐
    StringBuffer sb = new StringBuffer();
    
    // 推荐:预估大小
    StringBuffer sb = new StringBuffer(512);
    
  2. 减少扩容次数

    • 每次扩容涉及数组复制,成本较高。
    • 若知道大致长度,直接设置足够容量。
  3. 优先 StringBuilder(单线程)

    // 单线程场景
    StringBuilder sb = new StringBuilder(1024);
    
  4. 避免在循环中创建 StringBuffer

    // ❌ 错误示范
    for (int i = 0; i < 1000; i++) {
        StringBuffer temp = new StringBuffer();
        temp.append(i);
    }
    
    // ✅ 正确做法
    StringBuffer sb = new StringBuffer(4000); // 预估容量
    for (int i = 0; i < 1000; i++) {
        sb.append(i);
    }
    
  5. 及时释放引用

    sb = null; // 不再使用时置空,帮助 GC 回收
    

七、完整示例:构建用户信息字符串

public class StringBufferExample {
    public static void main(String[] args) {
        // 预设容量为 200
        StringBuffer userInfo = new StringBuffer(200);
        
        String name = "张三";
        int age = 30;
        boolean isActive = true;
        String email = "zhangsan@example.com";
        
        userInfo.append("=== 用户信息 ===\n")
                .append("姓名: ").append(name).append("\n")
                .append("年龄: ").append(age).append("\n")
                .append("状态: ").append(isActive ? "激活" : "未激活").append("\n")
                .append("邮箱: ").append(email).append("\n")
                .append("生成时间: ").append(System.currentTimeMillis());
        
        // 输出结果
        System.out.println(userInfo.toString());
        
        // 清空重用
        userInfo.setLength(0);
        userInfo.append("新内容...");
    }
}

总结

要点 说明
✅ 核心用途 可变字符串操作,线程安全
✅ 关键方法 append(), insert(), delete(), reverse(), toString()
✅ 性能提示 预设容量、单线程用 StringBuilder
✅ 最佳实践 链式调用、避免频繁扩容、及时清理

📌 一句话总结

多线程字符串拼接用 StringBuffer,单线程用 StringBuilder,记得预设容量更高效!