String,StringBuffer,StingBuilder

前言

1
public final class String implements java.io.Serializable, Comparable<String>,CharSequence {}
1
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {}
1
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {}

String 就不提了.

String 分析

StringBuffer 分析

append 的真正实现

先看 StringBuffer.

查看 append(String str).

1
2
3
4
5
6
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}

首先,注意关键字 synchronized,这说明这个方法是线程安全的,需要执行同步.

事实上,StringBuffer 就是一个线程安全的类.

其次,可以看到该方法的实现实际上是调用了父类的方法(super.append()).

StringBuffer 和 StringBuilder 都继承了 AbstractStringBuilder,实际上存储数据及相应操作是由 AbstractStringBuilder 这个类实现的.

我们查看一下 AbstractStringBuilder.

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

这是个抽象类,不能被实例化…(看名字也知道…)

Appendable 接口,看名称,应该是允许对这个类执行 append 操作的意思吧…不扯远了.

看看在 AbstractStringBuilder 实现的方法…

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

实际上调用 String 的 getChars() 来实现…

最后毕竟直接返回 this,那么这里的效果应当是把 str 复制放到 AbstractStringBuilder 维护的 value 后面…

当然,AbstractStringbuilder 也有这个 getChars() 方法,StringBuffer 和 StringBuilder 都继承了.

查看一下 getChars()…

1
2
3
4
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
...//异常判断
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

疑问

arraycopy 实现数组间的复制,把 value 复制接到 dst 后面…

没看懂啊…

这里绕过来,似乎根本没地方用到 append(String str) 中的 str 啊…

效果反正应该是把 str 复制到 value 上…

看,getChars() 是把 value 复制到 dst,而 dst 不就是上面传入的 value 吗???

PS : 解决…

自己调试跑了一下,发现源码中其实是 :

1
2
3
4
5
6
7
8
9
10
11
public void getChars(int var1, int var2, char[] var3, int var4) {
if (var1 < 0) {
throw new StringIndexOutOfBoundsException(var1);
} else if (var2 > this.value.length) {
throw new StringIndexOutOfBoundsException(var2);
} else if (var1 > var2) {
throw new StringIndexOutOfBoundsException(var2 - var1);
} else {
System.arraycopy(this.value, var1, var3, var4, var2 - var1);
}
}

这里的 this,就是 append(String str) 的 str…

把 str 的字符串数组复制到了 var3,也就是 StringBuffer 中的字符串数组…

这就说得通了嘛…


不过怎么回事啊,我下载的这份源码有问题嘛,还是那种写法其实没问题,只不过有歧义??

Ps :

没错,写法没问题…

当执行

1
str.getChars(0, len, value, count);

getChars() 方法体中的 this,就是 调用这个方法的对象…

1
2
public void getChars(int var1, int var2, char[] var3, int var4) {
}

里面的 value 也自然是这个对象的 value…

很基础的一个概念啊…

可见易读的代码是多么重要…

[2019-02-03-15:03]

之前有点乱,没理清楚.重新来…

上面似乎仍然理解复杂了…

对于 append(String str),首先这里的 str.getChars() 是 String 中的方法.

在调用 str.getChars(0, len, value, count); 时,方法体中的 value 其实是 String 维护的 value.

所以这里的效果就是 : 把 str 的 value 复制到调用 append 方法的 StringBuffer 实例维护的 value 后面…

噢耶!

是我搞混了…

怎么实现可变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}

这里能看到,始终给予了 StringBuffer 维护的 value 16个字符的空间…

回到上面,在执行 AbstractStringBuilder 的 append() 时,有个

1
ensureCapacityInternal(count + len);//判断溢出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* For positive values of {@code minimumCapacity}, this method
* behaves like {@code ensureCapacity}, however it is never
* synchronized.
* If {@code minimumCapacity} is non positive due to numeric
* overflow, this method throws {@code OutOfMemoryError}.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}

这里就是保证字符串能够正常扩大…

如果要添加的字符串长度大于预留空间,就重新申请空间,复制…

StringBuilder

对应的,看看 StringBuilder,任意查看个方法…

1
2
3
4
public StringBuilder append(String str) {
super.append(str);
return this;
}

super 不提了,这里没有用 synchronized…

所以现在可以总结一个点先…

对比

String : 线程不安全,不可变(看起来变了实际上是新建了对象)

StringBuffer : 线程安全,可变

StringBuilder : 线程不安全,可变

参考

浅谈 Java 字符串(String, StringBuffer, StringBuilder)

使用场景

[来源参考]

使用 String 类的场景:在字符串不经常变化的场景中可以使用 String 类,例如常量的声明、少量的变量运算。

使用 StringBuffer 类的场景:在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用 StringBuffer,例如 XML 解析、HTTP 参数解析和封装。

使用 StringBuilder 类的场景:在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,则可以考虑使用 StringBuilder,如 SQL 语句的拼装、JSON 封装等。