先看一下StringBuilder和StringBuffer的构成
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {......} public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {....}
两个类继承了同一个类,也实现了同样的接口,那么导致线程不安全的方法肯定在类内部自身的方法了。
再来看一下他们共同的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; int count;
}
该类有两个重要的成员变量 一个是count,一个是value,count是字符个数,也就是长度。value则为具体的字符数组。
再来看一下常见的append(String str)方法
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; }
讲解一下这个代码,首先数组的长度在创建的时候就是固定的,想一个原先大小为5的数组 想再添加n个元素 做法是创建一个新得5+n长度的数组,先优先放原先的数组,再在数组的长度(count)处开始添加需要添加进来的元素。
String的本质就是char[] ,所以这个方法里的str.getChars()方法不难猜出是将原先字符数组和新传进来的字符数组大小相加,创建一个不小于该长度的新数组,先优先放入旧数组,再根据旧数组的长度作为索引存入新数组,在这个过程里,count有着举足轻重的作用,他标记了需要在新字符数组的哪个索引处开始添加。如果count混乱,最后的值肯定也是不准确的。所以关键点就是方法是否加锁。
StringBuffer: public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } StringBuilder: public StringBuilder append(String str) { super.append(str); return this; }
最后来看一下String.getChars()方法来验证一下以上是否正确
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd > value.length) { throw new StringIndexOutOfBoundsException(srcEnd); } if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); }
/*
* @param value 新加入的数组
* @param srcBegin 新加入的数组起始位置
* @param det 原先数组
* @param destPs 原先数组添加新元素的索引 也就是count
* @param length 新加入数组的加入个数
* @return 新得数组 长度为dst.count() +value.count() ,值为两部分,自身的[dst]和新加入的[value]
* int[] arr={2,3};
* int[] arr2={1,4,5};
* System.arraycopy(arr,0,arr2,1,2);
* 此时 arr2的值为[1,2,3]
* 此处没有违背数组大小的长度无法改变的准则,只是arr2指向了另一个新得数组而已。
*/
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}