当前位置: 首页 > news >正文

Java String 类深入解析

Java String 类深入解析

一、前言

在 Java 开发中,String 字符串是使用频率最高的类之一,无论是日常业务开发、数据处理还是框架源码,都离不开 String。但很多开发者只停留在 new String()equals()concat() 等基础用法上,对其底层原理、内存机制、性能优化一知半解,极易写出 Bug 或低效代码。

本文将从源码结构、不可变性、内存存储、常用方法解析、字符串拼接、常见面试点六个维度,全面剖析 Java String 类,帮你从根源吃透字符串。

二、String 类基础定义与源码结构

1. 类定义

Stringjava.lang 包下的核心类,被 final 修饰,不能被继承,实现了三个核心接口:

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {// 源码核心部分
}
  • Serializable:支持序列化,方便网络传输、持久化存储
  • Comparable<String>:支持字符串自然排序
  • CharSequence:字符序列接口,与 StringBuilderStringBuffer 统一规范

2. 核心成员变量

// 用于存储字符串的字符数组,final 修饰,不可修改
private final char value[];
// 字符串的哈希码,缓存哈希值,避免重复计算
private int hash; 

关键结论String 的本质是一个不可变的 char 数组,所有字符串操作都不会修改原对象,而是生成新对象。

三、String 不可变性:核心特性与底层原理

1. 什么是不可变性?

一旦 String 对象被创建,其字符内容、长度、哈希值都无法被修改

任何对字符串的操作(截取、替换、拼接),都会返回新的 String 对象,原对象保持不变。

示例代码:

public class StringImmutableTest {public static void main(String[] args) {String str = "Hello";// 看似修改,实际返回新对象str = str.replace("H", "h"); System.out.println(str); // 输出 hello}
}

执行 replace 后,原 "Hello" 对象并未改变,只是 str 引用指向了新的 "hello" 对象。

2. 不可变性的实现原因

  1. 类被 final 修饰:禁止继承,避免子类破坏不可变性
  2. 存储字符的 value 数组被 final 修饰:数组引用无法指向新内存
  3. value 数组被 private 修饰:外部无法直接访问修改数组元素
  4. 所有方法都不修改原数组:替换、截取等操作均新建数组 + 新对象

3. 不可变性的优势

  1. 线程安全:多线程环境下无需加锁,直接使用
  2. 字符串常量池复用:节省内存,提升性能
  3. 哈希值缓存HashMapHashSet 中作为 Key 效率极高
  4. 安全可靠:避免敏感字符串被意外 / 恶意修改

四、String 对象创建方式与内存存储

String 有两种创建方式,内存位置、性能、复用性完全不同,这是面试和开发必考点。

1. 字面量创建

String str1 = "Java";
  • 直接在字符串常量池(String Constant Pool) 中创建对象
  • 若常量池已存在 "Java",直接返回引用,不会新建对象
  • 内存复用,性能最优,无冗余对象

2. new String () 创建

String str2 = new String("Java");
  • 第一步:检查常量池,无 "Java" 则在常量池创建
  • 第二步:在堆内存中新建 String 对象,指向常量池的字符数组
  • 无论常量池是否有值,堆中一定会创建新对象
  • 产生冗余对象,不推荐使用

3. 两种创建方式对比

public class StringCreateTest {public static void main(String[] args) {String str1 = "Java";String str2 = "Java";String str3 = new String("Java");String str4 = new String("Java");// true  :常量池复用,同一引用System.out.println(str1 == str2); // false :堆中不同对象System.out.println(str3 == str4); // false :str1(常量池),str3(堆)System.out.println(str1 == str3); }
}

== 与 equals () 区别

