StringBuffer和StringBuilder源码分析

简介:

    今天分析的两个类是:StringBuffer 和 StringBuilder。开篇前,先看看它们的继承层次:

public final class StringBuffer extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {...}
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {...}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    都继承了 AbstractStringBuilder ,实现了 Serializable 和 CharSequence 接口。final类型,不能再派生子类。


一.成员变量

    (1) char[] value;// 底层都是用字符数组char[]实现,存储字符串,默认的大小为16。在父类 AbstractStringBuilder 中定义的。String的value数组使用final修饰,不能变动,StringBuffer和StringBuilder的value数组没有final修饰,是可变的。 关于数组的大小,默认的初始化容量是16。这个数有木有想起了Map的实现子类的初始容量。假如初始化的时候,传入字符串,则最终的容量将是 (传入字符串的长度 + 16) 。 

    (2) private transient char[] toStringCache;// StringBuffer特有,缓存toString最后一次返回的值。 如果多次连续调用toString方法的时候由于这个字段的缓存就可以少了Arrays.copyOfRange的操作(每次调用其他的修改StringBuffer对象的方法时,这些方法的第一步都会先将toStringCache设置为null,详细参见源码) StringBuilder.toString()

 public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
 }
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

StringBuffer.toString()

 public synchronized String toString() {
    if (toStringCache == null) {// toStringCache为空,第一次操作
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);// 使用缓存的toStringCache,实际只传递了引用,没有复制操作
 }
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    String 提供了一个保护类型的构造方法。目前不支持使用false,只使用true。那么可以断定,加入这个share的只是为了区分于String(char[] value)方法,不加这个参数就没办法定义这个函数,只有参数不同才能进行重载。那么,第二个区别就是具体的方法实现不同。这里直接将value的引用赋值给String的value。那么也就是说,这个方法构造出来的String和参数传过来的char[] value共享同一个数组。作用的话,肯定是性能好一点。假如把该方法改为public,而不是protected的话,对外开放访问,就可以通过修改数组的引用来破坏String的不可变性。

String(char[] value, boolean share) {
    // assert share : "unshared not supported";
    this.value = value;// 没有真正复制,只是赋值引用
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    对比一下,下面是实际复制了数组元素的:

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);// 用到Arrays的copyOf方法将value中的内容逐一复制到String当中
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二.成员方法

    两者方法最大的区别是:StringBuffer是线程安全的,StringBuilder是非线程安全的。实现是StringBuffer在和StringBuilder相同的方法上加了 synchronized 修饰。 

StringBuffer.append(String)

public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

StringBuilder.append(String)

public StringBuilder append(String str) {
    super.append(str);
    return this;
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

三.底层存储的扩容机制

    通过分析源码,发现每次需要扩容的都是按照 "以前容量*2+2" 进行扩容,如果扩容之后仍不满足所需容量,则直接扩容到所需容量。 对外通过 ensureCapacity(size) 来主动扩容。

public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0)
        ensureCapacityInternal(minimumCapacity);
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    AbstractStringBuilder.ensureCapacityInternal(int) private修饰,供类内部调用。newCapacity是在JDK1.8中拆分出来的方法,之前扩容都是在一个方法里面操作完成的 - expandCapacity

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,newCapacity(minimumCapacity));
    }
}
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;
}
private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE;
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    特意去对比了一下JDK1.7的源码: AbstractStringBuilder.ensureCapacity(int) AbstractStringBuilder.ensureCapacityInternal(int) AbstractStringBuilder.expandCapacity(int) JDK1.8的源码: AbstractStringBuilder.ensureCapacity(int) AbstractStringBuilder.ensureCapacityInternal(int) AbstractStringBuilder.newCapacity(int) AbstractStringBuilder.hugeCapacity(int) 通过对比发现,JDK8把之前在一个方法里面做的操作拆分成了两个方法,看源码的时候,更容易理解。


四.两者对比总结

1.相同点 

    (1)继承层次相同,都继承了 AbstractStringBuilder ,实现了 Serializable 和 CharSequence 接口; 

    (2)底层都是用字符数组实现,字符串都是可变的,区别于String; 

    (3)初始容量都是16,扩容机制都是"以前容量*2+2" 

2.不同点 

    (1)StringBuilder不是线程安全的,StringBuffer是线程安全的(方法上多了synchronized修饰); 

    (2)StringBuffer比StringBuilder多了一个toStringCache字段,用来在toString方法中进行缓存; 

    (3)StringBuilder没有加同步,在不会出现线程安全问题的情况下,性能上StringBuilder应该要高于StringBuffer


五.扩展

    今晚看源码的时候,发现上面的数组容量有个最大值,很好奇,点进去看一下:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// Some VMs reserve some header words in an array.
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    已有的注释意思是:有些虚拟机在数组中保留了一些头信息。避免内存溢出。 MAXVALUE是0x7fffffff,即 2^31 = 2,147,483,648 。那为啥最大数组的大小是 2^31 减掉 8 呢?8这个数字刚好是一个字节里面比特的数量,让人联想翩翩。 然后去gg了一下:在StackOverflow上,有个已经解答的问题,Why the maximum array size of ArrayList is Integer.MAXVALUE - 8? 回答参考了developerworks的论文:Java Memory management 原因是:数组需要 8 byte 来存储自己的大小数量,所以最大数组定义为 Integer.MAX_VALUE - 8。

目录
相关文章
|
11天前
|
安全 Java
Java StringBuffer 和 StringBuilder 类
Java StringBuffer 和 StringBuilder 类
18 0
|
3天前
|
存储 安全 Java
【JAVA学习之路 | 提高篇】StringBuffer与StringBuilder
【JAVA学习之路 | 提高篇】StringBuffer与StringBuilder
|
3天前
|
存储 安全 Java
javaSE-String,StringBuffer和StringBuilder
javaSE-String,StringBuffer和StringBuilder
9 0
|
9天前
|
存储 Java
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
|
11天前
|
安全 Java 编译器
Java中String、StringBuilder和StringBuffer的区别
Java中String、StringBuilder和StringBuffer的区别
12 1
|
11天前
|
存储 缓存 安全
【 Java中String源码分析(JVM视角你不来看看?】
【 Java中String源码分析(JVM视角你不来看看?】
17 0
|
11天前
|
安全
String、StringBuuffer、StringBuilder三者的区别
String、StringBuuffer、StringBuilder三者的区别
|
11天前
|
缓存 安全 Java
【Java基础】String、StringBuffer和StringBuilder三种字符串对比
【Java基础】String、StringBuffer和StringBuilder三种字符串对比
9 0
|
11天前
StringBuilder和StringBuffer区别是什么?
StringBuilder和StringBuffer区别是什么?
|
11天前
|
Java API 索引
Java基础&API(2) String、StringBuilder详解
Java基础&API(2) String、StringBuilder详解