StringBuffer类源码剖析

StringBuffer类源码剖析

一、StringBuffer类的简介

StringBuffer线程安全可变的字符序列。StringBuffer类似于String,但可以被修改。在任何时间点,它都包含一些特定的字符序列,但序列的长度和内容可以通过某些方法调用来更改。StringBuffer可由多个线程安全使用,必要时同步(synchronized)这些方法,以便任何StringBuffer实例上的所有操作都表现得好像它们以某种串行顺序发生,这与所涉及的每个线程进行的方法调用的顺序一致。

注意:每当发生涉及源序列的操作(例如从源序列append或insert)时,此类仅在执行操作的字符串缓冲区上同步,而不在源上同步。请注意,虽然StringBuffer设计为可以安全地从多个线程并发使用,但如果构造函数或追加或插入操作传递了跨线程共享的源序列,则调用代码必须确保操作在操作期间具有一致且不变的源序列视图。这可以通过在操作调用期间保持锁使用不可变的源序列不跨线程共享源序列来满足。

image-20230913152857119

二、StringBuffer类的构造方法

StringBuffer类的构造方法与StringBuilder类的构造方法如出一辙,就不过多解释了。

image-20230913153134690

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

image-20230913153215277

1
2
3
public StringBuffer(int capacity) {
super(capacity);
}

image-20230913153239043

1
2
3
4
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}

image-20230913153258980

1
2
3
4
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}

三、StringBuffer类的方法

与构造函数类似,StringBuffer的所有方法基本都是通过调用父类的相应方法实现的,多出的仅仅是 synchrinized修饰符,重点功能实现在于父类AbstractStringBuilder,参考之前的AbstractStringBuilder类源码剖析。不过需要注意一点,StringBuffer类引入了一个新字段toStringCache,顾名思义该字段就是toString方法的缓存,一旦StringBuffer被修改了,该字段也就失效了。

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

image-20230913153525634

四、StringBuffer类的toString方法

toString方法会检查上一次调用toString的缓存字段是否失效,如果已经失效则重新创建一个字符串(深拷贝),并且toStringCache字段指向该字符串;否则直接返回toStringCache的一个副本(浅拷贝),不过注意的是String是不可变类,共享String的底层字节数组不存在线程安全问题!。

1
2
3
4
5
6
7
8
public synchronized String toString() {
if (toStringCache == null) {
return toStringCache =
isLatin1() ? StringLatin1.newString(value, 0, count)
: StringUTF16.newString(value, 0, count);
}
return new String(toStringCache);
}

之所以说深拷贝还是浅拷贝是有依据的,前者会拷贝底层的字节数组,后者只是引用的指向!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// StringLatin1.newString,深拷贝
public static String newString(byte[] val, int index, int len) {
return new String(Arrays.copyOfRange(val, index, index + len),
LATIN1);
}

public static byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
byte[] copy = new byte[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);

// String的构造方法,浅拷贝
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}

五、StringBuffer类的序列化/反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
java.io.ObjectOutputStream.PutField fields = s.putFields();
char[] val = new char[capacity()];
if (isLatin1()) {
StringLatin1.getChars(value, 0, count, val, 0);
} else {
StringUTF16.getChars(value, 0, count, val, 0);
}
fields.put("value", val);
fields.put("count", count);
fields.put("shared", false);
s.writeFields();
}

private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
java.io.ObjectInputStream.GetField fields = s.readFields();
char[] val = (char[])fields.get("value", null);
initBytes(val, 0, val.length);
count = fields.get("count", 0);
}