  • ==:基本类型比较值,引用类型比较内存地址
  • String.equals():重写了方法,比较字符串内容,开发中判断字符串相等必须用 equals()

五、String 常用方法源码 + 代码分析

1. equals () 方法:内容比较

// 源码简化版
public boolean equals(Object anObject) {// 1. 引用相同,直接返回 trueif (this == anObject) {return true;}// 2. 判断是否为 String 类型if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;// 3. 长度相同,再逐字符比较if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}// 4. 类型/长度/字符不同,返回 falsereturn false;
}

使用示例

String a = "hello";
String b = new String("hello");
// true,比较内容而非地址
System.out.println(a.equals(b)); 

2. hashCode () 方法:哈希值计算

// 源码:哈希公式 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;
}
  • 选用 31 :质数,减少哈希冲突,JVM 可优化(31*i = (i<<5)-i
  • 哈希值缓存:计算一次后永久缓存,提升 HashMap 性能

3. 其他高频常用方法

public class StringMethodTest {public static void main(String[] args) {String str = "  Hello Java  ";// 1. 去除首尾空格System.out.println(str.trim()); // Hello Java// 2. 获取长度System.out.println(str.length()); // 13// 3. 截取子串 [begin, end)System.out.println(str.substring(2, 7)); // Hello// 4. 转大写/小写System.out.println(str.toUpperCase()); //   HELLO JAVA  // 5. 判断是否包含子串System.out.println(str.contains("Java")); // true// 6. 字符串分割String[] arr = "a,b,c".split(",");// 7. 字符替换"Hello".replace('l', 'x');}
}

六、字符串拼接:原理与性能坑点

字符串拼接是开发中最常用操作,但不同方式性能天差地别

1. 直接 + 拼接

String a = "Hello" + " Java";
  • 编译期优化:直接合并为 "Hello Java",存入常量池
  • 变量拼接:a + b 底层会自动创建 StringBuilder 调用 append()

2. 循环中 + 拼接

// 低效代码:循环创建大量 StringBuilder 对象
String s = "";
for (int i = 0; i < 1000; i++) {s += i;
}

问题:每次循环都 new StringBuilder()append()toString(),产生大量临时对象,内存飙升、性能极差。

3. 最优拼接方案

  • 单线程 / 非线程安全StringBuilder(JDK 1.5+,性能最高)
  • 多线程 / 线程安全StringBuffer(方法加 synchronized

高效代码示例

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {sb.append(i);
}
String result = sb.toString();

七、String、StringBuffer、StringBuilder 对比(面试必考)

特性 String StringBuffer StringBuilder
不可变性 不可变 可变 可变
线程安全 安全 安全(synchronized) 不安全
性能
使用场景 少量字符串操作 多线程拼接 单线程高频拼接

总结

  • 固定字符串:用 String
  • 高频拼接:单线程用 StringBuilder,多线程用 StringBuffer

八、总结

  1. String 是不可变类:底层是 final char 数组,所有操作生成新对象
  2. 创建方式:字面量(常量池,推荐)、new String()(堆,慎用)
  3. 比较相等:必须用 equals()== 比较地址
  4. 拼接性能:循环拼接用 StringBuilder,禁止用 +
  5. 核心价值:不可变性保证了安全、高效、线程安全,是 Java 字符串设计的精髓
http://www.jsqmd.com/news/740852/

相关文章:

  • 如何快速成为斗地主高手:DouZero AI助手完整使用指南
  • 从零搭建GPU监控看板:用Python脚本+nvidia-smi定时抓取数据并可视化
  • 从色卡到代码:手把手教你用Python实现CIE 1931色度图转换(附完整代码)
  • 告别symbolicatecrash:Xcode 13.3后,用atos和CrashSymbolicator.py高效解析iOS崩溃日志
  • DBA不会告诉你的事:90%性能问题源于这5个SQL错误
  • 多平台内容矩阵分发系统 核心模块技术实现与技术选型详解
  • 深入RTA-OS内核:手把手教你配置ETAS ISOLAR多核工程的中断(Category1 vs Category2详解)
  • 从用量看板观察不同模型调用的 token 消耗与成本分布
  • 1 7.4.4 PPPoE 上网配置(拨号 → 新连接 → 宽带 PPPoE)
  • 3分钟上手:N_m3u8DL-CLI-SimpleG视频下载终极指南
  • Python分布式训练配置终极检查表(含NCCL_TIMEOUT、TF_CPP_MIN_LOG_LEVEL、RANK/WORLD_SIZE等11个关键环境变量避雷解析)
  • Windows HEIC缩略图完整教程:让资源管理器完美预览iPhone照片
  • 滴滴测开面试复盘:从两道烧脑的智力题到‘猜数字’算法,我的真实闯关记录
  • 网状Meta分析结果怎么看?手把手教你解读gemtc输出:异质性检验、节点分割与SUCRA排序图
  • 利用Taotoken模型广场为你的应用场景选择最合适的大模型
  • 【RAG】【ingestion03】摄取管道与文档管理示例
  • 告别手忙脚乱:用这些Verdi快捷键和窗口操作技巧,让你的仿真效率翻倍
  • 紧急!医疗设备量产前最后72小时:C语言采集线程死锁自愈方案(含FreeRTOS优先级翻转熔断机制源码)
  • 如何快速突破百度网盘限速:Python直链解析工具完整指南
  • 算法训练营第19天|1047. 删除字符串中的所有相邻重复项
  • 【Python分布式机器学习训练配置黄金标准】:20年ML基础设施专家亲授——避坑指南+5大核心参数调优清单
  • 分布式大模型推理实战:TP/PP/EP并行策略深度解析与架构选型指南
  • 3种强大方案:将旧电视盒子变身高性能Linux服务器的终极指南
  • 全域数学·数术本源·高维代数卷(72分册)【乖乖数学】
  • 告别手动刷图!E7Helper如何让你在《第七史诗》中解放双手
  • [具身智能-539]:云端就是一个大市场,什么都可以拿来卖,基础设施、平台、软件、远程API RPC, 工具,模型,智能体,游戏,装备、算力、能力、数据,“智慧”都被打包成了标准化的商品进行买卖
  • 2026 降 AI 软件排行:99.26% 达标率的嘎嘎降AI 凭什么稳坐第一?
  • 体验Taotoken平台在高峰时段的API请求成功率与路由效果
  • Windows 11终极怀旧游戏复活指南:用IPXWrapper轻松启用IPX/SPX协议
  • HAGeo系统:启发式辅助构造提升几何定理自动证明效率