本文主要以简单的String/StringBuilder/StringBuffer操作来看这三个类的实现原理。
什么简单操作呢?那就是StringBuilder与StringBuffer的append() && toString()两个方法。
示例代码如下:
public class TestStringBuffer_Builder {
public static void main(String[] args) {
TestStringBuffer_Builder testStringBuffer_builder = new TestStringBuffer_Builder();
testStringBuffer_builder.testStringBuilder();
testStringBuffer_builder.testStringBuffer();
}
public void testStringBuilder()
{
String strName = new String("JackMa");
String strCountry = new String("China");
StringBuilder stringBuilder = new StringBuilder(64);
stringBuilder.append("Name:").append(strName).append("\n");
stringBuilder.append("Country:").append(strCountry).append("\n");
System.out.print(stringBuilder.toString());
}
private void testStringBuffer()
{
String strName = new String("JackMa");
String strCountry = new String("China");
StringBuffer stringBuffer = new StringBuffer(64);
stringBuffer.append("Name:").append(strName).append("\n");
stringBuffer.append("Country:").append(strCountry).append("\n");
System.out.print(stringBuffer.toString());
}
}
以上的demo中,涉及到了String的构造,StringBuilder & StringBuffer的构造、append与toString。我们分别研究这几个方法,来了解其内部的实现原理。
一、String
代码中首先构造了String。
先简单看看String内部发生了什么:
public final class String
{
private final char value[];
private int hash;
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
}
可以看的出来,String中的数据都是保存在数组char value[]中。
对于String的concat(拼接)过程可以看出,最后生成了一个新的String对象作为拼接的结果。
二、StringBuilder
先看看StringBuilder中的方法:
public class StringBuilder
extends AbstractStringBuilder
{
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
父类:
public abstract class AbstractStringBuilder
{
char[] value;
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
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;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
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);
}
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}
StringBuilder中也是用数组进行数据保存。相比于String的concat操作,StringBuilder在拼接字符串的过程始终是一个对象在操作,变化的是StringBuilder内部的数组若容量不够,则进行扩充。
扩容算法中,在拼接字符串不长的情况下,容量通常扩为2倍。若2倍不足,则扩为需要的大小。
在toString方法中,使用StringBuilder内部的value数组构造一个新的的String对象,并返回。
三、StringBuffer
public final class StringBuffer
extends AbstractStringBuilder
{
private transient char[] toStringCache;
public synchronized StringBuffer append(String str)
{
toStringCache = null;
super.append(str);
return this;
}
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
}
StringBuffer中,也是用数组进行字符串数据保存。不同于StringBuilder,StringBuffer中的操作字符串方法是同步的,因此属于线程安全类。
同时用了transient char[] toStringCache来缓存数据。在调用toString时,将字符串内容保存进toStringCache, 且在修改StringBuffer时(例如append、insert、delete等字符操作),清空该缓存。
若字符串无修改,在第二次调用toString时直接将缓存内容返回,从而提升字符转换效率。
这里面涉及到一个点:用于缓存字符内容的数组toStringCache是用transient修饰,直接访问内存,从而实现线程间的可见性。具体内容可进一步了解关键字transient。