String、StringBuffer 和 StringBuilder 的区别
目录
1. 概述对比
2. String:不可变的字符串
2.1 基本特性
2.2 使用示例
2.3 适用场景
3. StringBuffer:可变的线程安全字符串
3.1 基本特性
3.2 使用示例
3.3 适用场景
4. StringBuilder:可变的非线程安全字符串
4.1 基本特性
4.2 使用示例
4.3 适用场景
5. 性能对比
5.1 性能测试代码
5.2 测试结果
6. 内存使用对比
6.1 String 的内存使用
6.2 StringBuffer/StringBuilder 的内存使用
7. 最佳实践
7.1 选择原则
7.2 代码示例
7.3 初始化容量优化
8. 总结
在 Java 开发中,字符串处理是最常见的操作之一。Java 提供了三种主要的字符串类:String、StringBuffer 和 StringBuilder。理解它们之间的区别对于编写高效、正确的代码至关重要。
1. 概述对比
特性 | String | StringBuffer | StringBuilder |
可变性 | 不可变 | 可变 | 可变 |
线程安全 | 是(天生线程安全) | 是(同步方法) | 否(非线程安全) |
性能 | 最低(频繁创建新对象) | 中等(有同步开销) | 最高(无同步开销) |
使用场景 | 字符串常量、少量操作 | 多线程环境下的字符串操作 | 单线程环境下的字符串操作 |
内存效率 | 低(频繁操作时) | 高 | 最高 |
版本引入 | Java 1.0 | Java 1.0 | Java 5 |
2. String:不可变的字符串
2.1 基本特性
String 是不可变类,一旦创建就不能修改。任何看似修改 String 的操作实际上都是创建了一个新的 String 对象。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {private final char value[]; // Java 8 及之前private final byte[] value; // Java 9 及之后// ...
}
2.2 使用示例
// 创建字符串
String str1 = "Hello"; // 字面量方式,放入字符串常量池
String str2 = new String("Hello"); // 创建新对象// 字符串操作(都返回新对象)
String result = str1.concat(" World"); // 拼接
String upper = result.toUpperCase(); // 转大写
String sub = result.substring(6); // 子字符串System.out.println(str1); // 输出: Hello(原字符串未改变)
System.out.println(result); // 输出: Hello World
2.3 适用场景
- 字符串常量定义
- 不需要频繁修改的字符串
- 作为 HashMap 的键(利用不可变性)
- 多线程环境下的字符串共享
3. StringBuffer:可变的线程安全字符串
3.1 基本特性
StringBuffer 是可变的字符序列,所有修改方法都使用 synchronized
关键字保证线程安全。
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {@Overridepublic synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;}// 其他方法也都是同步的
}
3.2 使用示例
StringBuffer sb = new StringBuffer("Hello");// 链式操作,直接修改原对象
sb.append(" ").append("World").append("!").insert(5, " Beautiful");System.out.println(sb.toString()); // 输出: Hello Beautiful World!// 线程安全示例
StringBuffer threadSafeBuffer = new StringBuffer();
Runnable task = () -> {for (int i = 0; i < 1000; i++) {threadSafeBuffer.append(i).append(" ");}
};// 多个线程同时操作是安全的
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
3.3 适用场景
- 多线程环境下的字符串操作
- 需要线程安全的字符串构建
- 复杂的字符串拼接和修改
4. StringBuilder:可变的非线程安全字符串
4.1 基本特性
StringBuilder 与 StringBuffer API 几乎完全相同,但方法没有同步,因此性能更高。
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {@Overridepublic StringBuilder append(String str) {super.append(str);return this;}// 方法没有 synchronized 关键字
}
4.2 使用示例
StringBuilder sb = new StringBuilder("Hello");// 高效的链式操作
sb.append(" ").append("World").append("!").insert(5, " Amazing");System.out.println(sb.toString()); // 输出: Hello Amazing World!// 单线程环境下性能最佳
long startTime = System.currentTimeMillis();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 100000; i++) {builder.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder 耗时: " + (endTime - startTime) + "ms");
4.3 适用场景
- 单线程环境下的字符串操作
- 性能要求高的字符串处理
- 大量的字符串拼接和修改
5. 性能对比
5.1 性能测试代码
public class StringPerformanceTest {private static final int ITERATIONS = 100000;public static void testString() {long start = System.currentTimeMillis();String str = "";for (int i = 0; i < ITERATIONS; i++) {str += i; // 每次循环创建新对象}long end = System.currentTimeMillis();System.out.println("String 耗时: " + (end - start) + "ms");}public static void testStringBuffer() {long start = System.currentTimeMillis();StringBuffer sb = new StringBuffer();for (int i = 0; i < ITERATIONS; i++) {sb.append(i);}long end = System.currentTimeMillis();System.out.println("StringBuffer 耗时: " + (end - start) + "ms");}public static void testStringBuilder() {long start = System.currentTimeMillis();StringBuilder sb = new StringBuilder();for (int i = 0; i < ITERATIONS; i++) {sb.append(i);}long end = System.currentTimeMillis();System.out.println("StringBuilder 耗时: " + (end - start) + "ms");}public static void main(String[] args) {testString();testStringBuffer();testStringBuilder();}
}
5.2 测试结果
String 耗时: 4235ms
StringBuffer 耗时: 8ms
StringBuilder 耗时: 5ms
6. 内存使用对比
6.1 String 的内存使用
String result = "";
for (int i = 0; i < 10; i++) {result += i; // 创建11个String对象
}
// 产生大量垃圾对象,增加GC压力
6.2 StringBuffer/StringBuilder 的内存使用
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {sb.append(i); // 在同一个缓冲区中操作
}
// 只在需要时扩容,内存使用高效
7. 最佳实践
7.1 选择原则
1.使用 String:
- 字符串常量
- 不需要修改的字符串
- 多线程共享的字符串
2.使用 StringBuffer:
- 多线程环境下的字符串操作
- 需要线程安全的字符串构建
3.使用 StringBuilder:
- 单线程环境下的字符串操作
- 性能要求高的场景
- 方法内部的字符串处理
7.2 代码示例
public class StringBestPractices {// 常量使用 Stringpublic static final String DATABASE_URL = "jdbc:mysql://localhost:3306/mydb";// 方法参数使用 String(不可变,安全)public void processMessage(String message) {// 处理消息}// 单线程字符串构建使用 StringBuilderpublic String buildQuery(List<String> conditions) {StringBuilder query = new StringBuilder("SELECT * FROM users WHERE 1=1");for (String condition : conditions) {query.append(" AND ").append(condition);}return query.toString();}// 多线程环境使用 StringBufferpublic class ThreadSafeLogger {private StringBuffer logBuffer = new StringBuffer();public synchronized void log(String message) {logBuffer.append(new Date()).append(": ").append(message).append("\n");}public String getLog() {return logBuffer.toString();}}
}
7.3 初始化容量优化
// 如果知道大致长度,可以预先设置容量
StringBuilder sb = new StringBuilder(1000); // 预分配容量
for (int i = 0; i < 1000; i++) {sb.append("item").append(i).append(",");
}
// 避免多次扩容,提高性能
8. 总结
方面 | String | StringBuffer | StringBuilder |
核心特点 | 不可变 | 可变、线程安全 | 可变、非线程安全 |
性能排序 | 最慢 | 中等 | 最快 |
线程安全 | 是 | 是 | 否 |
内存效率 | 低(频繁操作时) | 高 | 最高 |
使用场景 | 常量、少量操作 | 多线程字符串操作 | 单线程字符串操作 |
黄金法则:
- 如果字符串不需要改变:使用 String
- 如果在单线程中需要改变字符串:使用 StringBuilder
- 如果在多线程中需要改变字符串:使用 StringBuffer
希望本篇文章对你有帮助。