String、StringBuilder与StringBuffer

可变性

String是不可变的

StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder类中也是使用字符数组保存字符串,但没有使用final和private关键字修饰,最关键的是AbstractStringBuilder类还提供了很多修改字符串的方法,例如append()方法

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    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;
    }

    ...
}

线程安全性

String中的对象是不可变的,也可以理解为常量,线程安全

AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法

StringBuilder没有对方法加同步锁,所以是非线程安全的;StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的

性能

每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象

StringBuilder与StringBuffer每次对对象本身进行操作,而不是生成新的对象并改变对象引用

相同情况下使用StringBuilder与相比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的风险

使用总结

操作少量的数据:String

单线程操作字符串缓冲区下操作大量数据:StringBuilder

多线程操作字符串缓冲区下操作大量数据:StringBuffer

String为什么是不可变的

1.保存字符串的数组被final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法

2.String类被final修饰导致其不能被继承,进而避免了子类破坏String的不可变性

字符串拼接用“+”还是StringBuilder

Java语言本身并不支持运算符重载,“+”和“+=”是专门为String类重载过的运算符,也是Java中仅有的两个重载过的运算符

代码:

String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;

对应的字节码:

字符串对象通过“+”的字符串拼接方式,实际上是通过StringBuilder调用append()方法实现的,拼接完成后调用toString()得到一个String对象

若在循环内使用“+”进行字符串的拼接,存在比较明显的缺陷:编译器不会创建单个StringBuilder以复用,从而导致创建过多的StringBuilder对象

代码:

String[] arr = {"he", "llo", "world"};
String s = "";
for (int i = 0; i < arr.length; i++) {
    s += arr[i];
}
System.out.println(s);

对应的字节码:

若直接使用StringBuilder对象进行字符串拼接,就不会存在这个问题

代码:

String[] arr = {"he", "llo", "world"};
StringBuilder s = new StringBuilder();
for (String value : arr) {
    s.append(value);
}
System.out.println(s);

对应的字节码:

Java9新特性

存储方式

在Java9之后,String、StringBuilder与StringBuffer的实现改用byte数组存储字符串

新版的String支持两个编码方案:Latin-1和UTF-16,若字符串中包含的汉字没有超过Latin-1可表示范围内的字符,就会使用Latin-1作为编码方案,Latin-1编码方案下,byte占一个字节,char占两个字节,byte相较char节省一半的内存空间

JDK官方表示绝大部分字符串对象只包含Latin-1可表示的字符

public final class String implements java.io.Serializable,Comparable<String>, CharSequence {
    // @Stable注解表示变量最多被修改一次, 称为“稳定的”
    @Stable
    private final byte[] value;
}

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    byte[] value;

    ...
}

字符串拼接 

使用“+”进行字符串拼接会产生大量的临时对象的问题在JDK9中得到解决,在JDK9中,字符串相加“+”改为用动态方法makeConcatWithConstants()实现,而不是大量的StringBuilder

JDK9之后,可以放心使用“+”进行字符串拼接了

还在无脑用 StringBuilder?来重温一下字符串拼接吧 - 掘金 (juejin.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值