写 Java 的时,你有没有遇到过这种情况:拼接个字符串,发现程序变慢了?或者多线程环境下,拼出来的字符串乱了?其实,很多时候问题就出在于你用了不该用的"字符串"。
Java里有三个常用的字符串相关类:String
、StringBuffer
、StringBuilder
。它们看着像一家人,但如果用错了地方,可能会出现性能差或者其它的问题。
一、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:多线程下的"安全卫士"
StringBuffer
和StringBuilder
功能几乎一样,都能拼接字符串。区别在哪?线程安全。
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
能保证顺序正确。
但是!
加锁是有代价的。StringBuffer
比StringBuilder
慢一点。所以,如果不是多线程,别用它。
四、总结
场景 | 用哪个? | 为什么? |
---|---|---|
字符串不变,简单使用 | String |
简单直观,适合常量 |
单线程,大量拼接 | StringBuilder |
快,不加锁,效率高 |
多线程,共享拼接 | StringBuffer |
安全,防止数据错乱 |
循环拼接(不管多少线程) | 别用 String |
性能极差,容易 OOM |
- 不变用
String
- 单线程拼接用
StringBuilder
- 多线程拼接用
StringBuffer
所以,别小看这几个类的选择。用对了,程序真的可以很稳;