StringBuilder和StringBuffer和String的区别
文章目录
StringBuilder、StringBuffer和String的区别
拼接字符串:
StringBuilder
和StringBuffer
拼接字符串,直接可以输出拼接后的结果
String
不接收字符串,返回的还是原来的空字符串;接收为str2
则返回拼接后的结果
// StringBuilder声明的stringbuilder
StringBuilder stringbuilder = new StringBuilder();
stringbuilder.append("aaa");
System.out.println(stringbuilder); // aaa
// StringBuffer声明的stringbuffer
StringBuffer stringbuffer = new StringBuffer();
stringbuffer.append("aaa");
System.out.println(stringbuffer); // aaa
// String声明的str1
String str1 = new String();
str1.concat("aaa");
String str2 = str1.concat("ccc");
System.out.println(str1); // 空字符串
System.out.println(str2); // ccc
String
不可变字符串,不可以改变长度和内容
1、String字符串底层使用final
修饰value[]
数组,所以不可以改变内容和新的指向
// String 底层存储字符串源码
private final char value[];
2、String
类中的所有方法,返回值是new
一个新的字符串
例如:concat("字符串")
拼接字符串,可以看到return
返回的是new String()
,没有改变当前的字符串
// concat() 方法底层源码
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
StringBuilder和StringBuffer
可变字符串,可以改变长度和内容
1、String
字符串底层,没有使用final
修饰value[]
数组,所以vlaue
只是一个普通字符数组
// StringBuilder和StringBuffer 底层存储字符串源码
char[] value;
2、StringBuilder
和StringBuffer
类中的所有方法,返回值是this
当前字符串
例如:append("字符串")
拼接字符串,返回值都是this
,代表返回的值都是当前的字符串
// StringBuffer的append()方法底层源码
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
// StringBuilder的append()方法底层源码
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilder和StringBuffer的区别
区别 | StringBulider | StringBuffer |
---|---|---|
线程安全性 | 不安全 | 安全 |
性能 | 高 | 低 |
StringBuffer 的线程安全性
StringBuffer
被设计为线程安全的类。它的所有公开方法(如append()
, insert()
, delete()
, 等等)都是使用synchronized
关键字修饰的。这意味着在任意时刻,只有一个线程能够执行StringBuffer
的某个方法。这种同步机制确保了多线程环境下对StringBuffer
对象的并发访问不会导致数据不一致或损坏。
例如,如果有两个线程同时尝试向同一个StringBuffer
对象追加数据,synchronized
关键字会确保一个线程完成其操作之前,另一个线程不会开始其操作。这种机制虽然保证了线程安全,但也带来了性能上的开销,所以相对性能低。
StringBuilder 的非线程安全性
StringBuilder
没有使用synchronized
关键字来修饰其方法。这意味着在多线程环境下,多个线程可以同时访问和修改同一个StringBuilder
对象,而不需要等待其他线程完成其操作。虽然这提高了性能,但也带来了线程安全性的问题。
如果在多线程环境中使用StringBuilder
,并且多个线程同时修改同一个StringBuilder
对象,就可能导致数据不一致或损坏。例如,一个线程可能正在向StringBuilder
中追加数据,而另一个线程可能同时也在进行追加操作,这可能导致数据被覆盖或混淆。
扩容方式
因为StringBuffer
和StringBuilder
继承的是同一个类AbstractStringBuilder
并且append
方法都调用的是父类的append
方法,所以这里直接看父类的扩容方式
// 源码:
// AbstractStringBuilder父类中的append方法
public AbstractStringBuilder append(String str) {
if (str == null) // 判断添加的内容是否为空
return appendNull(); // 调用添加null方法,将null追加到字符串末尾
int len = str.length(); // 追加字符串的长度
ensureCapacityInternal(count + len); // 扩容
str.getChars(0, len, value, count);
count += len;
return this; // 返回当前字符串
}
// ensureCapacityInternal()方法
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) { // 如果需要的长度大于现有的长度
value = Arrays.copyOf(value,
newCapacity(minimumCapacity)); // 进行扩容,获取新的长度
}
}
// newCapacity()方法 扩容机智的底层
private int (int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2; // 先扩大原长度的2倍+2
if (newCapacity - minCapacity < 0) { // 如果扩大2n+2后,还是不足需要的长度
newCapacity = minCapacity; // 直接将需要的长度赋值给新扩容的长度
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
- 当向
StringBuilder
或StringBuffer
中追加内容导致当前容量不足时,它们会进行扩容。 - 扩容的新容量通常是当前容量的两倍再加一个额外的值2。
- 扩容时,会创建一个新的字符数组,并将原有内容复制到新数组中,然后更新内部引用以指向新的字符数组。