String、StringBuffer和StringBuilder的区别
下面从可变性、是否线程安全等方面来对String、StringBuffer、StringBuilder进行比较。
一、可变性
1. String
String 类中使用 final 关键字修饰字符数组来保存字符串。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { private final char value[]; //... }
分析:我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的(final 修饰引用类型变量的情况)。
String 真正不可变有下面几点原因:
1)保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
2)String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。
2. StringBuffer
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法。
二、线程是否安全
1. String
String在Java中是线程安全的。
原因:
1)不可变性:一旦创建,String 对象的内容就不能更改。
2)内存管理:Java 使用字符串常量池来优化内存使用。当多个 String 对象具有相同的值时,它们实际上会引用同一个对象。
在多线程环境中,多个线程可以安全地读取同一个 String 对象而无需同步。由于不可变性,不需要额外的同步机制来保护 String 对象,因此可以提高性能。
2. StringBuilder
StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
3. StringBuffer
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁(synchronized), 所以是线程安全的。这意味着在多线程环境中,StringBuffer 可以安全地被多个线程访问。
部分源码如下:
1 public final class StringBuffer extends AbstractStringBuilder implements Serializable, CharSequence 2 { 3 //... 4 @Override 5 public synchronized StringBuffer append(Object obj) { 6 toStringCache = null; 7 super.append(String.valueOf(obj)); 8 return this; 9 } 10 }
三、是否实现了equals和hashCode方法
1. String
String 类重写了 equals() 和 hashCode() 方法,使得两个 String 对象可以根据其内容进行比较。例如:
1 public class StringExample { 2 public static void main(String[] args) { 3 String str1 = new String("java"); 4 String str2 = new String("java"); 5 6 // 比较两个 String 对象的内容 7 System.out.println(str1.equals(str2)); // 输出: true 8 System.out.println(str1.hashCode() == str2.hashCode()); // 输出: true 9 } 10 }
StringBuilder 和 StringBuffer 没有实现 equals() 和 hashCode() 方法,使用时主要关注字符串的构建和修改。
如果需要比较内容,可以使用 toString() 方法将其转换为 String,然后进行比较。
总结:
String:不可变,适合不频繁修改的字符串,性能较低。
StringBuffer:可变,线程安全,适合多线程环境,性能较好。
StringBuilder:可变,非线程安全,性能最好,适合单线程环境。通常在需要频繁修改字符串时优先选择。
4. 字符串拼接用“+” 还是 StringBuilder?
Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。
4. 总结:
我们在平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer。不过,字符串使用 final 关键字声明之后,可以让编译器当做常量来处理。
原文链接:https://javaguide.cn/java/basis/java-basic-questions-02.html