美文网首页程序员
你说你精通spring源码?那你知道StringBuffer的扩

你说你精通spring源码?那你知道StringBuffer的扩

作者: 程序员伟杰 | 来源:发表于2020-07-23 20:23 被阅读0次

    前言

    在Java中,String是一个常量,一旦创建其值后不能更改但可以共享。

    如果我们把多个字符串进行连接(拼接)操作,就会开辟很多空间,从而造成了大量内存空间的浪费。

    为了解决这个问题,我们需要用到StringBuffer类和StringBuilder类。

    这两个类可牛逼了,它们都是可变长度的字符串类,在字符串的拼接处理上大大提高了效率。

    一、StringBuffer与StringBuilder的区别

    共同点:底层数据结构都是char类型的数组,都是可变的字符串。

    不同点:StringBuffer线程同步的安全性高,但多线程操作时效率低。StringBuilder线程不同步,进行多线程操作时,安全性低,但效率高。

    因为这两个类的用法都类似,所以我就以StringBuffer类为例来讲解。

    二、StringBuffer类的定义

    StringBuffer类的部分源码:

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

    从源码中可以看出,StringBuffer是一个用final修饰的最终类,继承了父类AbstractStringBuilder类,实现了Serializable接口和CharSequence接口,说明具备了两种能力。

    三、四个构造方法

    StringBuffer类的部分源码:

    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);
        }
    

    重点讲下这两个构造方法:

    构造方法:StringBuffer()

    方法描述:构造一个没有字符的字符串缓冲区,初始容量为16。

    构造方法:StringBuffer(String str)

    方法描述:构造一个初始化为指定字符串内容的字符串缓冲区,初始容量为str.length() + 16。

    实例:

    package cn.tkr.demo;
    
    public class MyStringBuffer {
        public static void main(String[] args) {
            
            StringBuffer sb1 = new StringBuffer();
            System.out.println("sb1字符缓冲区的容量:" + sb1.capacity()); //获取当前StringBuffer的容量
    
            StringBuffer sb2 = new StringBuffer("LoveJava");
            System.out.println("sb2字符缓冲区的容量:" + sb2.capacity()); //获取当前StringBuffer的容量
        }
    }
    

    运行结果:

    sb1字符缓冲区的容量:16
    sb2字符缓冲区的容量:24
    

    实例分析:调用无参数构造函数,sb1字符缓冲区的初始容量为16,调用有参数构造函数,初始容量为字符串的长度(8) + 16,所以初始容量为24。

    四、StringBuffer的常用方法

    注: append(...)中的...代表各种类型的参数,如append(int i)、append(char c)、append(String str)等

    五、StringBuffer的扩容原理

    扩容原理:

    StringBuffer的底层数组结构用的是char类型的数组。

    所以,当我们使用StringBuffer对象的append(...)方法追加数据时,如果char类型数组的长度无法容纳我们追加的数据,StringBuffer就会进行扩容。

    扩容时会用到Arrays类中的copyOf(...)方法,每次扩容的容量大小是原来的容量的2倍加2。

    注: copyOf(...)中的...代表各种类型的参数,如
    copyOf(int[] original, int newLength)、copyOf(char[] original, int newLength)等

    实例源码分析:

    假设我现在调用了append(String str)方法,追加了一个字符串(char类型数组的长度无法容纳的字符串)。

    append(String str)方法源码:

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

    方法中通过super.append(str)调用了父类的append(String str)方法。

    父类的append(String str)方法源码:

    public AbstractStringBuilder append(String str) {
            if (str == null)
                return appendNull();
            int len = str.length();
            ensureCapacityInternal(count + len);
            str.getChars(0, len, value, count);
            count += len;
            return this;
        }
    

    重点来了,这里的ensureCapacityInternal(count + len)就是一个扩容相关的方法,变量count是一个全局变量,并没有实际的值,变量len是我们追加进来的字符串的长度。

    也就是说,我们追加进来的字符串的长度会传递给ensureCapacityInternal(int minimumCapacity)方法。

    再来看看ensureCapacityInternal(int minimumCapacity)方法的源码:

    private void ensureCapacityInternal(int minimumCapacity) {
            // overflow-conscious code
            if (minimumCapacity - value.length > 0) {
                value = Arrays.copyOf(value,
                        newCapacity(minimumCapacity));
            }
        }
    

    其中,minimumCapacity指我们追加进来的字符串的长度,value是一个全局的char类型的数组名。

    也就说,value.length指数组的长度,那如果(minimumCapacity - value.length > 0)这个条件成立,也就意味着,char类型数组的长度无法容纳我们追加的字符串的长度。

    这时,就需要使用Arrays类中的copyOf(char[] original, int newLength)方法进行扩容。

    方法:

    copyOf(char[] original, int newLength)

    描述:复制指定的数组,截断或使用相应的默认值进行填充

    该方法的第一个参数是源数组名,所以要传递的是value。

    第二个参数是新数组长度,新数组长度的值是通过newCapacity(int minCapacity)方法来计算并返回的值。

    newCapacity(int minCapacity)方法的源码:

    private int newCapacity(int minCapacity) {
            // overflow-conscious code
            int newCapacity = (value.length << 1) + 2;
            if (newCapacity - minCapacity < 0) {
                newCapacity = minCapacity;
            }
            return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
                ? hugeCapacity(minCapacity)
                : newCapacity;
        }
    

    这个方法会返回一个新的容量大小(即新数组长度),每次扩容的容量大小是原来的容量的2倍加2。

    六、最后

    感谢你看到这里,说的都是自己的一些看法和见解,如有不对,请指正!觉得文章对你有帮助的话不妨给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

    相关文章

      网友评论

        本文标题:你说你精通spring源码?那你知道StringBuffer的扩

        本文链接:https://www.haomeiwen.com/subject/tzfklktx.html