String、StringBuffer、StringBuilder的应用场景

结论

String:不需要修改字符串,或者修改次数极少 。(String的+=其实不保证线程安全,String的+=操作底层是StringBuilder实现的,线程不安全)

StringBuffer:需要频繁修改字符串,多线程场景,需要保证线程的安全。

StringBuilder:需要频繁修改字符串,单线程场景,需要修改速度快。

原因

为什么String的访问速度快,为什么不能修改

String底层是一个常量,访问一个常量很快的,常量不支持修改。

java 复制代码
private final char value[]

既然String不能被修改,那+=是怎么回事

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s = "123";
        s += "456";
        System.out.println(s);
    }
}

输出

out 复制代码
123456

这样看起来,好像是在s后面添加了字符串"456",但是实际上,是改变了s的引用

下面这段代码可以验证,s已经不是之前的s了。

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s = "123";
        String b = s;
        System.out.println(b == s);
        s += "456";
        System.out.println(b);
        System.out.println(b == s);
    }
}

输出

out 复制代码
true
123
false

所以String的修改不是修改原字符串,而是替换字符创指向的引用。

真正的修改原来字符串会有下面的现象

java 复制代码
public class Main {
    public static void main(String[] args) {
        StringBuilder s = new StringBuilder("123");
        StringBuilder b = s;
        System.out.println(b == s);
        s.append("456");
        System.out.println(b);
        System.out.println(b == s);
    }
}

输出

out 复制代码
true
123456
true

验证StringBuilder线程不安全,StringBuffer线程安全

通过对比多线程场景下 StringBuilderStringBuffer 的拼接结果,验证二者的线程安全性:

一个线程负责在末尾添加"1",另一个线程负责在末尾添加"2"。

假如线程安全,那么要么全是1后面接全是2,要么全是2后面接全是1

如果结果不是这两个,那么线程不安全。

java 复制代码
public class Main {
    public static void main(String[] args) throws InterruptedException {
        int repeatCount = 100000;
        String result1 = "1".repeat(repeatCount) + "2".repeat(repeatCount);
        String result2 = "2".repeat(repeatCount) + "1".repeat(repeatCount);

        StringBuilder stringBuilder = new StringBuilder();
        Thread thread1 = new Thread(() -> {
            stringBuilder.append("1".repeat(repeatCount));
        });
        Thread thread2 = new Thread(() -> {
            stringBuilder.append("2".repeat(repeatCount));
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(stringBuilder.toString().equals(result1) || stringBuilder.toString().equals(result2));

        StringBuffer stringBuffer = new StringBuffer();
        thread1 = new Thread(() -> {
            stringBuffer.append("1".repeat(repeatCount));
        });
        thread2 = new Thread(() -> {
            stringBuffer.append("2".repeat(repeatCount));
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(stringBuffer.toString().equals(result1) || stringBuffer.toString().equals(result2));
    }
}

输出

out 复制代码
false
true

如果抛出ArrayIndexOutOfBoundsException,也能证明,ArrayIndexOutOfBoundsExceptionStringBuilder 线程不安全的典型严重表现 ,核心原因是多线程同时操作 StringBuilder 底层的字符数组时,导致数组扩容逻辑混乱,最终触发数组下标越界。

SrringBuffer线程安全的原因,StringBuilder速度快的原因

SrringBuffer对方法加了同步锁,保证了线程安全,但是限制了运行速度。

StringBuilder没有同步锁,所以线程不安全,由于没有锁的限制,执行速度却快。

面试

如果不修改或者修改次数极少,可以用String。

如果需要频繁修改,

多线程下,必须使用StringBuffer来保证线程安全。

单线程下,可以使用StringBuilder比StringBuffer快一些。

相关推荐
IT成长史15 小时前
Windows D盘安装Docker Desktop全流程(避坑+ECR镜像推送实战)
java·docker
电商API_1800790524715 小时前
API分享:获取淘宝商品价格|详情|主图|sku信息
开发语言·c++·人工智能·数据分析
一定要AK15 小时前
java基础
java·开发语言·笔记
splage15 小时前
Java进阶之泛型
java·开发语言
Meepo_haha15 小时前
python爬虫——爬取全年天气数据并做可视化分析
java
xiaohe0715 小时前
JAVA系统中Spring Boot 应用程序的配置文件:application.yml
java·开发语言·spring boot
羊小蜜.15 小时前
C++17: map & multimap—— 键值映射容器
开发语言·c++·stl
Eternity_GQM15 小时前
【CMake入门】
java·开发语言
爱丽_15 小时前
SQL 事务主线:ACID、隔离级别、MVCC 与一致性读
jvm·矩阵
糖猫猫cc15 小时前
Kite 实现逻辑删除
java·kotlin·orm·kite