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

Java 中的 char、String、StringBuilder 与 StringBuffer 深度详解
(从底层原理到最佳实践,2026 最新版)

这四个类型是 Java 字符串处理的基石,几乎每天都会用到。掌握它们,能让你写出更高效、更安全的代码。

1. char —— 字符的基本单位

char c1 = 'A';           // 正确
char c2 = '中';          // 正确(Unicode)
char c3 = '\u4E2D';      // 十六进制 Unicode 表示“中”
char c4 = 65;            // 正确,ASCII 值 65 对应 'A'

核心特性

  • 底层类型:16 位无符号整数(2 字节),范围 0 ~ 65535(` ~\uFFFF`)。
  • 编码:Java 使用 UTF-16 编码(不是 UTF-8!)。
  • 注意事项
  • Java 9 之后,String 内部可能使用 byte[](Latin-1 优化),但 char 始终是 2 字节。
  • 代理对(Surrogate Pair):一个 emoji(如 😂)可能需要两个 char(高低代理对)。
    java String emoji = "😂"; System.out.println(emoji.length()); // 输出 2(不是 1!) System.out.println(emoji.codePointCount(0, emoji.length())); // 输出 1

最佳实践:日常很少单独使用 char,更多通过 String.codePointAt() 处理 Unicode 字符。

2. String —— 不可变字符串(Immutable)

2.1 核心特性

  • 不可变:一旦创建,内容无法修改。
  • 底层存储(JDK 9+):
  • private final byte[] value;
  • private final byte coder;LATIN1=0UTF16=1
  • 字符串常量池(String Pool):相同字面量只在堆中保存一份,节省内存。
  • 线程安全:天然线程安全(不可变)。

2.2 常见“陷阱”与原理

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");

System.out.println(s1 == s2);     // true  (常量池)
System.out.println(s1 == s3);     // false (new 在堆中新建对象)

字符串拼接的性能陷阱

String result = "";
for (int i = 0; i < 10000; i++) {
    result += "a";        // 每次都创建新 String 对象!O(n²) 时间复杂度
}

编译器在编译期会把常量拼接优化为 StringBuilder,但循环中+= 不会优化。

2.3 常用高效方法(JDK 11+ 推荐)

  • String.isBlank()String.lines()String.strip()
  • String.repeat(int count)
  • String.formatted(Object... args)(替代 String.format

3. StringBuilder —— 单线程高性能可变字符串

核心特性

  • 可变字符序列,底层是 char[](JDK 9+ 是 byte[])。
  • 非线程安全,速度最快。
  • 初始容量 16,超过时自动扩容(newCapacity = oldCapacity * 2 + 2)。

3.1 推荐使用场景

  • 循环拼接字符串
  • 单线程构建复杂字符串(JSON、手写 SQL、日志等)

3.2 最佳实践写法

// 推荐:预估容量,避免多次扩容
StringBuilder sb = new StringBuilder(128);

for (int i = 0; i < 10000; i++) {
    sb.append("a");
}

String result = sb.toString();   // 最后只调用一次 toString()

链式调用

String json = new StringBuilder()
        .append("{\"name\":\"")
        .append(name)
        .append("\",\"age\":")
        .append(age)
        .append("}")
        .toString();

4. StringBuffer —— 线程安全的可变字符串

核心特性

  • StringBuilder 几乎完全一样。
  • 所有公开方法都加了 synchronized
  • 性能比 StringBuilder 慢 20%~50%(锁开销)。

4.1 什么时候必须用 StringBuffer?

  • 多线程环境下共享同一个可变字符串对象。
  • 遗留代码(老项目中大量使用)。

现代推荐

  • 单线程 → StringBuilder
  • 多线程 → StringBuffer 或更好的选择:ConcurrentLinkedQueueCopyOnWriteArrayList + String.join() 等。

5. 三者性能对比(实测结论 2026)

操作StringStringBuilderStringBuffer
循环 10 万次拼接最慢(O(n²))最快较慢
单线程简单拼接最快
多线程共享修改安全(不可变)不安全安全
内存占用较高(新对象)最低较低

基准测试建议:使用 JMH(Java Microbenchmark Harness)验证具体场景。

6. 最佳实践总结(生产级建议)

  1. 字符串常量 → 直接用双引号 "hello"
  2. 单线程大量拼接 → 优先 StringBuilder,并预估初始容量
  3. 多线程共享可变字符串StringBuffer
  4. 多线程不共享 → 每个线程用自己的 StringBuilder
  5. 最终结果需要 String → 只在最后调用一次 toString()
  6. JDK 8+ 推荐使用 StringJoiner(更优雅):
   String result = Stream.of("a", "b", "c")
                         .collect(Collectors.joining(","));
  1. 大字符串处理 → 考虑 StringBuilder + append(char[]) 或零拷贝技术
  2. 避免
  • 循环中使用 String +=
  • new String("literal")(多此一举)
  • 不必要的 toString() 调用

7. 面试高频问题精讲

  • String 为什么不可变?
    安全性(类加载、String Pool)、线程安全、作为 HashMap Key 的可靠性。
  • StringBuilder 和 StringBuffer 的区别?
    线程安全 vs 性能,底层扩容机制完全相同。
  • new String(“abc”) 创建了几个对象?
    编译期 1 个(常量池) + 运行期 1 个(堆中),共 2 个。
  • substring() 方法的性能(JDK 7 vs JDK 8+)?
    JDK 7 之前返回原数组子视图(内存泄漏风险),JDK 7+ 复制新数组(安全但耗内存)。

掌握以上内容,你对 Java 字符串的理解就达到了中高级水平。

需要我继续补充以下任意内容,随时说:

  • String 源码深度解析(value、hash、coder)
  • StringBuilder 扩容源码 + 位运算详解
  • JMH 性能测试完整代码
  • Spring Boot 中字符串拼接的最佳实践
  • 与 Kotlin、Scala 字符串处理的对比

祝你写出又快又安全的 Java 字符串代码!🚀

文章已创建 5103

发表回复

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

相关文章

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

返回顶部