StringBuffer(JDK1.0)
1、相关知识
StringBuffer介绍:
1、线程安全的可变字符序列。一个类似于String的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
2、可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
3、每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。
2、定义
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
1、StringBuffer是final修饰的,所以不可以被继承。
2、继承了的AbstractStringBuilder类,这个类是StringBuilder和StringBuffer的直接父类,该类在源码中注释是以JDK1.5开始作为他们的父类,可是直到JDK1.8的API中,Object才是StringBuilder和StringBuffer的父类。
3、AbstractStringBuilder类实现了可变字符序列的一系列的操作,比如append()、insert()、delete()、replace()、charAt()方法等 。
4、Serializable接口:序列化接口,表示对象可以被序列化。
CharSequence接口:字符序列接口,提供了几个对字符序列进行只读访问的方法,比如:length()、charAt()、subSequence()、toString()方法等。
//构建一个StringBuffer的对象
StringBuffer stringBuffer = new StringBuffer("123");
//源码分析
private transient char[] toStringCache;
//被transient修饰的变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。假如一个用户有一些密码等信息,为了安全起见,不希望在网络操作中被传输,这些信息对应的变量就可以加上transient关键字。用来缓存toString()方法返回的最近一次的value数组中的字符。当修改StringBuffer对象时会被清除。
// 下面的两个变量是在AbstractStringBuilder.java中
char[] value;//用来存储字符序列中的字符。value是一个动态的数组,当存储容量不足时,会对它进行扩容。
int count;//表示value数组中已存储的字符数。
//……
public StringBuffer(String str) {
super(str.length() + 16);//把字符串长度传给父类AbstractStringBuilder的构造方法
append(str);
//构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。该字符串的初始容量为 16 加上字符串参数的长度。
}
//……
//AbstractStringBuilder.java
AbstractStringBuilder(int capacity) {
value = new char[capacity];//利用子类传来的字符串长度对value数组进行初始化
}
//StringBuffer提供了4种构造方法,我们选择了其中的一种,构造方法主要完成了对value数组的初始化。
3、append(String str)方法如何扩容
StringBuffer sb1 = new StringBuffer("123");
sb1 = sb1.append("456");
System.out.println(sb1);//输出:123456
//源码分析
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str); //本质上是调用了父类AbstractStringBuilder类中对应的方法
return this;
}
//下面的两个变量是在AbstractStringBuilder.java中
public AbstractStringBuilder append(String str) {
if (str == null) //如果传入的字符串为空
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len); //判断value数组的容量是否足够
str.getChars(0, len, value, count); //将str从此序列复制到目标字符数组value
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
//如果传入str为空
int c = count;
ensureCapacityInternal(c + 4); //判断value数组的容量是否足够
final char[] value = this.value; //把null放入value数组
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
public void ensureCapacity(int minimumCapacity) {
//首先内部先确认一遍传的参数是否有效
if (minimumCapacity > 0) //即是否大于0
ensureCapacityInternal(minimumCapacity);
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
//value数组的容量不足
value = Arrays.copyOf(value,newCapacity(minimumCapacity));
//调用newCapacity()方法进行扩容,再调用Arrays类的copyOf()静态方法来创建一个新数组并拷贝原数据到新数组,并将value指向新数组。
}
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;//默认情况下将数组容量扩大到原数组容量的2倍+2
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
//新容量必需在int所支持的范围内,之所以有<=0判断是因为,在执行 (value.length << 1) + 2操作后,
//可能会出现int溢出的情况。如果溢出或是大于所支持的最大容量(MAX_ARRAY_SIZE为int所支持的最 //大值减8)就会调用hugeCapacity()方法进行计算,否则取newCapacity作为新容量
}
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) {
// overflow
throw new OutOfMemoryError();//数组的容量最大只能扩容到Integer.MAX_VALUE,如果最大容量都小于扩容后的容量的话,就会报内存溢出异常,即必须在int所支持的最大范围内。
}
return (minCapacity > MAX_ARRAY_SIZE)//范围是Integer.MAX_VALUE - 8到Integer.MAX_VALUE之间
? minCapacity : MAX_ARRAY_SIZE; //在minCapacity与MAX_ARRAY_SIZE之间取较大者
}
StringBuilder(JDK1.5)
StringBuffer介绍:
一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
StringBuffer和StringBuilder的区别:
1、线程安全性
StringBuffer是线程安全的,因为当StringBuffer对象在字符串缓冲区被多个线程使用时,很多方法因为带有synchronized关键字,所以可以保证多线程是同步执行的。
但StringBuilder的方法中没有synchronized关键字,多线程不能同步执行,所以是线程不安全的。
2、性能
比较StringBuffer和StringBuilder使用append方法拼接字符串的性能
//StringBuffer
StringBuffer sb01 = new StringBuffer("123");
long startTime = System.currentTimeMillis(); //执行前时间(ms毫秒)
for (int i = 0 ; i < 100000000 ; i++) //append一亿次
{
sb01 = sb01.append("123");
}
long endTime = System.currentTimeMillis(); //执行结束的时间(ms毫秒)
System.out.println(endTime - startTime); //执行使用的时间(ms毫秒)
//输出:2690
//StringBuilder
StringBuilder sb01 = new StringBuilder("123");
long startTime = System.currentTimeMillis();//执行前时间(ms毫秒)
for (int i = 0 ; i < 100000000 ; i++) //append一亿次
{
sb01 = sb01.append("123");
}
long endTime = System.currentTimeMillis(); //执行结束的时间(ms毫秒)
System.out.println(endTime - startTime); //执行使用的时间(ms毫秒)
//输出:1166
通过这个测试得出性能:StringBuilder>StringBuffer
3、缓冲区
//StringBuffer
private transient char[] toStringCache;
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
//StringBuilder
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
StringBuffer调用tostring会直接使用缓冲区的toStringCache来构造一个字符串,是synchronized同步化的。
StringBuilder调用tostring会通过new一个String对象来构造一个字符串。
总结:
String:适用于少量字符串操作,通过隔离,还是可以使用
StringBuffer:因为线程安全,所以适用多线程下在字符缓冲区进行大量操作
StringBuilder:因为线程不安全,适用于单线程下在字符缓冲区进行大量操作,通过隔离,还是可以使用
希望可以和大家互相学习,谢谢大家!!!