聊一聊我所理解的String,StringBuffer和StringBuilder

String,StringBuffer和StringBuilder之间的区别,应用及底层实现已经是老生常谈了.从开始接触java到现在,只要是要复习或者是准备面试,这个题就是必须要经历的.可以说是众里寻他千百度,蓦然回首,他依旧在那等着你啊.
在这里插入图片描述
这里,我就聊一下我对这3个类的理解~
**

1. String

**
首先,String这个老大哥底层是维持的是 final 修饰过的 private 权限的 char型数组.
在这里插入图片描述
这个final就很厉害呀,他修饰过之后,这个char型数组就成为了一个不可变的数组,所以String对象就是不可变的咯.
因此,String对象就是相当于一个常量啦,自然也就是不存在线程安全问题,换言之就是线程安全的.

然后,在性能方面,因为String对象是一个不可变的char型数组,所以每次改变String对象的时候都是会重新生成一个String对象,然后将新创建的String对象的地址赋给原来的变量.可想而知,如果存在大量的update操作时,使用String是多么的浪费资源.
**

2.在分开聊StringBuffer和StringBuilder之前,先看个有意思的东西

**
在这里插入图片描述
通过查看继承树,我们发现,StringBuffer 和StringBuilder 都是继承于一个抽象类AbstractStringBuilder.
这里使用的就是设计模式中的建造者模式,AbstractStringBuilder作为抽象建造者,已经写好了各种方法,而StringBuilder和StringBuffer作为具体建造者去实现抽象建造者.(具体的设计模式,我以后会写再写,到时候贴过来~ )
下面以用的最多的append()方法举例:
我们都知道.StringBuffer和StringBuilder 都重载了很多append()方法,
查看源码能发现:
StringBuilder的append(String str)方法:

  @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

StringBuffer的append(String str)方法:

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

其实不难发现,StringBuilder和StringBuffer的方法其实在实现上没有很大区别,最大的就是StringBuffer在方法上加上了synchronized关键字.
然后我们看一下AbstractStringBuilder的属性和构造器

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage. //该值用于字符存储。
     */
    char[] value;  	//	这个就是我们具体持有的char型数组

    /**
     * The count is the number of characters used. //用于记录 字符的个数
     */
    int count;

    /**
     * 一个必须的空参构造器
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     * 创建指定容量的AbstractStringBuilder
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

下面我们来具体聊一聊这哥俩~

3.StringBuilder和StringBuffer的初始化及扩容问题

首先,我们来看看StringBuilder底层实现原理, 他持有的是他的"爸爸"AbstractStringBuilder的属性 char[ ] value,
而我们看一下StringBuilder的构造器:

  /**
     * StringBuilder的空参构造器
     * 这就告诉我们,在使用空参构造器创建StringBuilder对象的时候,直接调用了父类的有参构造器,使我们持有的char型数组的长度变为了16.
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * 如果们使用有参构造器来初始化StringBuilder对象长度,那么我们就会将char型数组的初始化长度变为我们想要的长度(capacity)
     *
     * @param      初始化长度(大于0,小于0就会报错)
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * 如果我们使用有参构造器在创建StringBuilder对象时传入String字符串的话,那么我们的char型数组的长度就变为了字符串长度+16
     * @param   str   初始化时传入的String字符串
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

其实不止是StringBuilder如此,StringBuffer也是这样.
那么,我们不禁有一个疑问,因为这哥俩都是可变数组,那么当数组长度大于了我们的初始化长度时怎么办呢?
答案就是 调用父类的 newCapacity方法进行扩容

	/**
	* @minimumCapacity 就是最小容量
	*
	*/
   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;
    }

由代码中我们可以看出,基本扩容就是 原数组长度 * 2 + 2.即 如果我们初始化长度是 16,那么第一次扩容之后的长度就变为了
16 * 2 + 2 = 34;

4.StringBuilder和StringBuffer的线程安全问题:

StringBuilder是线程不安全的.StringBuffer是线程安全的.这中的关键就是 synchronized 关键字.
StringBuffer中所有操作持有的char型数组的方法中都加入了synchronized关键字来保证线程安全,
同时StringBuffer有一个独有的
在这里插入图片描述
缓存属性, 用来保存最后进行操作的值,来提升一定的性能.但是总体性能还是要低于StringBuilder.

**

5.三者使用的总结

**
1.在只有少量字符串或者极少量字符串改变的情况下, 可以考虑使用String.
2.在单线程或者使用其他方式确保了数据线程安全情况下,优先考虑使用StringBuilder.
3.在多线程或者没有其他方式确保数据线程安全情况下,使用StringBuffer.

猜你喜欢

转载自blog.csdn.net/shiliu_baba/article/details/106071363
今日推荐