可变性
String是不可变的
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder类中也是使用字符数组保存字符串,但没有使用final和private关键字修饰,最关键的是AbstractStringBuilder类还提供了很多修改字符串的方法,例如append()方法
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
...
}
线程安全性
String中的对象是不可变的,也可以理解为常量,线程安全
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法
StringBuilder没有对方法加同步锁,所以是非线程安全的;StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的
性能
每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象
StringBuilder与StringBuffer每次对对象本身进行操作,而不是生成新的对象并改变对象引用
相同情况下使用StringBuilder与相比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的风险
使用总结
操作少量的数据:String
单线程操作字符串缓冲区下操作大量数据:StringBuilder
多线程操作字符串缓冲区下操作大量数据:StringBuffer
String为什么是不可变的
1.保存字符串的数组被final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法
2.String类被final修饰导致其不能被继承,进而避免了子类破坏String的不可变性
字符串拼接用“+”还是StringBuilder
Java语言本身并不支持运算符重载,“+”和“+=”是专门为String类重载过的运算符,也是Java中仅有的两个重载过的运算符
代码:
String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;
对应的字节码:
字符串对象通过“+”的字符串拼接方式,实际上是通过StringBuilder调用append()方法实现的,拼接完成后调用toString()得到一个String对象
若在循环内使用“+”进行字符串的拼接,存在比较明显的缺陷:编译器不会创建单个StringBuilder以复用,从而导致创建过多的StringBuilder对象
代码:
String[] arr = {"he", "llo", "world"};
String s = "";
for (int i = 0; i < arr.length; i++) {
s += arr[i];
}
System.out.println(s);
对应的字节码:
若直接使用StringBuilder对象进行字符串拼接,就不会存在这个问题
代码:
String[] arr = {"he", "llo", "world"};
StringBuilder s = new StringBuilder();
for (String value : arr) {
s.append(value);
}
System.out.println(s);
对应的字节码:
Java9新特性
存储方式
在Java9之后,String、StringBuilder与StringBuffer的实现改用byte数组存储字符串
新版的String支持两个编码方案:Latin-1和UTF-16,若字符串中包含的汉字没有超过Latin-1可表示范围内的字符,就会使用Latin-1作为编码方案,Latin-1编码方案下,byte占一个字节,char占两个字节,byte相较char节省一半的内存空间
JDK官方表示绝大部分字符串对象只包含Latin-1可表示的字符
public final class String implements java.io.Serializable,Comparable<String>, CharSequence {
// @Stable注解表示变量最多被修改一次, 称为“稳定的”
@Stable
private final byte[] value;
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
byte[] value;
...
}
字符串拼接
使用“+”进行字符串拼接会产生大量的临时对象的问题在JDK9中得到解决,在JDK9中,字符串相加“+”改为用动态方法makeConcatWithConstants()实现,而不是大量的StringBuilder
JDK9之后,可以放心使用“+”进行字符串拼接了