最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
坚持学完这套JDK源码系列:告别CRUD开发从java.lang.String源码精读开始
时间:2026-05-28 17:35:01 编辑:袖梨 来源:一聚教程网
深入解析Java String类的核心机制与最佳实践,从不可变性到最新API特性全面剖析。
一、类概述
String作为Java中最常用的类,代表不可变的字符序列。这种不可变性为Java安全模型、字符串常量池和hashCode缓存提供了基础支持。

- 继承体系:直接继承自
Object - 实现接口:支持
Serializable、Comparable<String>和CharSequence - 声明方式:采用
public final class String定义,禁止被继承 - 核心职责:提供线程安全且可缓存的不可变文本表示
二、关键字段与常量
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
}
实现要点:
this == anObject通过引用比较快速判断instanceof结合模式匹配完成类型检查- 比较
coder确保编码方式一致 - 最终委托给底层字节比较方法
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 8 | JDK 9–17 |
|---|---|---|
| 内部存储 | char[](固定 2 字节/字符) | byte[] + coder(1 或 2 字节/字符) |
substring | JDK 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 |
StringJoiner | JDK 8 引入,专为分隔符拼接设计 |
java.util.regex.Pattern | String.matches()底层实现 |
java.util.Formatter | String.format()的实际执行者 |
StringConcatFactory | JDK 9+ 编译器拼接优化的核心 |
Charset/StandardCharsets | 字节数组与字符串转换编码 |
下次专题预告:java.lang.StringBuilder && StringBuffer
下期将深入探讨:
- 动态扩容策略实现原理
append系列方法源码解析- 三者的性能对比与选型建议
- JDK 9+的
StringConcatFactory优化机制
通过本文全面解析Java String类的核心机制与最新特性,帮助开发者深入理解字符串处理的底层原理与最佳实践。
相关文章
- 《三国志战略版》5月10日版本更新公告 05-28
- 星语相机app如何修复老照片 05-28
- 专业体育app盘点:热门体育软件精选推荐 05-28
- 星露谷物语万象晶球可以获取哪些物品 05-28
- 三国志战略版如何搭建营帐:三国志战略版营帐建造步骤详解 05-28
- 三国志战略版天师举义剧本解析 全新剧本玩法设定详解 05-28