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快一些。

相关推荐
执笔画流年呀21 小时前
⾃动化测试常⽤函数
java·dubbo
我星期八休息21 小时前
扩展— TCP 全连接队列与 tcpdump 抓包
linux·服务器·开发语言·前端·网络·tcp/ip·tcpdump
疯狂打码的少年21 小时前
【面向对象】UML概述(基本构造块:事物/关系/图)
java
小小代码狗1 天前
PHP 常用函数与变量参考文档
开发语言·安全·php
一水1 天前
TTFT优化:一个方案的5次推倒重来
java·ai·状态模式
编程阿峰1 天前
Python 环境配置(二)安装jupyter、matplotlib、numpy库
开发语言·python·jupyter·numpy·matplotlib
AC赳赳老秦1 天前
CSDN 技术社区数据采集:OpenClaw 抓取公开技术热帖,生成领域技术热点周报
java·大数据·前端·数据库·python·php·openclaw
磁爆步兵1 天前
内存分区:程序运行的核心秘密
java·开发语言·jvm
此生决int1 天前
深入理解C++系列(06)——模版初阶与STL
开发语言·c++
流星白龙1 天前
【Docker】4.NameSpace空间隔离实战
java·运维·docker