Java源码分析之StringBuffer和StringBuilder

 

在研究StringBuffer和StringBuilder时,两个继承了AbstractStringBuilder实现Serializable序列化和CharSequence,查看CharSequence时,发现里面只有5个方法。

 

其中最后一个方法是public default IntStream chars(),嗯。。。优秀

这里的default并非default权限修饰符,而是default关键字。它是Java8引入的关键字,也可称为Virtual extension methods——虚拟扩展方法。即在接口内部实现默认方法(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。

那其他的4个方法也就是为不同的字符序列类提供了统一的规范了。

然后它是继承了AbstractStringBuffer,其中定义了数组char[] value;与String类不同的是它并没有设置为final,即可变数组。

两个构造方法:一个无参,一个动态创建容量构造

AbstractStringBuilder() {}
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

然后是扩容的方法:

public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }
     /**
     * This method has the same contract as ensureCapacity, but is
     * never synchronized.
     */
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
    /**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }

封装的扩容的逻辑如下:

1.当前最小容量为minimumCapacity,若小于等于0则返回。

2.若上一步大于0,则判断是否大于字符长度,若不是则返回。

3.若上一步是,则进行扩容。扩容是试探,以字符长度左移一位加上2作为基准newCapacity,若小于minimumCapacity,则将其赋给newCapacity,然后判断是否溢出。若是则定义为Integer的最大值。容量确定之后进行数组拷贝。

由此,我们可知,在创建StringBuffer和StringBuilder对象时,要尽量估算好字符串的初始值,避免其进行多次扩容带来的效率低下等问题。

下面这个方法看逻辑就知道是释放了多余的空间,减少内存空间的使用。

public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }

然后剩下的就是一些字符串基本的处理方法,这些在String里面也有,里面还有很多重载的append方法,返回值都是AbstractStringBuilder类型的,有兴趣可以看看,这里不便赘述。

再回过头来看StringBuilder和StringBuffer,StringBuffer相比于StringBuilder多了一个private transient char[] toStringCache,这里又出现了一个新的关键字transient,被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

StringBuffer/StringBuilder构造方法如下:

public StringBuilder() {
        super(16);
    }
 
    public StringBuilder(int capacity) {
        super(capacity);
    }
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
 
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

由此可知其默认构造大小为16

剩下的代码都没啥说的了,只是StringBuffer相对于StringBuilder加了synchronized关键字修饰。

 

两者之间的区别?

继承关系?如何实现扩容?线程安全性?

继承关系如下所示:

 

实现扩容由AbstractStringBuilder内部实现,当前最小长度大于16时,确定长度为2*length+2,创建新数组并将原数组拷贝过来。

线程安全性StringBuffer除了构造方法之外所有方法都加了synchronized,几乎都是调用的父类的方法.,用synchronized关键字加锁,资源同步串行化处理,所以是线程安全的。StringBuilder是线程不安全的,但也正因为StringBuffer加了锁,占用了很多资源导致效率不高,StringBuilder效率较高。

StringBuffer和StringBuilder都继承自抽象类AbstractStringBuilder。

存储数据的字符数组也没有被final修饰,说明值可以改变,且构造出来的字符串还有空余位置拼接字符串,但是拼接下去肯定也有不够用的时候,这时候它们内部都提供了一个自动扩容机制,当发现长度不够的时候(默认长度是16),会自动进行扩容工作,扩展为原数组长度的2倍加2,创建一个新的数组,并将数组的数据复制到新数组,所以对于拼接字符串效率要比String要高。自动扩容机制是在抽象类中实现的。

线程安全性:StringBuffer效率低,线程安全,因为StringBuffer中很多方法都被 synchronized 修饰了,多线程访问时,线程安全,但是效率低下,因为它有加锁和释放锁的过程。StringBuilder效率高,但是线程是不安全的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值