Java中的char、String、StringBuilder与StringBuffer 深度详解

Java 中的 char、String、StringBuilder 与 StringBuffer 深度详解
(2025–2026 面试/生产最常考点 + 实际使用建议)

1. 四者最核心对比表(背下来)

特性charStringStringBuilderStringBuffer
基本类型 / 引用类型基本类型引用类型(不可变对象)引用类型(可变字符序列)引用类型(可变字符序列)
是否线程安全是(不可变天然安全)(方法加 synchronized)
是否可变不可变(值类型)不可变可变可变
底层数据结构16位 Unicode 字符char[] + finalchar[](非 final)char[](非 final)
性能(单线程拼接)最差最好较差
性能(多线程拼接)好(但不适合频繁修改)最差(需手动加锁)较好(内置锁)
内存开销极小较大(每次修改产生新对象)中等中等(比 Builder 略大)
典型使用场景单个字符、switch case常量、键值、配置文件单线程高频拼接多线程高频拼接(极少用)
JDK 引入版本1.01.01.51.0
现代推荐指数(2025+)正常使用★★★★☆(大部分场景)★★★★★(推荐)★☆☆☆☆(基本不推荐)

2. char 的关键知识点(常被忽略)

  • 本质:16 位无符号整数(0 ~ 65535),对应 Unicode 码点
  • char vs Character:char 是基本类型,Character 是包装类(有缓存 -128~127)
  • 常见陷阱
char c1 = 'A';          // 65
char c2 = 65;           // 合法,等价于上面
char c3 = '\u0041';     // 合法,Unicode 转义

// 经典面试题
System.out.println('a' + 'b');           // 195 (char 自动提升为 int 相加)
System.out.println("" + 'a' + 'b');      // "ab"(字符串拼接)
  • Java 9+ 字符串底层压缩:如果字符串只包含 Latin-1 字符(0-255),则用 byte[] 存储(节省一半内存),但 char 本身仍是 2 字节。

3. String 的不可变性(Immutable)核心原理

为什么 String 是 final + 不可变?

  1. 安全性(ClassLoader、反射、常量池)
  2. 线程安全(天然)
  3. HashMap 等集合的 key 稳定性
  4. 字符串常量池(String Pool)复用

内存结构(JDK 8 → JDK 17+ 变化)

JDK 8及之前:
String 对象
  ↓
private final char value[];   // 指向字符数组
private final int hash;       // 缓存 hashCode

JDK 9+(Compact Strings):
String 对象
  ↓
private final byte[] value;   // 可能是 byte[] 或 char[]
private final byte coder;     // 0 = LATIN1, 1 = UTF16

经典面试题:下面代码创建了几个 String 对象?

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello").intern();

String s5 = s1 + "world";           // 新对象
String s6 = "hello" + "world";      // 编译期优化为 "helloworld"(常量池)

答案:

  • s1、s2 → 1 个(常量池)
  • s3 → 2 个(new + 常量池)
  • s4 → 1 个(intern 强制入池)
  • s5 → 至少 2 个(”world” + 拼接结果)
  • s6 → 1 个(编译期直接 “helloworld”)

4. StringBuilder vs StringBuffer 终极对比

共同点

  • 都继承 AbstractStringBuilder
  • 内部都是 char[](JDK9+ 是 byte[] + coder)
  • 扩容机制:newCapacity = oldCapacity * 2 + 2(JDK8+ 优化)

最大区别:线程安全实现方式

// StringBuffer(几乎所有 public 方法都加了 synchronized)
public synchronized StringBuffer append(char c) { ... }

// StringBuilder(无锁)
public StringBuilder append(char c) { ... }

真实性能对比(2025 常见压测结论)

场景StringStringBuilderStringBuffer推荐选择
单线程 10万次 append最慢(~500ms)最快(~15ms)中等(~80ms)StringBuilder
多线程 每个线程 1万次安全但慢不安全(数据错乱)安全但较慢StringBuffer 或 Concurrent 替代
现代微服务日志拼接首选几乎不用StringBuilder + 局部 synchronized

StringBuffer 现代替代方案(2025+ 推荐)

// 方案1:局部加锁(最常用)
StringBuilder sb = new StringBuilder();
synchronized (lock) {
    sb.append(...).append(...);
}

// 方案2:StringJoiner(JDK8+,更优雅)
StringJoiner joiner = new StringJoiner(",", "[", "]");
joiner.add("a").add("b");
String result = joiner.toString();

// 方案3:Collectors.joining()(Stream 场景)
String result = list.stream().collect(Collectors.joining(", ", "[", "]"));

5. 面试/生产中最常问的 10 个深度问题

  1. String a = “hello” + “world” 和 String b = a + “!” 底层发生了什么?
  2. new String(“hello”) 到底创建了几个对象?
  3. 为什么 String 的 hashCode 要缓存?
  4. StringBuilder 在扩容时容量是怎么计算的?
  5. StringBuilder 和 StringBuffer 的 char[] 什么时候从 char[] 变成 byte[]?
  6. intern() 方法在 JDK 6、7、8+ 的行为变化?
  7. 字符串常量池在 JDK 7+ 迁移到堆后有什么影响?
  8. 如何高效拼接大量字符串(百万级)?
  9. String 是线程安全的吗?在什么场景下不安全?
  10. StringBuilder.append() 为什么不线程安全?底层指令重排序可能导致什么问题?

6. 2025–2026 生产最佳实践总结

场景推荐工具理由 / 注意事项
常量、配置、日志模板String不可变 + 常量池复用
单线程、循环中频繁拼接StringBuilder性能最高
多线程共享一个 builder(极少见)StringBuffer 或 加锁优先加锁而非 StringBuffer
Stream / Collectors 拼接StringJoiner / joining代码最优雅
需要线程安全且高性能StringBuilder + 局部锁 或 ThreadLocal避免全局锁开销
JSON / 模板引擎输出StringBuilder大部分框架内部也这么做

一句话总结(面试背诵版):

char 是单个字符,String 是不可变字符串常量,StringBuilder 是单线程高性能可变字符序列,StringBuffer 是历史遗留的多线程安全版本(现已被 StringBuilder + 局部同步取代)。

需要我针对某个具体问题展开更详细的字节码分析、内存图、压测代码、JDK 版本差异对比吗?
可以直接告诉我你最想深挖的点。

文章已创建 4812

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部