发布时间:2026/9/6 7:02:16
坚持看完这套 JDK 源码系列,彻底摆脱 CRUD 开发|专题:java.lang.String 源码解读 一、类概述String是 Java 中使用最频繁的类代表不可变的字符序列。它的不可变性是 Java 安全模型、字符串常量池和hashCode缓存的基石。继承体系Object直接父类实现接口Serializable、ComparableString、CharSequence声明方式public final class Stringfinal禁止被继承核心职责提供线程安全、可缓存的不可变文本表示类比String就像已经刻好的石碑——你可以复制一块新石碑创建新 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;⚠️JDK 8 vs JDK 9 的重大变化JDK 8内部用char[] value每个字符固定占2 字节UTF-16。JDK 9Compact Strings改为byte[] valuebyte coder。若字符串全为拉丁字母Latin-1每字符仅占1 字节内存节省约 50%。这是 JEP 254 引入的优化对 ASCII 密集型应用如 HTTP 路径、JSON key效果显著。三、核心构造方法与静态工厂// 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为什么new String(char[])要复制数组如果不复制外部代码改了chars[0]就破坏了 String 的不可变性。这是**防御性复制Defensive Copy**设计模式。四、核心方法源码级解析4.1equals(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 模式匹配JDK 16 语法一步完成类型检查和类型转换。coder比较LATIN1 的 String 和 UTF16 的 String 内容编码不同coder 不同直接排除。最终委托给StringLatin1.equals或StringUTF16.equals进行字节级比较。4.2hashCode()— 哈希值缓存机制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]为什么用 3131 是奇质数减少哈希冲突。31 * x可以被 JIT 优化为(x 5) - x避免乘法指令性能更好。实验表明 31 在英文单词集合上碰撞率极低。⚠️hashIsZero的意义极少数字符串的哈希值正好是 0如空字符串的哈希是 0 么不是空串hash 0但空串进入的是h 0 !hashIsZero分支会每次重算——这是 JDK 9 之前的一个性能 BugJDK 9 通过hashIsZero标志修复了。4.3intern()— 字符串常量池交互public native String intern();这是一个本地方法native由 JVM 实现检查字符串常量池String PoolHotSpot 中存于堆的永久代/元空间。若池中已有相同内容的字符串返回池中引用。否则将当前字符串放入池中返回该引用。String a new String(hello); // 堆上新对象 String b hello; // 常量池 System.out.println(a b); // false System.out.println(a.intern() b); // true ← intern 后指向同一常量池对象⚠️慎用intern()大量调用会使常量池膨胀可能导致 GC 压力增大。适合固定的少量字符串如枚举键不适合动态内容。4.4substring(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); }⚠️JDK 7u6 的重大变化JDK 6 的substring共享原字符串的char[]通过offset和count字段引用会导致内存泄漏大字符串无法被 GC。JDK 7u6 后改为复制子数组彻底解决此问题但每次substring都会产生新数组时间复杂度 O(n)。4.5String.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[] coder1 或 2 字节/字符substringJDK 7u6 起已复制数组同左无变化String.join已引入JDK 8性能持续优化isBlank/strip不存在JDK 11 引入支持 Unicode 空白repeat(int)不存在JDK 11 引入lines()不存在JDK 11 引入返回StreamStringformatted()不存在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; } // ✓ 使用 StringBuilderO(n) 复杂度 StringBuilder sb new StringBuilder(); for (int i 0; i 10000; i) { sb.append(i); } String result sb.toString();注意单行字符串拼接如Hello name !由 JDK 编译器JDK 9 用invokedynamicStringConcatFactory自动优化为高效拼接无需手动用StringBuilder。问题在于循环中累积拼接。❌ 误区3trim()vsstrip()String s \u3000Hello\u3000; // \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 空白。❌ 误区4String的不可变不等于线程安全的操作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 line1\nline2\nline3; 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可变字符序列单线程拼接首选与String可互相转换StringBuffer线程安全版StringBuilder方法均加synchronizedStringJoinerJDK 8 引入专为分隔符拼接设计底层用StringBuilderjava.util.regex.PatternString.matches()、split()的底层实现者java.util.FormatterString.format()的实际执行者StringConcatFactoryJDK 9 编译器拼接优化的核心invokedynamic实现Charset/StandardCharsets字节数组 ↔ 字符串转换时指定编码 下期专题预告java.lang.StringBuilderStringBuffer明天我们将深入StringBuilder的动态扩容策略ensureCapacityInternal容量×22的原因append系列方法源码如何高效追加各种类型StringBuildervsStringBuffervsString三者的性能对比与使用场景选择JDK 9 的StringConcatFactory编译器如何用invokedynamic优化拼接今日小结String的不可变性由private final byte[] valuefinal class双重保障JDK 9 的 Compact Strings 是减少内存占用的重要优化hashCode的缓存设计是空间换时间的经典案例日常使用中牢记用equals比较内容、strip替代trim、循环内用StringBuilder拼接。

