String + StringBuilder + StringBuffer 详解

String 类型

    private final char value[];

String 字符串常量底层是 final 修饰的 char 型数组,是“不可变”的对象【反射可以改变】, 每次对已创建的 String 类型进行改变时,都等同于生成一个新的 String 对象,然后将新的 String 对象的地址赋给原来的引用。

	String s1 = new String("hello");
    String s2 = "world";

    s1 = s1 + s2;
    System.out.println(s1);     //helloworld

图解:
在这里插入图片描述
通过反射改变:

public class StringReflectTest {

    public static void main(String[] args) throws Exception {

        String s1 = "hello ";
        String s2 = "word";

        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] ch = (char[]) value.get(s1);
        ch[5] = '_';
        System.out.println(s1 + s2);		//hello_word
    }
}

StringBuilder 与 StringBuffer 类型

StringBuilder 和 StringBuffer 均为字符串变量。是可变长的字符序列,能够被多次修改,并且不会创建新的对象。

在这里插入图片描述

为什么是可变长字符序列?

StringBuilderStringBuffer的构造方法均继承自父类AbstractStringBuilder的构造方法,同时value是一个普通的字符数组。【这里以StringBuilder为例做解说,StringBuffer类似】

	char[] value;
	AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

在下一次追加字符串的时候调用了父类的append() 方法,进行容量分配和拷贝。初始容量为16,当容量不足时进行扩容处理【左移一位并加2】。

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;
    }
//AbstractStringBuilder.java     扩容判断方法
private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
//AbstractStringBuilder.java     扩容具体实现
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;
    }
	//String.java     字符串的拷贝
	public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
(1)StringBuilder 类型

非线程安全,不保证同步,但是执行速度快java.lang.StringBuilder包下。

常用方法:

  • StringBuilder append(boolean b)
    将 boolean 参数的字符串表示形式 追加 到序列。
  • StringBuilder insert(int offset, char c)
    将 char 参数的字符串表示形式 插入 此序列中。
  • char charAt(int index)
    返回此序列中指定索引处的 char 值。
  • StringBuilder deleteCharAt(int index)
    移除此序列指定位置上的 char。
  • int capacity()
    返回当前容量。
  • void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
    将字符从此序列复制到目标字符数组 dst。
  • int indexOf(String str)
    返回第一次出现的指定子字符串在该字符串中的索引。
  • int length()
    返回长度(字符数)。
  • StringBuilder replace(int start, int end, String str)
    使用给定 String 中的字符替换此序列的子字符串中的字符。
  • StringBuilder reverse()
    将此字符序列用其反转形式取代。
  • void setCharAt(int index, char ch)
    将给定索引处的字符设置为 ch。
  • void setLength(int newLength)
    设置字符序列的长度。
  • String toString()
    返回此序列中数据的字符串表示形式。

在 StringBuilder 上的主要操作是 appendinsert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串生成器中。

  • append 方法始终将这些字符添加到生成器的 末端
  • 而 insert 方法则在 指定的点 添加字符。
为什么是非线程安全?
  • 当多线程去访问同一个对象时,append方法是一个不同步的方法,count+=len不是一个原子操作,假如两个线程同时访问count并赋值后,在完成操作后,两个线程得到的count结果是同一个值。
  • 有两个线程同时执行append()方法,并且执行到了ensureCapacityInternal()方法,这时线程1的CPU时间片结束,线程2继续执行。线程2执行完成整个append()方法之后count值增加 len,当线程1继续执行str.getChars()方法时,count就等于线程2执行结束后新的count值,当进行拷贝时抛出ArrayIndexOutOfBoundsException异常。
	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;
    }
(2)StringBuffer 类型

线程安全,synchronized修饰 StringBuffer 中的方法,但是执行速度慢java.lang.StringBuffer包下。

常用方法与StringBuilder等价

	@Override
    public synchronized StringBuffer append(int i) {
        toStringCache = null;
        super.append(i);
        return this;
    }
(3)使用场景
  • String:适用于少量的字符串操作的情况
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
(4)Java9中的改进

Java9改进了String、StringBuffer、StringBuilder 的实现。在Java9之前字符串采用char[]数组来保存字符,占用两个字节;Java9+ 字符串采用byte[]数组再加一个encoding-flag字段来保存字符,占用一个字节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值