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。

目录
相关文章
|
1月前
|
安全 Java
Java StringBuffer 和 StringBuilder 类
Java StringBuffer 和 StringBuilder 类
16 0
|
3月前
|
存储 安全 Java
【JAVA基础】String、StringBuilder和StringBuffer的区别——巨详细
String是不可变的,StringBuilder和StringBuffer是可变的。而StringBuffer是线程安全的,而StringBuilder是非线程安全的。
|
2月前
|
存储 XML 缓存
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
26 0
|
4月前
|
安全 Java 调度
Java基础面试,String,StringBuffer,StringBuilder区别以及使用场景
* String是final修饰的,不可变,每次操作都会产生新的对象。 * StringBuffer和StringBuilder都是在原对象上进行操作 * StringBuffer是线程安全的,StringBuilder是线程不安全的。 * StringBuffer方法是被synchronized修饰的
|
15天前
|
移动开发 安全 Java
String、StringBuffer 、StringBuilder、StringJoiner
String、StringBuffer 、StringBuilder、StringJoiner
|
1月前
|
存储 算法 安全
【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法
【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法
|
1月前
|
存储 安全 Java
String、StringBuilder、StringBuffer的区别
String、StringBuilder、StringBuffer的区别
13 0
|
1月前
|
安全 Java
针对String、StringBuffer、Stringbuilder区别及使用场景
针对String、StringBuffer、Stringbuilder区别及使用场景
|
1月前
|
存储 安全 Java
36、Java 中的 String、StringBuilder、StringBuffer、字符串常量池和 intern 方法
36、Java 中的 String、StringBuilder、StringBuffer、字符串常量池和 intern 方法
31 0
|
3月前
|
存储 安全 前端开发
Java中的String类与StringBuilder、StringBuffer的比较:缺点与解决办法
Java中的String类与StringBuilder、StringBuffer的比较:缺点与解决办法
53 0