append方法的追踪(课堂复现)
起
将原有的字符串扩展,或是进行不同字符串之间的拼接,往往很容易想到在两个字符串之间直接用“+”号运算来达到目的,而在java中这样的方法会残留些不必要的内容,而推荐使用Stringbuilder或StringBuffer中的append()方法,以下就以StringBuffer为例
public class FeiWuTest {
public static void main(String[] args) {
String s1 = "Wo ";
String s2 = s1 + "shi ";
String s3 = s2+"Fei";
String s4 = s3+"wu";
System.out.println(s4);
//这里会比下面多存在"Wo shi "、"Wo shi Fei"从而浪费空间
StringBuffer sb1 = new StringBuffer("Wo ");
sb1.append("shi ");
sb1.append("Fei");
sb1.append("wu");
System.out.println(sb1);
}
}
以第一个append为例
首先构造的一个sb1这个对象
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
调用父类构造
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
即构建出一个长度为输入字符串的长度+16,这里我写入的是”Wo “,所以对用传入的长度为19.(如果没有传入字符串,对应构造方法,传入的长度为16)
进入 append()方法,这里是构建第一个对象sb1时append
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
其中
private transient char[] toStringCache;
super.sppend(str);调用父类AbstractStringBuilder的append方法,其中str="Wo "
public AbstractStringBuilder append(String str) {
if (str == null)//flase
return appendNull();
int len = str.length();//len=19
ensureCapacityInternal(count + len)
str.getChars(0, len, value, count);
count += len;
return this;
}
其中
int count;//未赋值,默认为零
进入ensureCapacityInternal()传入的参数值为19
这里就开始了扩容操作
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {//true
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
其中value
char[] value;
value = Arrays.copyOf(value,newCapacity(minimumCapacity));
这是"类名."的方式调用,括号内newCapacity(minimumCapacity)大概最后还是返回这个minimumCapacity,也就是19
具体代码
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;
}
看到copyOf()方法
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
里面的arrycopy就不用说了,就是将传进来的字符数组复制一份给了copy这个数组然后返回,到这里AbstractStringBuilder的append方法中的value就被赋值了
之后的这个 str.getChars(0, len, value, count);在上一篇博文中也提到了,主要就是将这个value给赋值出来
最后两次return
至此”Wo “就被传入了sb1这个对象中,也就是构建出了一个StringBuffer对象sb1
现在我们进去测试类中的第一个append方法
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
这里传入的str也就是”shi “
这里append方法跟上面构建是的方法是一样的
public AbstractStringBuilder append(String str) {
if (str == null)//flase
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
又是一步一步调用,确定字符串应该扩容的长度,然后经历getChats等直到用arraycopy将新传入的字符串复制到了原有字符串的后面,从此对象sb1的内容成了”Wo shi “,之前的”Wo “就没有东西指向了,而到了第二次append(“Fei”);就使sb1的内容变成了”Wo shi Fei“,之前的”Wo shi “就也没有了谁去指向就被回收了。
而相反使用”+“这样的方法,之前几次组合的字符串都有被指向,一直不会消失,如果多次使用这样的方式拼接,有很大的弊端。
最后将输出结果也贴一下吧
public class FeiWuTest {
public static void main(String[] args) {
String s1 = "Wo ";
String s2 = s1 + "shi ";
String s3 = s2+"Fei";
String s4 = s3+"wu";
System.out.println(s4);
StringBuffer sb1 = new StringBuffer("Wo ");
sb1.append("shi ");
sb1.append("Fei");
sb1.append("wu");
System.out.println(sb1);
}
}
/*
Wo shi Feiwu
Wo shi Feiwu
Process finished with exit code 0
*/
这篇也就是粗略地回忆一下讲过的这玩意
至于System.out,println这玩意有兴趣可以看看我上篇的”瞎撞“