和String类型一样,StringBuilder类、StringBuffer类也是用来操作字符串的类。
继承关系图:
和String类不一样,使用 StringBuilder类或 StringBuffer 类时,每次都会对对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuilder类或StringBuffer 类。
StringBuilder 类在 Jdk5 中被提出,它和 StringBuffer(据说最早的jdk版本中就有这个类) 之间的最大不同在于 StringBuilder 的方法不是线程安全的,但是速度比 StringBuffer 快。
StringBuilder类
StringBuilder类部分源码
AbstractStrigBuilder类构造方法
和String类一样,StringBuilder类也不可以被继承、因为类的定义用了final修饰。
StringBuilder对象实质上是一个字符数组,创建 StringBuilder对象不可以直接使用字符串常量,需要new一个对象,使用构造方法创建。使用空参构造方法创建对象时,新建一个长度为16的空字符数组。使用传入参数值为整数的构造方法创建对象时,新建一个长度为参数值的空字符数组。使用传入参数值为字符串对象的构造方法创建对象时,新建一个长度为字符串长度+16的字符数组,并将字符串对象的值复制到字符数组中。
StringBuilder对象保存的是字符数组的引用地址。
public class TestMain {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder(20);
StringBuilder sb3 = new StringBuilder("abc");
System.out.println(sb1.length()); //返回对象字符串长度 0
System.out.println(sb2.length()); //返回对象字符串长度 0
System.out.println(sb3.length()); //返回对象字符串长度 3
System.out.println(sb1.capacity()); //返回字符数组长度 16
System.out.println(sb2.capacity()); //返回字符数组长度 20
System.out.println(sb3.capacity()); //返回字符数组长度 19
}
}
执行结果:
length和capacity方法源码
count是 StrigBuilder类的父类AbstractStrigBuilder类的成员变量,没有初始化,是个int类型,所以默认值是0,用来保存 StrigBuilder对象实际保存的字符个数(从append方法中可以看出来)。所有length方法返回的是StrigBuilder对象实际保存的字符个数。
capacity方法的返回值是value这个字符数组的长度,不管数组中有没有存满字符,所有capacity方法返回的值也是StrigBuilder对象最大保存的字符个数。
append方法是最常用的方法,使用来拼接字符串的,直接把新的字符串放到原字符串的末尾,组成一个新的字符串,但是不创建新的StrigBuilder对象,新字符串还保存在StrigBuilder对象中。
public class TestMain {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
System.out.println(sb); //空串
System.out.println(sb.length()); //返回对象字符串长度 0
System.out.println(sb.capacity()); //返回字符数组长度 16
sb.append("abcderghijklmnop");
System.out.println(sb); //abcderghijklmnop
System.out.println(sb.length()); //返回对象字符串长度 16
System.out.println(sb.capacity()); //返回字符数组长度 16
sb.append("qrst");
System.out.println(sb); //abcderghijklmnopqrst
System.out.println(sb.length()); //返回对象字符串长度 20
System.out.println(sb.capacity()); //返回字符数组长度 34
sb.append("abcderghijklmnoprst");
System.out.println(sb); //abcderghijklmnopqrstabcderghijklmnoprst
System.out.println(sb.length()); //返回对象字符串长度 39
System.out.println(sb.capacity()); //返回字符数组长度 70
}
}
执行结果:
可以看出,随着调用append方法不断的添加字符串,StringBuilder对象的实际保存字符长度和最大保存字符长度都在变大,实际保存字符长度变大不难理解,那么最大保存字符长度增加(扩容)的原理是什么呢?
查看append方法源代码
当传入 append方法的参数是非空字符串时,调用ensureCapacityInternal(count + len)方法,传入原字符串长度和新字符串长度之和,查看ensureCapacityInternal方法源码
可以看出是 newCapacity方法改变的最大保存字符长度值,即value.length的值。
value.length << 1 ,字符数组长度是个int类型数据,在计算机中是用二进制存储的,共四个字节,范围是,“<<"是左移运算符,value.length << 1,表示length左移一位,因为是二进制存储,所以相当于放大2倍。int newCapacity = (value.length << 1) + 2 的意思是新数组长度是原数组长度的2倍加2。
可以看出,扩容也是需要代价的,所以对于那些不断增加的字符串对象,使用 StringBuilder(int capacity) 构造方法创建对象,并指定合理的初始化大小,减少扩容次数。
StringBuffer类的功能与 Stringbuilder类类似,只不过 StringBuffer类是线程安全的,成员方法都被synchronized修饰。
参考资料: