一.定义
先从类定义来看看:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
......
}
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
......
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
......
}
- 都是用final修饰,不可被继承,即没有子类,可以防止子类覆盖情况的发生。
- String 实现了Comparable<String>。
- StringBuffer 和 StringBuilder 都继承了AbstractStringBuilder类。
- 三者均实现了java.io.Serializable, CharSequence接口。
二、无参数构造函数
1. 首先,看一下String的无参构造函数源代码:
public String() {
this.value = new char[0];
}
- 该函数创建了一个空字符序列(character sequence)对象。由于字符串是不可变的,所以该构造函数没有必要用。
private final char value[];
- this.value = new char[0],初始化一个空char数组value。
- value是一个字符数组。
2. 然后我们看看StringBuilder()源代码:
public StringBuilder() {
super(16);
}
super(16)是父类 AbstractStringBuilder的构造函数:
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
同String一样,value是一个字符数组:
char[] value;
StringBuilder()方法创建了一个大小16的字符数组 ,用于存储字符。
3. 继续看 StringBuffer():
public StringBuffer() {
super(16);
}
StringBuffer的父类也是AbstractStringBuilder,所以同StringBuilder()一样,创建了一个大小16的字符数组。
由以上可以知道:
String()创建一个空字符数组;
而StringBuffer()和StringBuffer()创建一个大小为16的字符数组。
String的字符数组value是final的,而StringBuffer和StringBuffer则不是。
三、带有初始字符串的构造函数
开发中常用的,创建一个指定字符串内容的对象,代码如下:
String s = new String("qqq");
StringBuilder sb = new StringBuilder("qqq");
StringBuffer sb1 = new StringBuffer("qqq");
1. 首先看看new String("qqq")的源代码:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
实际上,就是把参数original字符串对象的字符数组value和hash code直接赋值给新对象。我们已经知道value是字符数组,那么hash是什么呢,先看定义:
private int hash; // Default to 0
hash是字符串的哈希code,默认值是0。那么哈希code计算将在后续说明。
2. 再看看new StringBuilder("qqq")源代码:
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
此处,
- 第一步,初始化数组。super(str.length() + 16)是AbstractStringBuilder构造函数:
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
可以看出,这里创建的字符数组容量=参数str大小的+16。
- 第二步,赋值。append(str)定义如下:
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
赋值时候,调用了父类方法super.append(str):
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;
}
如果str参数是null,则赋值“null”;
如果str参数非null,判断是否有足够的空间容纳str字符串,如不够则扩容,需要最小空间是:已用字符数(count)+str的字符数。
使用str.getChars方法将字符串中的字符复制到字符数组value中。
更新已用字符数count。
3. 最后看看new StringBuffer ("qqq")源代码:
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer构造方法与StringBuilder一样,需要注意的是append方法是加了同步锁synchronized:
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
四、字符数组value的区别
String使用字符数组value存储字符串数据。其定义的value是有final修饰的,代表该变量不可变的,所以String对象也就是不可变的。定义代码如下:
private final char value[];
StringBuffer与StringBuilder也使用字符数组value存储字符串数据。value来自父类AbstractStringBuilder。Value变量是可变的。定义代码如下:
char[] value;
五、线程安全的区别
- String,由于value是final的。一旦创建就不会再被改变了,由于存储的字符串不会被改变。所以一定程度上能使String对象强制变得线程安全了。
- StringBuffer,对调用方法加了同步锁synchronized,所以线程安全的。其中部分方法定义如下:
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
@Override
synchronized StringBuffer append(AbstractStringBuilder asb) {
toStringCache = null;
super.append(asb);
return this;
}
- StringBuilder则没有加同步锁,所以是非线程安全的。
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}