java基础的面试高频03:String、StringBuffer、StringBuilder 三者区别 + String 不可变原理详解

String相关知识点属于 Java 面试万年经典考题,不管是校招还是社招,一定会问到String、StringBuffer、StringBuilder三者的区别、适用场景,以及String为什么设计成不可变。很多开发工作几年的程序员,也只停留在线程安全的浅层答案,今天深入源码拆解底层原理。

一、String:不可变字符串

1. String 不可变底层原理

从 Java9 之前,String底层使用private final char[] value字符数组存储字符串内容;Java9 之后优化为byte[]字节数组。

两个核心关键字决定不可变:

  • private:字符数组对外不可访问,无法在类外部修改数组元素;

  • final:数组引用地址不可更改,不能指向新的数组;

所有字符串拼接、截取、替换操作,都不会修改原字符串,而是在堆中创建一个全新的 String 对象,原对象会被垃圾回收。

2. String 不可变的三大设计优势

  • 字符串常量池复用:相同字面量字符串可以复用常量池中的对象,节省内存;

  • 作为 HashMap 的 key 安全可靠:哈希值可以缓存,不会因为字符串内容变化导致哈希值改变;

  • 线程安全:不可变对象天然线程安全,多线程并发场景下无需同步锁。

3. 常量池小知识点

直接双引号赋值的字符串会进入字符串常量池复用,new String("xxx")会在堆创建对象,同时可能在常量池创建字面量。

二、StringBuffer 与 StringBuilder:可变字符串

两者底层都是char[]数组(没有 final 修饰),默认初始化容量 16,当字符串长度超过数组容量会自动扩容,字符串拼接时直接在原数组上修改,不会频繁创建新对象,适合大量字符串拼接场景。

核心区别:线程安全性

  • StringBuffer:绝大多数方法被synchronized关键字修饰,是线程安全的,多线程并发拼接场景使用,加锁会带来一定性能损耗;

  • StringBuilder:没有加同步锁,线程不安全,单线程环境下性能最优,日常开发字符串拼接首选。

三、三者详细对比 & 适用场景

类型 可变性 线程安全 性能 适用场景
String 不可变 线程安全 频繁拼接性能差 字符串很少修改、常量定义、作为 Map 的 key
StringBuffer 可变 线程安全 中等 多线程环境下大量字符串拼接
StringBuilder 可变 线程不安全 最优 单线程下高频字符串拼接(日常开发 95% 场景)

经典场景总结

  • 少量字符串拼接:直接使用String +即可,编译器会优化为 StringBuilder;

  • 循环内大量字符串拼接:禁止使用 String,必须用 StringBuilder;

  • 多线程共享场景字符串拼接:使用 StringBuffer。

四、面试高频追问

1. 为什么 String 设计为不可变?(常量池、哈希缓存、线程安全三点)

  • 实现字符串常量池复用,节省内存。
    String不可变是字符串常量池能够实现的前提。

    字符串常量池会缓存我们通过双引号直接创建的字符串字面量,多个引用可以同时指向常量池里同一个String对象。

    如果String是可变的,一旦其中一个引用修改了字符串内容,所有指向该对象的引用都会被连带修改,常量池复用机制就彻底失效,会造成大量重复字符串对象频繁创建,极大浪费内存。

  • 可以缓存 hashCode,提升哈希容器的性能
    String经常作为HashMap、HashSet、HashTable等哈希集合的key。

    String类内部会缓存hashCode值,第一次调用hashCode()计算后就会保存下来,后续每次获取哈希值直接复用缓存结果,不需要重复计算,效率极高;

    如果String可变,一旦字符串内容发生修改,它的hashCode就会跟着变化。当我们存入HashMap后再修改key内容,再次根据key去取值时,会因为哈希值改变找不到原来的存储位置,出现数据丢失、存取错乱的严重问题。

  • 天然线程安全,支持多线程并发场景

    不可变对象一旦创建就无法修改内容,多个线程同时访问同一个String对象时,不存在并发修改数据的情况,不需要加同步锁就能保证线程安全。

    多线程环境下可以安全地把同一个String对象在多个线程间共享传递;

    不会出现一个线程修改字符串、另一个线程读取到脏数据的并发问题,省去了同步加锁的性能开销,并发场景下稳定性更好。

  1. StringBuilder 默认容量多少?扩容机制是什么?

默认容量 16,扩容规则:新容量 = 原容量 * 2 + 2,若扩容后依旧不够则直接使用当前字符串长度作为新容量。

posted @ 2026-07-01 22:57  桃桃不淘1  阅读(0)  评论(0)    收藏  举报