java1.8源码阅读--StringBuilder和StringBuffer

首先在回顾一下String、StringBuilder和StringBuffer的区别。
引用这位大佬的解释https://blog.csdn.net/u013679744/article/details/58605645

String:字符串常量 。StringBuffer 与 StringBuilder是字符缓冲变量。StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer中的方法大都采用了synchronized 关键字进行修饰,因此是线程安全的,而StringBuilder没有这个修饰,可以被认为是线程不安全的。StringBuilder 是在JDK1.5才加入的。jdk的实现中StringBuffer与StringBuilder都继承自AbstractStringBuilder。
注意:从JDK1.5开始,带有字符串变量的连接操作(+),JVM 内部采用的是 StringBuilder 来实现的,而之前这个操作是采用 StringBuffer 实现的。
1、String类型的字符串对象是不可变的,一旦String对象创建后,包含在这个对象中的字符系列是不可以改变的,直到这个对象被销毁。
2、StringBuilder和StringBuffer类型的字符串是可变的,不同的是StringBuffer类型的是线程安全的,而StringBuilder不是线程安全的
3、如果是多线程环境下涉及到共享变量的插入和删除操作,StringBuffer则是首选。如果是非多线程操作并且有大量的字符串拼接,插入,删除操作则StringBuilder是首选。毕竟String类是通过创建临时变量来实现字符串拼接的,耗内存还效率不高,怎么说StringBuilder是通过JNI方式实现终极操作的。
4、StringBuilder和StringBuffer的“可变”特性总结如下:
(1)append,insert,delete方法最根本上都是调用System.arraycopy()这个方法来达到目的
(2)substring(int, int)方法是通过重新new String(value, start, end - start)的方式来达到目的。因此,在执行substring操作时,StringBuilder和String基本上没什么区别。
总的来说,三者在执行速度方面的比较:StringBuilder > StringBuffer > String。
一个特殊的例子

String s = "abcd";
s = s+"ab";
System.out.print(s);// result : abcdab

上边的例子会发现str的生成速度很快,这其实是JVM把戏。(好像jdk编译会自动转成StringBuilder对象拼接)

String str = "This is only a" + "simple" + "test";

其实就是:

String str = "This is only a simple test"

所以不需要太多的时间了。但如果你的字符串是来自另外的String对象的话,JVM就会像上边一样执行了。
StringBuffer和StringBuilder的区别
jdk的实现中StringBuffer与StringBuilder都继承自AbstractStringBuilder。AbstractStringBuilder的实现原理:我们知道使用StringBuffer等无非就是为了提高java中字符串连接的效率,因为直接使用+进行字符串连接的话,jvm会创建多个String对象,因此造成一定的开销。AbstractStringBuilder中采用一个char数组来保存需要append的字符串,char数组有一个初始大小(16),当append的字符串长度超过当前char数组容量时,则对char数组进行动态扩展,也即重新申请一段更大的内存空间,然后将当前char数组拷贝到新的位置,因为重新分配内存并拷贝的开销比较大,所以每次重新申请内存空间都是采用申请大于当前需要的内存空间的方式,这里是2倍。

成员变量

继承父类AbstractStringBuilder两个成员变量

构造方法

   public StringBuilder() {
        super(16);
    }

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the {@code capacity} argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

    /**
     * Constructs a string builder that contains the same characters
     * as the specified {@code CharSequence}. The initial capacity of
     * the string builder is {@code 16} plus the length of the
     * {@code CharSequence} argument.
     *
     * @param      seq   the sequence to copy.
     */
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

上一章我已经分析了AbstractStringBuilder类的源码,详情请转https://blog.csdn.net/weixin_42955916/article/details/100690426
我们知道在jdk1.8源码里面AbstractStringBuilder的两个子类是StringBuilder和StringBuffer。
而这里的构造方法都是调用父类的构造方法。初始化的字符缓存大小为str.length+16。注意AbstractStringBuilder构造方法传入参数如果与value.length*2+2比较,哪个大去哪个,如果小于0则去源字符数组的大小。

具体方法

翻看源码,所有的实现都是调用父类的方法。具体也没什么好研究的,请移步AbstractStringBuilder类的源码,详情请转https://blog.csdn.net/weixin_42955916/article/details/100690426
有两个方法还是没有

    /**
     * Save the state of the {@code StringBuilder} instance to a stream
     * (that is, serialize it).
     *
     * @serialData the number of characters currently stored in the string
     *             builder ({@code int}), followed by the characters in the
     *             string builder ({@code char[]}).   The length of the
     *             {@code char} array may be greater than the number of
     *             characters currently stored in the string builder, in which
     *             case extra characters are ignored.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        s.defaultWriteObject();
        s.writeInt(count);
        s.writeObject(value);
    }

    /**
     * readObject is called to restore the state of the StringBuffer from
     * a stream.
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        count = s.readInt();
        value = (char[]) s.readObject();
    }

writeObject(java.io.ObjectOutputStream s) throws java.io.IOException:将StringBuilder实例的状态保存到流中(即序列化它)。
void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException:调用readObject从流中恢复StringBuffer的状态。
ps:一遍文章看到的,StringBuilder 自己定义了 serialize 和 反 serialize方法,具体还不知道有什么用

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

重新new了一个String字符串对象哦

StringBuffer

StringBuffer和StringBuilder的方法都是差不多的,只是方法都加了synchronized修饰(同步方法)
StringBuffer的疑问点?
为什么有个成员变量

private transient char[] toStringCache;

toString返回的最后一个值的缓存。每当修改StringBuffer时清除。(不是很懂),看了源码每次调用里面的方法都要把toStringCache设置为null。

    private static final java.io.ObjectStreamField[] serialPersistentFields =
    {
        new java.io.ObjectStreamField("value", char[].class),
        new java.io.ObjectStreamField("count", Integer.TYPE),
        new java.io.ObjectStreamField("shared", Boolean.TYPE),
    };

    /**
     * readObject is called to restore the state of the StringBuffer from
     * a stream.
     */
    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();
    }

    /**
     * readObject is called to restore the state of the StringBuffer from
     * a stream.
     */
    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);
    }

这里也有些不同,求大佬解决。。。

transient使用小结

1) 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2) transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
3) 被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

总结

  1. AbstractStringBuilder为什么定义为抽象方法:抽象方法必须有abstract修饰的方法,而只有一个方法用这个修饰了那就是toString()方法,所以从语法规则上通过了。而且该类的权限为friendly 只能在jdk lang包和子类中使用,我们自己的项目中使用不到这个类,也用不到toString方法,顾定义为抽象方法。
  2. AbstractStringBuilder抽象化有什么用:重代码编写来看,因为StringBuilder和StringBuffer的实现方式都是调用父类的方法,所以减少代码的重复。不同的子类扩展不同使用。

你可能感兴趣的:(java源码)