我的jdk源码(四):StringBuffer 线程安全可多次修改String

一、概述

    StringBuffer类是我们动态操作字符串常用到的类,jdk1.8中StringBuffer继承了父类AbstractStringBuilder类,并且在源码内很多方法都是直接调用的父类AbstractStringBuilder的方法。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。 接下来就让我们进入到StringBuffer的源码学习!

二、源码分析

    (1) 类的声明源码如下:

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

    如源码所示,final关键字修饰的类为最终类,无法被继承 。StringBuffer类不但继承了AbstractStringBuilder类,还实现了Serializable接口和CharSequence接口。实现Serializable是为了能序列化它的对象,具体在《我的jdk源码(二):String 一个特殊而强大的类!》一文中有详细介绍;实现CharSequence是为了代表该类,或其子类是一个字符序列,具体在《我的jdk源码(三):AbstractStringBuilder类》一文中有详细介绍 。

    (2) 成员变量源码如下:

    //StringBuffer定义了一个自己的toStringCache用于缓存
    private transient char[] toStringCache;

    static final long serialVersionUID = 3388685877147921107L;

    字符数组toStringCache缓存最后一次toString的内容,当被修改的时候这个cache清空,所以在后面的很多方法内都可以看到清空toStringCache的操作。 如果没被修改,那么这个toStringCache就是上一次toString的结果。 没被修改的时候,就可以直接把toStringCache作为new String的参数,然后把这个String返回就行了。 也就是cache有效的时候,就不必进行arraycopy的复制操作,cache失效了才进行arraycopy的复制操作。此番操作,作用在于保证线程安全的情况下,提升读取效率。

    transient关键字修饰字符数组toStringCache,是为了在进行序列化操作的时候,不将缓存的toStringCache内容进行数据持久化,也就是不会保存到内存中。 StringBuffer实现了Serializable接口,所以默认情况下,对象所有的变量都会转变成持久状态,但是toStringCache只是作为读取缓存,所以没必须转变成持久状态。

    (3) 构造方法源码如下:

    public StringBuffer() {
        super(16);
    }

    public StringBuffer(int capacity) {
        super(capacity);
    }

    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

    public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

    构造函数都是调用父类构造方法,并且很直观的反映了以下几点:

        *  无参默认创建容量为16的底层数组。

        *  传入字符串时,容量为字符串长度+16。即StringBuffer sb = new StringBuffer("abc");时,sb.length()值为3,sb.capacity()值为19。

        *  传入整数时,则设定容量为参数值 。

    (4) toString()方法源码如下:

    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

        StringBuffer的toString方法与StringBuilder的toString方法有一点区别。这里是通过toStringCache成员构造String对象然后返回的。因为这里创建String对象调用的是String类的String(char[] value, boolean share)构造方法,是共享字符数组的,以提高效率。 所以通过toStringCache来保证每次调用toString方法时得到的String对象是不变所结果。试想一下如果没有使用toStringCache,而是直接共享了value,那么在调用toString方法后,再对StringBuffer进行操作的时候之前返回的String对象就改变了,违背了String对象不变的设计理念。

    (5) writeObject()方法源码如下:

    private synchronized void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        java.io.ObjectOutputStream.PutField fields = s.putFields();
        fields.put("value", value);
        fields.put("count", count);
        fields.put("shared", false);
        s.writeFields();
    }

         在进行序列化的时候保存StringBuilder对象的状态到一个流中。 但是toStringCache受到关键字transient的影响,不会被保存。

  (6) readObject()方法源码如下:

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        java.io.ObjectInputStream.GetField fields = s.readFields();
        value = (char[])fields.get("value", null);
        count = fields.get("count", 0);
    }

        反序列化时从流中获取StringBuild对象序列化之前的状态。

三、总结

     除了构造方法和少数几个方法,其他方法都用synchronized修饰,所以线程安全,并且大多数方法都是调用的了父类AbstractStringBuilder的方法实现,想了解更多可查阅《我的jdk源码(三):AbstractStringBuilder类》了解更多知识。另外值得一提的是,StringBuffer内容清空效率有几种方法,但是 要通过使用sb.setLength(0);来清空StringBuffer对象中的内容效率最高,即调用setLength(int newLength)方法。StringBuffer类还有它的姊妹类StringBuilder类,那么接下来,敬请期待《我的jdk源码(五):StringBuilder 高效可多次修改String》。

    更多精彩内容,请扫描关注我的微信公众号【Java觉浅】,获取第一时间更新!

猜你喜欢

转载自blog.csdn.net/qq_34942272/article/details/106330851