坚持学完这套JDK源码系列:告别CRUD开发从java.lang.String源码精读开始

作者:袖梨 2026-05-28

深入解析Java String类的核心机制与最佳实践,从不可变性到最新API特性全面剖析。

一、类概述

String作为Java中最常用的类,代表不可变的字符序列。这种不可变性为Java安全模型、字符串常量池和hashCode缓存提供了基础支持。

坚持看完这套 JDK 源码系列,彻底摆脱 CRUD 开发|专题:java.lang.String 源码解读

  1. 继承体系:直接继承自Object
  2. 实现接口:支持SerializableComparable<String>CharSequence
  3. 声明方式:采用public final class String定义,禁止被继承
  4. 核心职责:提供线程安全且可缓存的不可变文本表示

二、关键字段与常量

JDK 9+ 的内存布局变化

// JDK 17 源码(java.lang.String)// 核心字段:存储字符的字节数组
private final byte[] value;// 编码标志:0 = LATIN1(每字符1字节),1 = UTF16(每字符2字节)
private final byte coder;// 哈希值缓存,默认 0,延迟计算
private int hash;// 是否为哈希值 0(极少数真实哈希为 0 的情况下标记用)
private boolean hashIsZero;// 编码常量
static final byte LATIN1 = 0;
static final byte UTF16  = 1;

三、核心构造方法与静态工厂

// 1. 字面量(最常用,走常量池)
String s1 = "hello";// 2. 从 char 数组构造(会复制数组,保证不可变性)
char[] chars = {'h', 'e', 'l', 'l', 'o'};
String s2 = new String(chars);// 3. 从字节数组 + 指定字符集构造
byte[] bytes = "hello".getBytes(StandardCharsets.UTF_8);
String s3 = new String(bytes, StandardCharsets.UTF_8);// 4. 从 StringBuilder / StringBuffer 构造
StringBuilder sb = new StringBuilder("hello");
String s4 = new String(sb);// 5. 静态工厂 valueOf(对 null 友好,返回 "null")
String s5 = String.valueOf(42);        // "42"
String s6 = String.valueOf((Object)null); // "null"(不抛 NPE)

四、核心方法源码级解析

4.1 equals(Object anObject) — 值相等的判断

public boolean equals(Object anObject) {
    // 优化1:引用相等直接返回 true(同一对象必然内容相同)
    if (this == anObject) {
        return true;
    }
    // 优化2:快速类型检查,非 String 直接返回 false
    return (anObject instanceof String aString)
            && (!COMPACT_STRINGS || this.coder == aString.coder)
            && StringLatin1.equals(value, aString.value); // 或 StringUTF16.equals
}

实现要点

  1. this == anObject通过引用比较快速判断
  2. instanceof结合模式匹配完成类型检查
  3. 比较coder确保编码方式一致
  4. 最终委托给底层字节比较方法

4.2 hashCode() — 哈希值缓存机制

public int hashCode() {
    int h = hash;
    if (h == 0 && !hashIsZero) {
        // 延迟计算:第一次调用时才计算
        h = isLatin1() ? StringLatin1.hashCode(value)
                       : StringUTF16.hashCode(value);
        if (h == 0) {
            hashIsZero = true; // 真实哈希为0,打标记避免每次重算
        } else {
            hash = h; // 缓存结果
        }
    }
    return h;
}

哈希计算公式

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

4.3 intern() — 字符串常量池交互

public native String intern();

该本地方法由JVM实现:

  • 检查字符串常量池(位于堆的永久代/元空间)
  • 若池中存在相同内容,返回池中引用
  • 否则将当前字符串存入池中
String a = new String("hello"); // 堆上新对象
String b = "hello";             // 常量池
System.out.println(a == b);         // false
System.out.println(a.intern() == b); // true ← intern 后指向同一常量池对象

4.4 substring(int beginIndex, int endIndex) — 子串截取

public String substring(int beginIndex, int endIndex) {
    // 边界检查
    int length = length();
    checkBoundsBeginEnd(beginIndex, endIndex, length);
    // 计算子串长度
    int subLen = endIndex - beginIndex;
    // 快捷路径:整个字符串
    if (beginIndex == 0 && endIndex == length) {
        return this;
    }
    return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                      : StringUTF16.newString(value, beginIndex, subLen);
}

