java基础——String StringBuffer StringBuilder

String : 不可变字符序列
StringBuffer : 可变字符序列 线程安全
StringBuilder :可变字符序列 线程不安全
JDK11

1.可变和不可变

String 底层存储结构为final 修饰的字节数组,因此不可修改

private final byte[] value;

StringBuffer 和StringBuilder 都继承AbstractStringBuilder
底层使用的也是AbstractStringBuilder 中的字节数组,可变

/**
     * The value is used for character storage.
     */
    byte[] value;

2.jdk9之后String底层存储的新特性

这里先介绍一下jdk9之后的新特性
jdk9之前底层用的都是char[],9之后改用byte[]
原因:
一个char占用两个字节,一个byte占用一个字节
多数情况下String存储的都是拉丁字符,而拉丁字符占用一个字节,因此浪费了一个字节的空间,所以将char数组改为byte数组。那么对于中文占用两个字节的情况下,jdk9还有一种编码是UTF-16可以选择,因此内部需要一个标识coder来表示使用了哪种编码,LATIN1 值为0,UTF16 值为1。

String类中

private final byte coder;
 static final byte LATIN1 = 0;
 static final byte UTF16  = 1;
static final boolean COMPACT_STRINGS;

    static {
        COMPACT_STRINGS = true;
    }
 private boolean isLatin1() {
        return COMPACT_STRINGS && coder == LATIN1;
    }

很多方法需要区分是LATIN1编码还是UTF16编码,一般通过isLatin1来区分

3.StringBuffer StringBuilder

1.构造函数

 public StringBuffer() {
        super(16);
    }

    /**
     * Constructs a string buffer with no characters in it and
     * the specified initial capacity.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *             argument is less than {@code 0}.
     */
    @HotSpotIntrinsicCandidate
    public StringBuffer(int capacity) {
        super(capacity);
    }
    /**
     * Constructs a string buffer initialized to the contents of the
     * specified string. The initial capacity of the string buffer is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    @HotSpotIntrinsicCandidate
    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

可以看到生成对象时默认空参时是16大小的空间,也可以指定具体的大小
如果有参数,就是参数的长度加上16
保证有16大小的空余空间
StringBuffer StringBuilder构造函数的源码都是一样的,都调用父类的构造函数。

    AbstractStringBuilder(int capacity) {
        if (COMPACT_STRINGS) {
            value = new byte[capacity];
            coder = LATIN1;
        } else {
            value = StringUTF16.newBytesFor(capacity);
            coder = UTF16;
        }
    }

2. append() 、扩容

StringBuffer

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

StringBuilder

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

区别就是StringBuffer 加入了 synchronized同步锁 保证了线程安全

接下来看父类的 append() 方法

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

一进来先判断是否为空,为空就返回appendNull()

private AbstractStringBuilder appendNull() {
        ensureCapacityInternal(count + 4);
        int count = this.count;
        byte[] val = this.value;
        if (isLatin1()) {
            val[count++] = 'n';
            val[count++] = 'u';
            val[count++] = 'l';
            val[count++] = 'l';
        } else {
            count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
        }
        this.count = count;
        return this;
    }

可以看出最后返回的是原来的字符加上null字符

		StringBuilder stringBuffer1 = new StringBuilder("123");
        String s1=null;
        StringBuilder stringBuffer2 = stringBuffer1.append(s1);
        System.out.println(stringBuffer2);
       
    	>>123null

回过头来看如果不为空,就判断内部容量还够不够ensureCapacityInternal(count + len);

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        int oldCapacity = value.length >> coder;
        if (minimumCapacity - oldCapacity > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity) << coder);
        }
    }
 static final byte LATIN1 = 0;
 static final byte UTF16  = 1;

根据前面jdk9的新特性,可以看到如果编码为LATIN1 ,那么byte数组value的长度右移coder位,即0位,不变。
如果编码为UTF16 ,那么byte数组value的长度右移coder位,即1位。因为UTF16是两个字节存一个字符,而你value.length想返回的也是字符串的长度而不是byte数组的长度。

位移完之后判断容量够不够,不够就扩容,扩容你需要有新的容量大小,使用newCapacity(minimumCapacity)
minimumCapacity为原来的长度加上要拼接字符串的长度,即为拼接后的长度

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

首先新的容积为旧的两倍+2
如果还不够,直接把minCapacity作为新的容量
下面还有一些特殊情况比如左移后变成负数或者超出安全范围

4. 使用

1.少量数据或者对数据没有大量处理操作时可以使用String
2.单线程处理数据时可以使用StringBuilder,效率更高
3.多线程处理数据时可以使用StringBuffer,保证线程安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值