1. 可变性
- String:String 类的内容是不可变的。一旦一个 String 对象被创建,其内容就不能被修改。对 String 的任何修改操作,如拼接(
+
)或替换,实际上是创建了一个新的 String 对象。 - StringBuffer:StringBuffer 类的内容是可变的。它提供了多种方法来修改字符串内容,如追加(append)、插入(insert)和删除(delete)等。
- StringBuilder:与 StringBuffer 类似,StringBuilder 类的内容也是可变的,并且也提供了修改字符串内容的方法。
2. 线程安全性
- String:String 类的不可变性自然使其具有线程安全性,因为不可变的对象在多线程环境下不会引发数据不一致的问题。
- StringBuffer:StringBuffer 是线程安全的。它的所有公开方法都被
synchronized
修饰,确保了多个线程同时访问时的数据安全。 - StringBuilder:StringBuilder 不是线程安全的。如果多个线程同时修改同一个 StringBuilder 对象,可能会导致数据竞争和不一致的情况。
3. 性能
- String:由于 String 的不可变性,频繁的字符串修改操作会创建大量的临时对象,这可能导致性能问题和内存浪费。
- StringBuffer:虽然 StringBuffer 是线程安全的,但这种线程安全性是通过牺牲性能来实现的。因为所有公开方法都是同步的,所以在单线程环境下,其性能可能不如 StringBuilder。
- StringBuilder:StringBuilder 专为单线程环境设计,没有同步方法的开销,因此在单线程环境下性能优于 StringBuffer。
4. 使用场景
- String:适用于不需要修改字符串内容的场景,如字符串常量、字符串比较等。
- StringBuffer:适用于需要频繁修改字符串内容,并且要求线程安全的场景。
- StringBuilder:适用于需要频繁修改字符串内容,但不涉及多线程并发操作的场景。
5. 其他差异
- 实例化方式:String 可以直接通过赋值的方式实现对象实例化,而 StringBuffer 和 StringBuilder 只能通过构造方法的方式对象实例化。
- 常量池:String 对象的值存在于常量池中,可以被多个引用共享,而 StringBuffer 和 StringBuilder 的对象值则不存在于常量池中。
String 示例
String是不可变的,每次对String的操作实际上都会创建一个新的String对象:
String str1 = "Hello";
String str2 = str1 + " World"; // 这里实际上创建了一个新的String对象
System.out.println(str1); // 输出: Hello
System.out.println(str2); // 输出: Hello World
// 注意:str1的值仍然是"Hello",没有被修改
在这个例子中,str1
和str2
是两个不同的String对象,尽管str2
看起来像是str1
的扩展。
StringBuffer 示例
StringBuffer是可变的,并且它是线程安全的。如何追加字符串:
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // 在StringBuffer对象sb上追加字符串
System.out.println(sb.toString()); // 输出: Hello World
在这个例子中,sb
的内容被成功修改,从"Hello"变为了"Hello World"。由于StringBuffer是线程安全的,因此它适合在多线程环境中使用。
StringBuilder 示例
StringBuilder与StringBuffer类似,也是可变的,但它不是线程安全的。这使得它在单线程环境下比StringBuffer具有更好的性能:
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 在StringBuilder对象sb上追加字符串
System.out.println(sb.toString()); // 输出: Hello World
与StringBuffer的示例类似,但请注意,如果应用程序是单线程的,那么使用StringBuilder将比使用StringBuffer更高效。