简介

StringBuffer和StringBuilder,两者都是可变对象,都继承java.lang.AbstractStringBuilder类,都实现java.io.Serializable和java.lang.CharSequence接口。
最大的区别在于:StringBuffer是线程安全的,而StringBuilder是非线程安全的

下面代码摘自java.lang.StringBuffer

1
2
3
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

下面代码摘自java.lang.StringBuilder

1
2
3
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence


AbstractStringBuilder类

AbstractStringBuilder类封装了StringBuffer和StringBuilder大部分操作的实现。

字符串的内存形态

下面代码摘自java.lang.AbstractStringBuilder

1
2
3
4
5
6
7
8
9
10
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;

StringBuffer和StringBuilder没有具体的成员变量来存储字符串,而是使用继承自AbstractStringBuilder类的成员变量char[] value,因为没有使用final关键字修饰,因此值是可变的。

字符串构造方法

下面代码摘自java.lang.StringBuffer

1
2
3
public StringBuffer() {
super(16);
}

下面代码摘自java.lang.StringBuilder

1
2
3
public StringBuilder() {
super(16);
}

下面代码摘自java.lang.AbstractStringBuilder

1
2
3
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}

当创建一个StirngBuffer或StringBuilder对象时,若不指定容量,则默认创建长度为16的char类型数组

字符串的append操作

下面代码摘自java.lang.AbstractStringBuilder,以入参为String对象为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
// 检查是否char[]数组是否需要扩容
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
// value.length默认长度是16
// minimumCapacity = str.length + 字符串的实际长度
// 若当前字符串数组长度不足最小应分配的长度,则将重新创建一个长度的char[]数组
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}

字符串的insert操作

下面代码摘自java.lang.AbstractStringBuilder,以入参为String对象为例

1
2
3
4
5
6
7
8
9
10
11
12
public AbstractStringBuilder insert(int offset, String str) {
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
if (str == null)
str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
str.getChars(value, offset);
count += len;
return this;
}

假设执行如下代码:

1
2
StringBuffer sb = new StringBuffer("abghij");
sb.insert(2, "cdef");

字符串的delete操作

下面代码摘自java.lang.AbstractStringBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}

实际上的操作是字符串数组拷贝,假设执行如下代码:

1
2
StringBuffer sb = new StringBuffer("abcdefghij");
sb.delete(2, 6);


StringBuffer类

为什么是线程安全的

线程安全是指多线程操作同一个对象,不会出现同步等问题。StringBuffer类中,使用了大量的synchronized关键字来修饰方法。
摘取java.lang.StringBuffer部分使用synchronized关键字修饰的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}

transient关键字

摘自Java Language Specification, Java SE 7 Edition, Section 8.3.1.3. transient Fields

Variables may be marked transient to indicate that they are not part of the persistent state of an object.

在Java中,transient关键字用来指出哪些成员变量不应该被序列化。值得注意的是:

  • 序列化针对的是对象,而不是类;
  • static修饰的变量,本身是隐式的transient,同时静态变量是属于类层次,不能被序列化;
  • transient只能用于修饰成员变量,不能修饰本地变量,不能修饰方法和类。

StringBuffer类中,有一个成员变量

1
2
3
4
5
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;

toStringCache这个成员变量,从命名上看,猜测是为了用于toString()方法而做的字符串缓冲。可见,如果是为了做缓冲,确实没必要在StringBuffer对象中持久化。

toString的操作

下面代码摘自java.lang.StringBuffer

1
2
3
4
5
6
7
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}

toStringCache获得实际长度的字符串数组,并创建一个String对象


参考材料

Java SE java.lang.StringBuffer
Java SE java.lang.StringBuilder
Java transient关键字