首先咱们来看两个例子:
第一种情况报错了,但是第二种没有,同样是定义为final的对象,而String不能改变,但是StringBuilder却可以通过append改变,关于这个final关键字实际上它所谓的不可改变只是说我这个变量名对应着这个变量不可改变,这个变量名不能去对应别的变量,但是实际上存在于堆空间的变量是可以改变的,但是为什么定义为final的String拼接就不可以了呢?
原因很简单:
String这个类在jdk中定义的时候就是final的,这个类不能被继承改动,由于它是final的,所以我们不能改变它,那么就意味着在拼接字符串的时候我们直接产生新的String对象,那么就违背了这个final关键字,所以报错了
好,接下来说重点,为什么我们在进行大量字符串拼接的时候为什么不会使用String这个原因上面已经说了,因为会不断产生新对象,对性能不是很好,那么问题来了,为什么要使用StringBuffer/StringBuilder呢?它们效率就快些吗?带着各位看看jdk源代码来看看为什么它们效率更好.
使用StringBuffer/StringBuilder来拼接字符串我们用的都是append方法,直接看看这个方法.
(StringBuffer和StringBuilder的区别在于一个是线程安全的一个不是,StringBuffer线程安全的原因是因为它的方法上加上了synchronized关键字).
关于这个toStringCache它是一个类型为char数组的缓存变量,目前不用太在意它.重点是这个super.append(str)这行代码,直接调用父类的append方法,那就看看它爹,
Stringbuffer和Stringbuilder都继承于AbstractStringBuilder,直接来看看这个类的append方法
一行一行的解释吧,第一行和第二行:如果拼接的String是空就直接拼接一个null的字符串
ensureCapacityInternal方法
意思就是如果这个被拼接的字符串的长度比原本的字符串的长度还要长的话就执行value = Arrays.copyOf(value,newCapacity(minimumCapacity));那就继续看这个newCapacity方法
private int newCapacity(int minCapacity) {
// overflow-conscious code
//定义个int 值为原本字符串的长度*2然后+2
int newCapacity = (value.length << 1) + 2;
//如果这个int变量减去要拼接的字符串长度要小于0,那么这个字符串的长度就等于要拼接字符串的长度
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
//如果这个int变量小于或等于0 或者Integer的最大数值减去8(MAX_ARRAY_SIZE)减去这个int变量要小于0(也就是要比MAX_ARRAY_SIZE大的话)
//那么就返回hugeCapacity方法(最大返回这个MAX_ARRAY_SIZE ),否则的话返回这个定义的Int变量
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
Arrays.copyof我就不多说了,复制数组的方法,代表着将原来存放数据的char数组长度又加上后一个参数.
回到append方法
重点就是这个str.getChars(0, len, value, count);这个方法的意思就是:
将我自己(str)复制到value这个数组中(value就是实际存放字符的数组),后面的count是字符串的长度,拼接了嘛,肯定加上这个长度,然后返回自己