共同点
都是final类不可以被继承,都是现实了Serializable, Comparable, CharSequence接口
作用上的共同点则是都是用来存储字符串
区别
String
在申明内容是字面值常量每次对字符串进行拼接其实是对新建了一个对象,如果是有大量的字符串拼接则会创建大量的对象这样就效率低下了,适用于内容较短的且不需要多次拼接的场景
StringBuilder
@Override
@IntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
//检查容量,不够时进行扩容
ensureCapacityInternal(count + len);
//填空byte[]数组保存数据
putStringAt(count, str);
count += len;
return this;
}
//对byte[]数组进行扩容
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
if (minimumCapacity - oldCapacity > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity) << coder);
}
}
从上面代码上可以发现StringBuilder是通过byte[]存储数据并且动态改变byte的容量看,在代码中没有对数据的拼接也只是改变byte[]的容量并没有重新创建对象,从这里就可以看出StringBuilder在数据拼接时效率是比String高的,同是在代码中也没有看到synchronized关键字所以StringBuilder是线程不安全的
StringBuffer
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
//检查容量,不够时进行扩容
ensureCapacityInternal(count + len);
//填空byte[]数组保存数据
putStringAt(count, str);
count += len;
return this;
}
//对byte[]数组进行扩容
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
if (minimumCapacity - oldCapacity > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity) << coder);
}
}
可以看到StringBuffer和StringBuilder的实现基本类似在字符串凭借是都是改变byte的容量并填充数据唯一的区别就是使用synchronized修饰了方法所以在StringBuffer是线程安全的遵循有得必有失的理念所以StringBuffer效率低于StringBuilder