相关新闻

2026/9/6 7:02:16

AI写小说百万字成本实测:同样100万字,账单差了40倍

AI写小说一百万字大约消耗1000万输入token和200万输出token,同样一百万字不同模型的账单能差40倍。省钱的关键不是换便宜模型,而是别整本塞上下文:只召回用得上的部分能省三到六成,缓存省五到六成,模型分档能省七成。蛙…

2026/9/6 7:02:16

服务组装和组合有什么区别

在软件架构(尤其是 Web 服务、微服务、SOA)领域,“服务组装”和“服务组合”经常被混用,但在学术理论和工程实践层面,它们有非常明确的界限。 核心区别在于:组合(Composition)关注“…

2026/9/6 7:02:16

三大开源DDS实现对比:Fast DDS、Cyclone DDS与OpenDDS选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/6 7:57:18

Agentic AI在能源设备运维中的市场渗透率未来五年增长预测

摘要2026年,全球电力行业数字员工市场达到约47.2亿美元规模,预计2030年突破128.6亿美元,年复合增长率超过22.3%。电网侧数字员工在智能巡检、故障隔离与恢复方面的渗透率已提升至31%,每年减少非计划停电时间约1.7万小时。全球范围…

2026/9/6 7:57:18

Vibe Coding 代码审查规范

1. 背景与目标 “Vibe Coding”通常指借助 AI 编程助手(如 GitHub Copilot、Cursor、Claude、ChatGPT 等)以自然语言描述意图,快速生成代码的开发方式。这种方式极大提升了原型开发效率,但也容易引入以下问题: 代码风格不一致 安全漏洞(如注入、越权、敏感信息泄露) 性…

2026/9/6 7:57:18

百考通的期刊论文功能,精准覆盖核心期刊与普通期刊两大场景

在学术研究领域,期刊论文的撰写是成果输出的关键环节,却也让众多科研工作者与学生倍感压力:选题迷茫、逻辑梳理困难、格式规范复杂、内容提炼耗时,严重拖慢了学术成果的发表节奏。百考通(https://www.baikaotongai.com…

2026/9/6 7:57:18

本地部署AI推理服务实战:从模型加载到安全评估完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/6 7:57:18

iPhone 17若用回LCD:从PWM频闪到护眼,屏幕技术之争的真相

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/6 7:52:18

AI率检测工具免费版能测什么?报告定位和字数限制怎么比较?

AI率检测工具免费版能测什么?报告定位和字数限制怎么比较? 论文初稿刚写完,多数人的第一个动作不是改,而是先找个免费的AI率检测工具摸个底。摸完底之后卡住的人也最多:免费版丢回来一个总分,比如AIGC疑似…

2026/9/6 0:06:59

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/6 0:06:59

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/6 0:06:59

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/6 0:06:59

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/6 0:06:59

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/6 0:06:59

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/5 2:45:13

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/5 2:30:42

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/5 2:46:50

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…