本文介绍有关String、StringBuilder、StringBuffer的区别。
String:
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** 有参构造方法 */
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
在String类中,当我们创建String对象时,会将参数的value与hash属性进行传递,如当我们创建了如下的字符串对象时,
String a = new String("12345");
会将"12345"中的value与hash赋值给当前String对象(this代表当前对象)的char类型数组与int类型hash值(hash默认为0)。
因为数组value被final修饰,所以一旦String创建,不能改变。
另外字符串创建方式不同,创建的对象个数也不相同。
1.如果以new String("123456")这种方式创建对象则会创建两个对象,一个在堆内存中,一个在方法区的字符串常量池中;
2.如果以String s = "123456"这种方式声明,则只会在方法区常量池中创建一个对象。
StringBuilder:
StringBuilder继承了抽象类AbstractStringBuilder,当创建StringBuilder对象时,StringBuilder调用了父类中的构造方法,参数默认是16。父类构造方法则创建一个char类型数组,大小为16。
/** StringBuilder的无参构造方法,调用了AbstractStringBuilder的构造方法,参数默认是16 */
public StringBuilder() {
super(16);
}
/** AbstractStringBuilder的构造方法,创建一个char类型数组 */
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
且这个char数组没有被final修饰,所以可变,我们可以使用append方法进行字符串的追加,StringBuilder中的append方法也是继承了父类中的append方法,StringBuilder默认容量是16,超过容量时会自动扩容,扩容为原容量的2倍+2。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/** AbstractStringBuilder中的append方法 */
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
/** 此处要保证AbstractStringBuilder的容量,必要时会进行扩容操作,扩容时调用方法
* ensureCapacityInternal(int minimumCapacity),其中minimumCapacity为原长度与新追加
* 的字符串长度之和,即minimumCapacity = count + len。
*/
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
/** 扩容的方法,当传入的参数(新的容量大小)大于原来的容量时,进行扩容,调用
* expandCapacity(int minimumCapacity)。
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/** 扩容后的容量为原容量的的2倍+2,并进行数组的拷贝。 */
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
/** 在要输出StringBuilder中的字符串时使用该方法,该方法继承自Object,根据StringBuilder中的
* value与count创建了一个新的字符串对象并返回
*/
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}
StringBuffer:
StringBuffer与StringBuilder类似,最大的区别是,StringBuffer是线程安全的,因为使用了同步机制(synchronized对象锁),StringBuilder不是线程安全的。
/** StringBuffer继承了AbstractStringBuilder */
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* 声明一个字符串缓冲区,在调用toString方法时会使用到
*/
private transient char[] toStringCache;
/** StringBuffer的构造方法与StringBuilder相同 */
public StringBuffer() {
super(16);
}
/** StringBuffer的append方法与StringBuilder基本相同,但是使用了synchronized关键字,加上了
* 对象锁,保证了线程安全。
*/
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
/** 在输出StringBuffer时使用该方法,将StringBuffer中的字符串拷贝到字符串缓冲区
* toStringCache,并根据toStringCache创建String对象并返回。
*/
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
}
总结:
String是一种引用数据类型,一旦创建不可改变。
StringBuilder相当于是一个字符串缓冲区,使用append方法进行字符串的追加,还有其他方法可对字符串进行操作,也就是说字符串可变,但不是线程安全的,可自动扩容,扩容后容量为原容量的2倍+2。
StringBuffer与StringBuilder基本上是相同的,但是StringBuffer是线程安全的,因为使用了同步机制(toString与append方法),有一个字符串缓冲区toStringCache(char类型数组),可自动扩容,扩容大小与StringBuilder相同。
StringBuilder与StringBuffer在使用时可先预估存储字符的容量,采用初始化容量的方式创建,可以省略扩容过程(底层数组的拷贝)。