在 Java 中,String、StringBuilder和StringBuffer都是用于处理字符串的类,但它们在特性和使用场景上有显著区别:
1.可变性
String:不可变(Immutable)。每次对 String 进行修改(如拼接、替换)都会创建新的 String 对象,原对象不会改变。
String s = "hello";
s += " world"; // 创建了新的String对象,原"hello"仍存在StringBuilder和StringBuffer:可变(Mutable)。修改操作直接在原有对象上进行,不会创建新对象。
StringBuilder sb = new StringBuilder("hello");
sb.append(" world"); // 在原有对象上直接追加,没有创建新对象String:线程安全。由于不可变性,多线程操作不会引发问题。 StringBuffer:线程安全。其方法都被synchronized修饰,可在多线程环境中安全使用。 StringBuilder:线程不安全。没有同步机制,性能略高于 StringBuffer,但不能在多线程环境中使用。
3.性能
String:性能较差。 StringBuilder:性能最好。由于没有同步开销,适合单线程下的频繁字符串操作。 StringBuffer:性能介于两者之间。因同步机制会有一定性能损耗。
4.适用场景 String:适合字符串内容不常修改的场景(如常量定义、少量拼接操作)。 StringBuilder:适合单线程环境下需要频繁修改字符串的场景(如循环拼接、字符串构建)。 StringBuffer:适合多线程环境下需要频繁修改字符串的场景(如多线程日志拼接)。
String 是 Java 中最常用的数据类型之一,常被用于存储敏感信息(如用户名、密码、网络连接 URL 等)。 String属于不可变类,不可变性保证了字符串一旦创建,其内容就无法被修改。 为什么设计成这样的?
从安全性分析:
1.多线程环境下,无需额外同步机制就能安全访问(避免了并发修改导致的数据不一致). 2.作为参数传递时,不会被调用方意外篡改,确保数据完整性。
缓存优化
Java 设计了 字符串常量池(String Constant Pool) 来缓存字符串,避免重复创建相同内容的对象。
String a = "hello";
String b = "hello"; // 直接复用常量池中的"hello",而非新建对象如果 String 是可变的,当一个引用修改了字符串内容,会导致常量池中所有引用该字符串的变量都受到影响,破坏缓存逻辑。 不可变性确保了常量池中的字符串可以安全共享。
哈希表优化
这里是我上午搜集的学习的,哈希表博主此时还没有学习 String 常被用作哈希表(如 HashMap)的键(Key)。哈希表的工作依赖于键的哈希值(hashCode)的稳定性。 由于 String 不可变,其哈希值在创建后就固定不变,无需每次使用时重新计算,显著提升了哈希表的性能。 如果 String 可变,修改内容会导致哈希值变化,可能造成哈希表中键值对无法正确查找。
底层的设计 String 的不可变性由其内部实现强制保证:
public final class String {
private final char value[]; // 存储字符串的字符数组被声明为final
// 其他方法均不会修改value数组,而是返回新的String对象
}value 数组被 private final 修饰,确保外部无法直接修改,且 String 类本身被 final 修饰,禁止子类重写方法破坏不可变性。 所有修改字符串的方法(如 substring()、replace())都会创建新的 String 对象,而非修改原对象。
StringBuilder 是 Java 中用于处理可变字符串的类,专为高效拼接、修改字符串而设计。它解决了 String 不可变性导致的性能问题,是单线程环境下处理字符串的首选。 核心特点 可变性:所有修改操作(拼接、插入、删除等)直接在原有对象上进行,不会创建新对象。 非线程安全:没有同步机制(无 synchronized 修饰),性能优于 StringBuffer。 高效性:适合单线程下频繁修改字符串的场景,避免 String 频繁创建对象的开销。

