String、StringBuffer和StringBuilder,别再用错了!

写 Java 的时,你有没有遇到过这种情况:拼接个字符串,发现程序变慢了?或者多线程环境下,拼出来的字符串乱了?其实,很多时候问题就出在于你用了不该用的"字符串"。

Java里有三个常用的字符串相关类:StringStringBufferStringBuilder。它们看着像一家人,但如果用错了地方,可能会出现性能差或者其它的问题。

一、String:用在"不变"的场景

String是不可变的。一旦你创建了一个字符串,它就定下来了,改不了。如果你改了它,其实是新建了一个字符串。

比如:

java 复制代码
String str = "Hello";
str = str + " World";

你以为是改了str?其实是在内存里新建了一个"Hello World",原来的"Hello"被丢掉了。

使用场景:

  • 字符串内容固定不变
  • 作为配置项、常量、Map的key
  • 简单拼接,次数少(1~2 次)

案例:

你从配置文件读了个数据库地址:

java 复制代码
String dbUrl = "jdbc:mysql://localhost:3306/mydb";

这个地址不会变,就用String,简单直接。

注意:

别拿 String去搞循环拼接!比如:

java 复制代码
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i; // 每次都新建对象,内存爆炸!
}

这种写法在小数据量时看不出问题,数据一大,程序直接卡死。

二、StringBuilder:单线程下,拼接字符串的"快枪手"

如果你要在一个线程里 拼接大量字符串StringBuilder是首选。

它可变,而且速度快,内存消耗小。

使用场景:

  • 单线程下大量字符串拼接
  • 构造 JSON、SQL 语句
  • 日志信息组装

案例 1:拼接 SQL

java 复制代码
StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1");
if (age > 0) {
    sql.append(" AND age > ").append(age);
}
if (name != null) {
    sql.append(" AND name LIKE '%").append(name).append("%'");
}

这种动态 SQL,用StringBuilder拼,又快又安全。

案例 2:生成日志消息

java 复制代码
StringBuilder logMsg = new StringBuilder();
logMsg.append("用户 ").append(userId)
      .append(" 在 ").append(new Date())
      .append(" 执行了操作:").append(action);
System.out.println(logMsg.toString());

拼接几个字段,清晰又高效。 只要是一个人干活(单线程),拼字符串多,就用StringBuilder

三、StringBuffer:多线程下的"安全卫士"

StringBufferStringBuilder功能几乎一样,都能拼接字符串。区别在哪?线程安全。

StringBuffer的每个方法都加了锁(synchronized),多个线程同时操作也不会乱。

使用场景:

  • 多线程环境下拼接字符串
  • 全局共享的字符串缓冲区
  • 对线程安全有要求的场景

案例:

假设你有个计数器,多个线程往一个日志缓冲区里写信息:

java 复制代码
// 全局共享
StringBuffer sharedLog = new StringBuffer();

// 线程1
new Thread(() -> {
    sharedLog.append("Thread-1: 开始处理\n");
    // ...处理逻辑
    sharedLog.append("Thread-1: 处理完成\n");
}).start();

// 线程2
new Thread(() -> {
    sharedLog.append("Thread-2: 开始处理\n");
    // ...处理逻辑
    sharedLog.append("Thread-2: 处理完成\n");
}).start();

这时候用StringBuilder就可能出问题------两个线程同时写,内容可能错乱、丢失。而StringBuffer能保证顺序正确。

但是!

加锁是有代价的。StringBufferStringBuilder慢一点。所以,如果不是多线程,别用它

四、总结

场景 用哪个? 为什么?
字符串不变,简单使用 String 简单直观,适合常量
单线程,大量拼接 StringBuilder 快,不加锁,效率高
多线程,共享拼接 StringBuffer 安全,防止数据错乱
循环拼接(不管多少线程) 别用 String 性能极差,容易 OOM
  • 不变用 String
  • 单线程拼接用 StringBuilder
  • 多线程拼接用 StringBuffer

所以,别小看这几个类的选择。用对了,程序真的可以很稳;

相关推荐
葫芦和十三3 小时前
图解 MongoDB 02|BSON:你以为存的是 JSON,其实是带类型的二进制
后端·mongodb·agent
葫芦和十三4 小时前
图解 MongoDB 01|文档数据库
后端·mongodb·agent
陈随易6 小时前
VSCode的Copilot扩展支持接入DeepSeek,Kimi了!
前端·后端·程序员
我不是外星人7 小时前
有了 Harness Engineering ,真的还需要研发工程师吗?
前端·后端·ai编程
candyTong7 小时前
RTK 技术原理:一次典型会话里,80% 上下文是怎么省下来的
javascript·后端·架构
Rust研习社9 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒10 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro11 小时前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax11 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH11 小时前
Koa和Express的区别
后端