4.5 String.format / formatted()(JDK 15+)

// 传统方式
String result = String.format("Hello, %s! You are %d years old.", "Alice", 30);// JDK 15+ 实例方法(更简洁)
String result2 = "Hello, %s! You are %d years old.".formatted("Alice", 30);

内部使用java.util.Formatter,支持%s(字符串)、%d(整数)、%f(浮点)、%n(换行)等格式符。

五、设计思想与演进

特性JDK 8JDK 9–17
内部存储char[](固定 2 字节/字符)byte[] + coder(1 或 2 字节/字符)
substringJDK 7u6 起已复制数组同左,无变化
String.join已引入(JDK 8)性能持续优化
isBlank/strip不存在JDK 11 引入,支持 Unicode 空白
repeat(int)不存在JDK 11 引入
lines()不存在JDK 11 引入,返回Stream<String>
formatted()不存在JDK 15 引入
indent(int)不存在JDK 12 引入,文本缩进
文本块"""不支持JDK 15 正式发布(JEP 378)

六、易错点与常见误区

误区1:用==比较字符串内容

String a = new String("hello");
String b = new String("hello");
System.out.println(a == b);      // false!比较的是引用地址
System.out.println(a.equals(b)); // true 

误区2:在循环中用+拼接字符串

//  每次循环都创建新 String 对象,O(n²) 复杂度
String result = "";
for (int i = 0; i < 10000; i++) {
    result += i;
}//  使用 StringBuilder,O(n) 复杂度
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i);
}
String result = sb.toString();

误区3:trim()vsstrip()

String s = "u3000Hellou3000"; // u3000 是全角空格(Unicode 空白)
System.out.println(s.trim().length());  // 7(没去掉全角空格)
System.out.println(s.strip().length()); // 5(去掉了所有 Unicode 空白)

trim()只处理 ASCII 空白(<= ' '),strip()(JDK 11)使用Character.isWhitespace(),支持 Unicode 空白。

误区4:String的"不可变"不等于"线程安全的操作"

String对象本身不可变,但对String类型引用变量的读写,在多线程环境下仍需同步(volatile或锁)。

七、完整小示例

import java.util.StringJoiner;
import java.util.stream.Collectors;public class StringDemo {
    public static void main(String[] args) {
        // 1. 常量池 vs 堆对象
        String poolStr = "java";
        String heapStr = new String("java");
        System.out.println(poolStr == heapStr);          // false
        System.out.println(poolStr == heapStr.intern()); // true        // 2. JDK 11+ 新 API
        String blank = "   ";
        System.out.println(blank.isBlank());    // true
        System.out.println(blank.strip());      // ""
        System.out.println("ab".repeat(3));     // "ababab"        // 3. 文本块(JDK 15+)
        String json = """
                {
                    "name": "Alice",
                    "age": 30
                }
                """;
        System.out.println(json);        // 4. lines() 流式处理
        String multiLine = "line1nline2nline3";
        multiLine.lines()
                 .map(String::toUpperCase)
                 .forEach(System.out::println);        // 5. StringJoiner(比手动拼接更优雅)
        StringJoiner sj = new StringJoiner(", ", "[", "]");
        sj.add("apple").add("banana").add("cherry");
        System.out.println(sj); // [apple, banana, cherry]        // 6. hashCode 缓存验证
        String s = "hello";
        System.out.println(s.hashCode()); // 99162322
        System.out.println(s.hashCode()); // 99162322(第二次直接返回缓存)
    }
}

八、引申与预告

关联类

关系
StringBuilder可变字符序列,单线程拼接首选
StringBuffer线程安全版StringBuilder
StringJoinerJDK 8 引入,专为分隔符拼接设计
java.util.regex.PatternString.matches()底层实现
java.util.FormatterString.format()的实际执行者
StringConcatFactoryJDK 9+ 编译器拼接优化的核心
Charset/StandardCharsets字节数组与字符串转换编码

下次专题预告:java.lang.StringBuilder && StringBuffer

下期将深入探讨:

  • 动态扩容策略实现原理
  • append系列方法源码解析
  • 三者的性能对比与选型建议
  • JDK 9+的StringConcatFactory优化机制

通过本文全面解析Java String类的核心机制与最新特性,帮助开发者深入理解字符串处理的底层原理与最佳实践。

相关文章

精彩推荐