环境
- JDK1.8
01、String
String对象一旦创建就不可改变,String类被final关键字修饰,且String类的成员变量value[]也被final修饰。如下(jdk1.8):
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
//....其它源码
}
02、StringBuffer
StringBuffer创建的字符串对象是是可变的,为什么说是可变呢?
StringBuffer继承AbstractStringBuilder,AbstractStringBuilder中维护着char[]字符数组和count字符数组长度两个成员变量,且都没有通过final关键字修饰。append方法会调用AbstractStringBuilder.append方法,代码如下:
AbstractStringBuilder.append(String str)
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
// 判断长度溢出
ensureCapacityInternal(count + len);
// 复制要append的字符串到 value 数组中,追加到数组末尾
str.getChars(0, len, value, count);
// 增加数组长度
count += len;
return this;
}
看下 String.getChars(...) 方法源码,如下:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
//// 通过 arraycopy 追加到目标字符数组中
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
所以StringBuffer、StringBuilder的可变性是由可变字符数组char[], 数组长度count, System.arraycopy来完成的。
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
值得注意的是StringBuffer是线程安全的,体现在该类的方法上有synchronized关键字修饰。
03、StringBuilder
StringBuilder创建的字符串对象也是可变的,但提供的方法是非线程安全的。适合用于单线程处理大字符串的场景。JDK8中默认使用 StringBuilder 拼接字符串,所以不用显示的使用StringBuilder了,直接用"+"也可以。
04、String.intern()
该方法会从字符串常量池中检查是否有相同值,如果有,则直接使用常量池中的字符串对象,否则就在常量池中创建一个字符串对象并返回。
应用场景:产生大量重复字符串对象的时候,可以通过该方法来节省内存空间。
总结
- String对象是不可变的,StringBuffer、StringBuilder创建的对象是可变的;
- StringBuffer是线程安全的,StringBuilder是非线程安全的;
- 在出现大量重复字符串的时候,考虑一下
String.intern();