前言

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

String 就不提了.

String 分析

StringBuffer 分析

append 的真正实现

先看 StringBuffer.

查看 append(String str).

 @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

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

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

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

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

我们查看一下 AbstractStringBuilder.

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 实现的方法…

    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()…

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 : 解决…

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

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 :

没错,写法没问题…

当执行

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

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

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 后面…

噢耶!

是我搞混了…

怎么实现可变


    /**
     * 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() 时,有个

ensureCapacityInternal(count + len);//判断溢出
/**
 * 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,任意查看个方法…

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 封装等。