String的不可变性
String的底层是一个不可变的字符数组。
private final char value[];
如果发生变化,即使长度一致,也要更改内存地址。
String s1 = "abc";
String s2 = s1;
s2 = "mbc";
//结果为false
System.out.println(s2 == s1);
String对象建立后,如果再增加或减少,就需要新建一个对象,也就是变更内存地址。这样,使用String类拼接字符串,就是新建一个String对象。举例:
String s1="a";
for (int i=0; i<10;i++) {
s1+=i;
}
这里总共新建了12个对象,因为建s1的时候现在内存里新建了字符’a’,再新建字符串"a",在循环里又新建了10个对象,如果是StringBuilder,那么就只有2个。
StringBuilder的可变性
原理:
StringBuilder的无参构造器的默认容量为16。
public StringBuilder() {
super(16);
}
StringBuilder的有参构造器在默认容量16的基础上加上参数的长度。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
StringBuilder的append方法调用父类AbstractStringBuilder的append方法,返回的是当前对象,也就是说更改StringBuilder后仍然是原对象。
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
AbstractStringBuilder用字符数组value来存储变量,用count来记录数组内有多少字符。
char[] value;
int count;
append方法先调用ensureCapacityInternal方法保证value没有越界,再调用String的getChars方法将新的元素添加到value数组里。
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方法利用System.arraycopy方法将新增的字符串复制到value数组的末尾。
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);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
扩容原理:
minimumCapacity就是count加上新增字符串长度的和,如果minimumCapacity减value数组长度大于0,说明容量不够。用Arrays.copyOf方法来对数组扩容,
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
新的容量是原有长度乘2再加2,如果还是不够,就把长度设为minCapacity。位移运算可以提升性能,但可能会把容量变成负数,所以在return的时候需要检查newCapacity是不是小于等于0。在return的时候,还要检查新的容量是否越过Integer的边界,如果越界调用hugeCapacity方法。
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
MAX_ARRAY_SIZE属性是Integer最大长度减8。如果原有的容量超过Integer的边界,直接抛异常。如果没有越界,判断原有的容量比MAX_ARRAY_SIZE大,就返回原有的容量,否则返回MAX_ARRAY_SIZE。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
StringBuffer:
StringBuffer也是可变的。StringBuffer的底层在StringBuilder的基础上加上了synchronized关键字,也就是说StringBuffer的方法都是同步的,其余一样。
public synchronized int length() {
return count;
}