在 Java 开发中,字符串处理是非常常见的操作。很多初学者会疑惑:为什么有了 String,还需要 StringBuffer 和 StringBuilder?它们之间到底有什么区别?
本篇博客将从底层原理、线程安全、性能、使用场景几个方面详细分析三者的区别。
一、String、StringBuffer、StringBuilder 简介
Java 中提供了三个主要用于处理字符串的类:
| 类 | 特点 | 是否可变 | 线程安全 | 性能 |
|---|---|---|---|---|
| String | 不可变字符串 | ❌ 不可变 | ✅ 安全 | 较低 |
| StringBuffer | 可变字符串 | ✅ 可变 | ✅ 安全 | 中等 |
| StringBuilder | 可变字符串 | ✅ 可变 | ❌ 不安全 | 最高 |
简单理解:
-
String:适合少量、不频繁修改的字符串
-
StringBuffer:适合多线程环境下频繁修改字符串
-
StringBuilder:适合单线程环境下频繁修改字符串
二、String 为什么不可变?
1. String 的本质
在 Java 中:
String str = "hello";
实际上创建了一个 String 对象。
String 内部使用字符数组保存内容:
private final char[] value;
例如:
String对象
+-------------+
| hello |
+-------------+
|
↓
['h','e','l','l','o']
由于 String 被设计为不可变:
String str = "hello";
str = str + " world";
看起来修改了字符串:
hello
↓
hello world
实际上:
原对象:
hello
新创建对象:
hello world
原来的字符串没有改变。
2. String 修改为什么性能低?
例如:
String s = "";
for(int i=0;i<10000;i++){
s += i;
}
执行过程:
第一次:
""
+
"0"
创建新对象:
"0"
第二次:
"0"
+
"1"
创建新对象:
"01"
第三次:
"01"
+
"2"
创建新对象:
"012"
不断创建新的 String 对象。
所以:
大量字符串拼接时,不推荐使用 String。
三、StringBuffer 原理
StringBuffer 是可变字符串。
例如:
StringBuffer sb = new StringBuffer();
sb.append("hello");
sb.append(" world");
内部:
第一次:
hello
第二次:
hello world
它修改的是原来的字符数组:
StringBuffer对象
+----------------+
| hello world |
+----------------+
|
↓
['h','e','l','l','o',' ','w','o','r','l','d']
不会频繁创建对象。
四、StringBuilder 原理
StringBuilder 和 StringBuffer 几乎一样。
例如:
StringBuilder sb = new StringBuilder();
sb.append("Java");
sb.append("开发");
内部也是维护一个可变数组。
区别:
StringBuffer 方法加了 synchronized 关键字,而 StringBuilder 没有。
五、线程安全区别
1. String
String 不可变:
String s="hello";
多个线程同时访问:
线程A --> hello
线程B --> hello
线程C --> hello
不会修改,所以安全。
2. StringBuffer
StringBuffer:
public synchronized StringBuffer append(String str)
{
}
方法使用 synchronized:
线程A
|
锁
|
修改StringBuffer
线程B
等待
所以:
✅ 线程安全
但是:
因为加锁:
性能降低。
3. StringBuilder
StringBuilder:
public StringBuilder append(String str)
{
}
没有锁。
多个线程:
线程A 修改
线程B 同时修改
可能冲突
所以:
❌ 不保证线程安全。
但是速度最快。
六、性能测试对比
假设循环拼接 10 万次:
for(int i=0;i<100000;i++){
xxx.append(i);
}
大概性能:
| 类型 | 时间 |
|---|---|
| String | 最慢 |
| StringBuffer | 中等 |
| StringBuilder | 最快 |
原因:
String
每次创建新对象:
对象1
对象2
对象3
对象4
...
StringBuffer
修改自身:
对象
hello world
但是:
加锁
解锁
消耗性能。
StringBuilder
修改自身:
hello world
没有锁。
所以最快。
七、三者使用场景
1. String 使用场景
适合:
固定字符串
例如:
String name="Tom";
配置文件
String url="https://www.baidu.com";
少量拼接
String msg="hello"+"world";
2. StringBuilder 使用场景(推荐)
大部分开发场景首选:
例如:
生成 SQL:
StringBuilder sql=new StringBuilder();
sql.append("select * ");
sql.append("from user");
生成 JSON:
StringBuilder json=new StringBuilder();
json.append("{");
json.append("\"name\":\"Tom\"");
json.append("}");
特点:
-
单线程
-
大量拼接
-
追求性能
3. StringBuffer 使用场景
现在使用较少。
主要用于:
多线程共享字符串。
例如:
多个线程写日志:
线程1 ----\
\
线程2 ---- StringBuffer
/
线程3 ----/
保证数据安全。
八、StringBuffer 和 StringBuilder 源码区别
StringBuffer
public synchronized StringBuffer append(String str)
{
super.append(str);
return this;
}
关键:
synchronized
StringBuilder
public StringBuilder append(String str)
{
super.append(str);
return this;
}
没有锁。
九、面试高频问题
1. String 为什么不可变?
答案:
因为 String 类设计为 final,并且内部 char 数组不可修改。
好处:
-
安全性高
-
支持字符串常量池
-
可以作为 HashMap 的 key
-
多线程访问安全
2. String、StringBuffer、StringBuilder区别?
标准回答:
String 是不可变字符串,每次修改都会创建新的对象;StringBuffer 和 StringBuilder 都是可变字符串,通过修改自身内容提高效率。StringBuffer 使用 synchronized 保证线程安全,因此性能低于 StringBuilder;StringBuilder 不保证线程安全,但是性能最高,适合单线程大量字符串操作。
3. 为什么 StringBuilder 比 StringBuffer 快?
因为:
StringBuffer:
修改 + 加锁
StringBuilder:
修改
少了 synchronized 的开销。
十、总结
一句话记忆:
少量字符串 → String,大量拼接 → StringBuilder,多线程共享修改 → StringBuffer。
实际开发选择:
普通业务代码
|
↓
StringBuilder
多线程环境
|
↓
StringBuffer
固定文本
|
↓
String
在现在 Java 开发中:
StringBuilder 使用频率最高,StringBuffer 已经较少使用。