目录
String为何不可变,StringBuilder和StringBuffer为何可变
StringBuffer为何线程安全的,而StringBuilder为何非线程安全
String、StringBuilder和StringBuffer都是用来处理字符串的类,底层都是通过char[]数组实现的。(jdk1.8及之前)
String是不可变的(线程安全的),StringBuilder和StringBuffer是可变的。StringBuffer是线程安全的,而StringBuilder是非线程安全的。具体如下:
String对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象;而StringBuffer和StringBuilder对象的值是可以被修改的;
StringBuffer几乎所有的方法都使用synchronized实现了同步,线程比较安全,在多线程系统中可以保证数据同步,但是效率比较低;而StringBuilder 没有实现同步,线程不安全,在多线程系统中不能使用 StringBuilder,但是效率比较高。
如果我们在实际开发过程中需要对字符串进行频繁的修改,不要使用String,否则会造成内存空间的浪费;当需要考虑线程安全的场景下使用 StringBuffer,如果不需要考虑线程安全,追求效率的场景下可以使用 StringBuilder。
下面我们从三个维度:可变性、线程安全性、性能来分析区别。
String部分源码
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
//。。。。。。。。。。。。。。。。。。
}
String类被声明为final,这意味着它不能被继承。那么他里面的方法就是没办法被覆盖的。
用final修饰字符串内容的char[]?(从JDK 1.9开始,char[]变成了byte[]),由于该数组被声明为final,一旦数组被初始化,就不能再指向其他数组
String类没有提供用于修改字符串内容的公共方法。例如,没有提供用于追加、删除或修改字符的方法。如果需要对字符串进行修改,会创建一个新的String对象。所以说String是不可变的。
与String不同StringBuilder和StringBuffer底层封装的char[]并没有用final修饰,这意味着它是可以修改的。
StringBuilder部分源码
public final class StringBuilder
extends AbstractStringBuilder
implements Serializable, CharSequence
{
//。。。。。。。。。。。。。。。。。
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
//。。。。。。。。。。。。。
}
StringBuffer部分源码
public final class StringBuffer
extends AbstractStringBuilder
implements Serializable, CharSequence
{
//。。。。。。。。。。。。。。。。
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
//。。。。。。。。。。。。。。。。。。。。。。。
}
StringBuffer是线程安全的,它的方法都使用了synchronized关键字进行同步。在每个方法内部,通过加锁(synchronized)来确保在多线程环境下的安全访问。这意味着当一个线程访问StringBuffer的方法时,其他线程需要等待锁释放后才能执行相应的方法。因此,StringBuffer适用于多线程环境下对字符串的修改操作。
//StringBuffer部分源码
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
而StringBuilder则没有使用synchronized关键字进行同步,它是非线程安全的。这样可以提高性能,因为不需要进行锁的获取和释放操作。但是,在多线程环境下,如果有多个线程同时访问和修改StringBuilder对象,可能会导致数据不一致或出现竞态条件的问题。因此,StringBuilder适用于单线程环境下对字符串的修改操作。
//StringBuilder部分源码
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
String:由于String是不可变的,每次对String进行修改操作时,都会创建一个新的String对象。这可能导致频繁的对象创建和垃圾回收,影响性能
StringBuilder:由于StringBuilder是可变的,它使用可变的字符数组存储字符串,不会每次都创建新的对象。在需要频繁进行字符串拼接、替换等操作时,使用StringBuilder可以提高性能
StringBuffer:与StringBuilder类似,StringBuffer也是可变的,但是它是线程安全的。在多线程环境下,由于同步机制的存在,StringBuffer的性能可能会稍低于StringBuilder
String:适用于不需要频繁修改字符串内容的场景。由于String是不可变的,每次对String进行拼接、替换或者修改操作时,都会创建一个新的String对象,这样可能会导致频繁的对象创建和垃圾回收,影响性能。因此,如果字符串内容不需要改变,或者只需要读取字符串的值,可以使用String。
对于一些常量字符串或者字面量,例如日志输出时的提示信息、固定格式的输出等,由于它们的值是固定的,不需要做任何修改,因此可以使用String。
在某些业务逻辑中,需要对字符串进行一些操作,但这些操作的结果不会影响原始字符串。例如,从数据库中查询到的数据如果需要展示给用户,通常不会被修改,这种场景下可以使用String。
StringBuilder:适用于单线程环境下需要频繁修改字符串的场景。由于StringBuilder是可变的,它使用可变的字符数组存储字符串,不会每次都创建新的对象。因此,在需要频繁进行字符串拼接、替换等操作时,使用StringBuilder可以提高性能。
在开发Web应用时,需要将多个字符串拼接成一个完整的HTML页面,这种场景下可以使用StringBuilder。
当需要从文件中读取数据,并进行一系列复杂的字符串操作时,可以使用StringBuilder。
StringBuffer:适用于多线程环境下需要频繁修改字符串的场景。与StringBuilder类似,StringBuffer也是可变的,但是它是线程安全的,内部的方法都使用了synchronized关键字进行同步。这意味着在多线程环境下,多个线程可以安全地同时访问和修改StringBuffer对象。因此,如果在多线程环境下需要频繁进行字符串操作,应该使用StringBuffer来确保线程安全。
Web服务器需要同时处理多个客户端请求,这种场景下可以使用StringBuffer来确保线程安全。
一个多线程的任务,需要将多个线程的执行结果拼接成一个完整的字符串,可以使用StringBuffer。