public class StringBuilderDemo {
public static void main(String[] args) {
// 创建StringBuilder对象(初始内容为"Hello")
StringBuilder sb = new StringBuilder("Hello");
// 追加内容(支持链式调用)
sb.append(" ").append("World").append("!");
System.out.println(sb); // 输出:Hello World!
// 插入内容
sb.insert(5, " Java");
System.out.println(sb); // 输出:Hello Java World!
// 替换内容
sb.replace(6, 10, "Python");
System.out.println(sb); // 输出:Hello Python World!
// 反转字符串
sb.reverse();
System.out.println(sb); // 输出:!dlroW nohtyP olleH
// 转换为String
String result = sb.toString();
System.out.println(result); // 输出:!dlroW nohtyP olleH
}
}性能优势 与 String 相比,StringBuilder 在循环拼接等场景下性能差异巨大:
// 低效:每次拼接都创建新String对象
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 性能差,产生大量临时对象
}
// 高效:直接在原有对象上操作
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 性能优异,无额外对象创建
}注意事项: 单线程专用:多线程环境下需使用 StringBuffer 保证线程安全。 初始容量:默认容量为 16,可通过构造函数指定初始容量(如 new StringBuilder(100)),减少扩容次数。 最终转换:需调用 toString() 才能将 StringBuilder 转换为 String 类型(如用于输出或作为参数传递)。
StringJoiner 是 Java 8 新增的类,专为 “分隔符拼接” 场景设计,尤其适合构建如 a,b,c 或 [a, b, c] 形式的字符串。
StringJoiner 依赖 StringBuilder
StringJoiner 是 Java 8 新增的专用工具类,专为 “分隔符拼接” 场景设计(如 a,b,c)。 其内部通过 StringBuilder 实现(源码中包含一个 StringBuilder 成员变量),本质是对 StringBuilder 的封装,简化了分隔符、前缀、后缀的处理逻辑。 优势: 自动处理分隔符:无需手动判断是否为第一个 / 最后一个元素,避免多余分隔符。 支持前缀和后缀:可直接指定整体的前缀(如 [)和后缀(如 ])。 劣势: 功能单一:仅适用于分隔符拼接,不支持插入、删除等复杂操作。
性能对比:
单线程场景:两者性能接近,StringBuilder 略优(因 StringJoiner 内部其实是用 StringBuilder 实现的,多一层封装)。 差异可忽略:在大多数业务场景中,两者的性能差异对整体性能影响极小,选择主要看代码简洁性。
StringBuffer 是 Java 中用于处理可变字符串的类,与 StringBuilder 功能相似,但具备一个关键区别:它是线程安全的。这使得它适合在多线程环境中进行字符串操作。
核心特点
可变性:与 StringBuilder 一样,StringBuffer 的内容可以直接修改(无需创建新对象),支持高效的字符串拼接、插入等操作。 线程安全:所有方法都被 synchronized 修饰,确保多线程环境下操作的安全性(避免并发修改导致的数据不一致)。 性能:由于同步机制的开销,性能略低于 StringBuilder,但高于频繁修改的 String。
常用方法:
StringBuffer 的方法与 StringBuilder 几乎完全一致,主要包括:

public class StringBufferDemo {
public static void main(String[] args) {
// 创建StringBuffer对象(初始内容为空,容量16)
StringBuffer sb = new StringBuffer();
// 追加内容(支持链式调用)
sb.append("Java").append(" ").append("StringBuffer");
System.out.println(sb); // 输出:Java StringBuffer
// 插入内容
sb.insert(4, " 8");
System.out.println(sb); // 输出:Java 8 StringBuffer
// 反转字符串
sb.reverse();
System.out.println(sb); // 输出:rebuffeirtS 8 avaJ
// 转换为String
String result = sb.toString();
System.out.println(result); // 输出:rebuffeirtS 8 avaJ
}
}线程安全的体现 StringBuffer 的线程安全性源于方法的同步修饰,例如其 append 方法的底层实现:
// StringBuffer的append方法(简化版)
public synchronized StringBuffer append(String str) {
// 操作内部字符数组(修改内容)
// ...
return this;
}synchronized 确保同一时间只有一个线程能执行该方法,避免了多线程同时修改导致的数据混乱。
适用场景
多线程环境:当多个线程需要同时操作同一个字符串对象时(如多线程日志拼接、共享配置字符串等)。 频繁修改字符串:需要拼接、插入等操作,且无法保证单线程访问时。
如果是单线程场景,优先使用 StringBuilder 以获得更好的性能。 注意事项: 初始容量:默认容量为 16,可通过构造函数指定(如 new StringBuffer(100)),减少自动扩容的性能损耗。 避免过度使用:单线程环境中无需为了 “安全” 而使用 StringBuffer,同步开销会降低性能。 与 StringBuilder 的选择:除了线程安全,两者功能完全一致,选择时主要看是否涉及多线程。