本帖最后由 上海分校-小影 于 2018-4-10 11:05 编辑
很多Java初学者不理解为什么String长度不可变,StringBuilder长度可变的原理,在这里我们一起来看一下. 1.String长度不可变 String的有一个成员变量char value[],String保存的字符就是保存在这里,下面是String部分构造方法: [AppleScript] 纯文本查看 复制代码 ` /** The value is used for character storage. */
private final char value[];
public String() {
this.value = new char[0];
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}` 其中char value[]是用final修饰的,所以String类是不可变的,即字符串常量,所以每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。 2. StringBuilder 源码解析StringBuilder继承自AbstractStringBuilder抽象类.成员变量是一个char[],没用final修饰,StringBuilder的操作基本都是围绕这个数组进行: `char[] value;//在AbstractStringBuilder中定义 该字符数组在StringBuilder初始默认容量是16
[AppleScript] 纯文本查看 复制代码 Java代码 收藏代码
//默认构造方法
public StringBuilder() {
super(16);//默认容量
}
//指定容量构造方法
public StringBuilder(int capacity) {
super(capacity);
}
//指定初始字符串构造方法
public StringBuilder (String str) {
super(str.length() + 16); //初始容量为字符串长度+16
append(str);
} `
成员变量字节数组value初始化完成。 append系列重载方法
AbstractStringBuilder中的append系列重载方法(StringBuilder中的append方法通过super直接调用该系列方法),是其核心方法,可以处理处理所有的基础类型(比如boolean、int、long、double等)、引用类型的拼接(String、Object、AbstractStringBuilder自身等) 这里以用得最多的append(String)进行讲解:
[AppleScript] 纯文本查看 复制代码 [mw_shl_code=applescript,true] public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();//注意如果为str为null,会拼接一个"null"字符串
int len = str.length();
ensureCapacityInternal(count + len);//判断容量是否足够,如不够先扩容,再copy到新扩容后的数组
str.getChars(0, len, value, count);//把str copy到 字符数组中
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;//扩容两倍 + 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); //把老数据复制到扩容后的新数组。
} [/mw_shl_code] StringBuilder实际上就是把待拼接的字符串,放到自己的字符数组,如果字符数组的容量不够,需要进行扩容。具体操作是新创建一个字符数组(容量为老数组的2倍+2),再把老数组中的内容copy到新数组。这就是为什么在拼接大量字符串(拼接后超过长度16),最好采用指定容量的方式创建StringBuilder(或StringBuffer),防止拼接过程中不断扩容带来的性能消耗。
|