
🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录
- Java字符串API:String/StringBuffer/StringBuilder详解
-
- [📝 文章摘要](#📝 文章摘要)
- [一、字符串核心基础:底层存储与不可变性 📖](#一、字符串核心基础:底层存储与不可变性 📖)
-
- [1.1 `String`类的不可变性定义](#1.1
String类的不可变性定义) - [1.2 `String`不可变性的底层实现(JDK8)](#1.2
String不可变性的底层实现(JDK8)) - [1.3 不可变性的实际体现:操作会创建新对象](#1.3 不可变性的实际体现:操作会创建新对象)
- [1.4 `String`不可变性的设计优势](#1.4
String不可变性的设计优势)
- [1.1 `String`类的不可变性定义](#1.1
- [二、字符串常量池:String的内存优化机制 🧠](#二、字符串常量池:String的内存优化机制 🧠)
-
- [2.1 字符串常量池的核心原理](#2.1 字符串常量池的核心原理)
- [2.2 `String`的两种创建方式:常量池vs堆内存](#2.2
String的两种创建方式:常量池vs堆内存) -
- [方式1:直接赋值(`String s = "Java"`)→ 常量池](#方式1:直接赋值(
String s = "Java")→ 常量池) - [方式2:`new`关键字创建(`String s = new String("Java")`)→ 堆内存](#方式2:
new关键字创建(String s = new String("Java"))→ 堆内存) - 方式3:手动入池(`intern()`方法)
- [方式1:直接赋值(`String s = "Java"`)→ 常量池](#方式1:直接赋值(
- [三、三大字符串类核心对比:可变性+线程安全+效率 🆚](#三、三大字符串类核心对比:可变性+线程安全+效率 🆚)
-
- [3.1 核心对比表(必记)](#3.1 核心对比表(必记))
- [3.2 核心设计差异:可变性的实现](#3.2 核心设计差异:可变性的实现)
- [3.3 自动扩容机制](#3.3 自动扩容机制)
- [3.4 线程安全性的实现](#3.4 线程安全性的实现)
- [四、三大字符串类常用方法实战:拼接/截取/替换/查找 📦](#四、三大字符串类常用方法实战:拼接/截取/替换/查找 📦)
-
- [4.1 核心拼接方法:`append()`(Buffer/Builder)/`+`(String)](#4.1 核心拼接方法:
append()(Buffer/Builder)/+(String)) -
- [`String`:`+` 拼接(底层自动优化为StringBuilder)](#
String:+拼接(底层自动优化为StringBuilder)) - [`StringBuffer`/`StringBuilder`:`append()` 拼接(核心方法)](#
StringBuffer/StringBuilder:append()拼接(核心方法))
- [`String`:`+` 拼接(底层自动优化为StringBuilder)](#
- [4.2 `String`类常用核心方法(开发高频)](#4.2
String类常用核心方法(开发高频)) -
- [🔍 查找相关](#🔍 查找相关)
- [✂️ 截取/分割](#✂️ 截取/分割)
- [✏️ 替换/修改](#✏️ 替换/修改)
- [📌 判断相关](#📌 判断相关)
- [📏 长度/转换](#📏 长度/转换)
- [4.3 实战案例:String类常用方法综合使用](#4.3 实战案例:String类常用方法综合使用)
- [4.4 `StringBuffer`/`StringBuilder`特有方法](#4.4
StringBuffer/StringBuilder特有方法)
- [4.1 核心拼接方法:`append()`(Buffer/Builder)/`+`(String)](#4.1 核心拼接方法:
- 五、实战场景:如何选择三大字符串类?🌟
-
- [5.1 选择`String`的场景](#5.1 选择
String的场景) - [5.2 选择`StringBuilder`的场景](#5.2 选择
StringBuilder的场景) - [5.3 选择`StringBuffer`的场景](#5.3 选择
StringBuffer的场景) - [5.4 经典实战案例:循环拼接字符串的优化](#5.4 经典实战案例:循环拼接字符串的优化)
- [5.1 选择`String`的场景](#5.1 选择
- [六、高频误区&避坑指南 ⚠️](#六、高频误区&避坑指南 ⚠️)
- [七、JDK9+字符串底层优化:`char[]` → `byte[]` 🚀](#七、JDK9+字符串底层优化:
char[]→byte[]🚀) -
- [7.1 优化原因](#7.1 优化原因)
- [7.2 优化后的底层源码(JDK9+)](#7.2 优化后的底层源码(JDK9+))
- [7.3 优化效果](#7.3 优化效果)
- [✍️ 写在最后](#✍️ 写在最后)

Java字符串API:String/StringBuffer/StringBuilder详解
✨ 知识回顾
上一篇我们吃透了Java所有类的根父类------Object类,掌握了toString()、equals()、hashCode()等核心方法的重写规则与实战用法,理清了==与equals()的区别、equals()和hashCode()的绑定约定,夯实了Java核心API的基础。本篇我们将学习Java开发中使用频率最高的API------字符串相关类 ,包括String、StringBuffer、StringBuilder三个核心类。我们会从字符串的底层存储、类的特性入手,逐一讲解三者的核心区别、常用方法,结合实战案例展示各自的使用场景,帮你掌握字符串的正确使用技巧,避免开发中的常见坑🚀!
📝 文章摘要
- 核心摘要 :本文讲解Java字符串三大核心类
String、StringBuffer、StringBuilder的底层实现、类的特性,深入剖析三者的可变性、线程安全性、执行效率核心区别,拆解字符串的常用操作方法(拼接、截取、替换、查找等),结合实战案例展示不同场景下的类选择策略,同时梳理字符串开发中的常见误区与优化技巧,让你能根据业务场景精准选择字符串类,写出高效、规范的字符串代码。 - 阅读时长:10分钟
- 适合人群&阅读重点
🎯 Java初学者:重点牢记三者的核心区别,掌握String类的常用方法,理解可变性与不可变性的差异。
📚 高校计算机专业学生:理清字符串的底层字符数组存储机制,理解String的不可变性设计原理,掌握字符串常量池的工作机制。
💻 初级开发工程师:根据业务场景(单线程/多线程、频繁拼接/少量拼接)选择合适的字符串类,优化字符串拼接的执行效率,避免内存浪费。
📖 面试备考者:熟记三者的可变性、线程安全性、执行效率区别,理解字符串常量池的作用,应对"String为什么是不可变的""StringBuffer与StringBuilder的区别"类高频面试题。
一、字符串核心基础:底层存储与不可变性 📖
Java中的字符串本质上是字符序列的封装 ,底层基于char[]字符数组(JDK9+后改为byte[]字节数组,节省内存)实现,而String类的不可变性是整个字符串体系的核心,也是理解三个类区别的关键。
1.1 String类的不可变性定义
String类是不可变类(Immutable Class) ,即一旦一个String对象被创建,其底层字符数组的内容和长度就无法被修改 ,任何对字符串的操作(如拼接、截取、替换)都不会修改原对象,而是创建一个新的String对象并返回。
1.2 String不可变性的底层实现(JDK8)
String类的不可变性由其底层源码的三个关键设计保证,这是面试高频考点:
java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// 1. 字符数组用private final修饰,私有且不可被重新赋值
private final char value[];
// 2. 哈希码缓存,private修饰
private int hash; // Default to 0
// 3. 无任何修改value数组的公共方法
}
三大设计保证不可变性
- 类用final修饰 :
String类被final修饰,无法被继承,避免子类重写方法破坏不可变性; - 字符数组用private final修饰 :
value[]是私有成员,外部无法直接访问,且final修饰使其引用地址无法被重新赋值(注意:final仅保证引用不变,不保证数组内容不变,只是String类未提供修改数组内容的方法); - 无修改数组的公共方法 :
String类仅提供获取字符数组内容的方法(如charAt()),未提供任何修改value[]数组内容的公共方法,底层数组无法被修改。
1.3 不可变性的实际体现:操作会创建新对象
对String对象的任何操作都不会修改原对象,而是生成新对象,这是开发中必须注意的点:
java
public class TestStringImmutable {
public static void main(String[] args) {
String s1 = "Java";
// 拼接操作:创建新对象,s1仍指向原"Java"
String s2 = s1 + "EE";
// 替换操作:创建新对象,s1仍指向原"Java"
String s3 = s1.replace('J', 'j');
System.out.println(s1); // Java(原对象未变)
System.out.println(s2); // JavaEE(新对象)
System.out.println(s3); // java(新对象)
System.out.println(s1 == s2); // false(不同对象)
}
}
1.4 String不可变性的设计优势
String的不可变性并非设计缺陷,而是Java的刻意设计,带来了三大核心优势:
- 线程安全:不可变对象的内容不会被修改,多线程环境下无需加锁,天然线程安全;
- 支持字符串常量池:不可变性保证了字符串常量池的实现(相同字符串仅存一份),大幅节省内存;
- 哈希码缓存 :
String对象的哈希码计算后会被缓存(hash属性),无需重复计算,提升HashMap、HashSet等集合的查找效率。
二、字符串常量池:String的内存优化机制 🧠
字符串常量池(String Pool)是JVM为优化字符串内存占用设计的内存区域 (属于方法区/元空间),核心作用是缓存字符串对象,避免重复创建 ,这是String类特有的优化,也是理解String创建方式的关键。
2.1 字符串常量池的核心原理
当创建字符串时,JVM会先在字符串常量池中查找是否存在内容相同的字符串对象:
- 若存在,直接返回常量池中该对象的引用,不创建新对象;
- 若不存在,在常量池中创建该字符串对象,再返回其引用。
2.2 String的两种创建方式:常量池vs堆内存
String有两种创建方式,对应不同的内存存储位置,也是面试高频考点,核心区别在于是否使用new关键字:
方式1:直接赋值(String s = "Java")→ 常量池
java
String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2); // true(指向常量池同一个对象)
执行流程:
- JVM在字符串常量池中查找是否有"Java"对象;
- 若无,在常量池中创建"Java"对象,将引用赋值给
s1; - 创建
s2时,常量池中已存在"Java",直接将引用赋值给s2,s1和s2指向同一个对象。
方式2:new关键字创建(String s = new String("Java"))→ 堆内存
java
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2); // false(指向堆中不同对象)
执行流程:
- JVM先在常量池中查找是否有"Java",若无则创建(步骤1);
- 在堆内存 中创建一个新的
String对象,将常量池中"Java"的字符数组复制到该对象; - 将堆中对象的引用赋值给
s1,每次new都会在堆中创建新对象,因此s1和s2指向不同对象。
💡 核心结论:==判断字符串是否相等时,仅直接赋值的相同字符串会返回true,new创建的字符串即使内容相同,==也返回false ,判断字符串内容相等必须使用equals()方法。
方式3:手动入池(intern()方法)
intern()方法可将堆中 的String对象手动加入字符串常量池,返回常量池中的引用:
java
String s1 = new String("Java").intern();
String s2 = "Java";
System.out.println(s1 == s2); // true(s1指向常量池对象)
三、三大字符串类核心对比:可变性+线程安全+效率 🆚
StringBuffer和StringBuilder是为了解决String频繁拼接时创建大量新对象、效率低下 的问题而设计的,三者的核心区别集中在可变性、线程安全性、执行效率三个维度,这是必须牢记的核心知识点,也是面试必考内容。
3.1 核心对比表(必记)
| 对比维度 | String |
StringBuffer |
StringBuilder |
|---|---|---|---|
| 可变性 | ❌ 不可变(创建新对象) | ✅ 可变(修改原对象) | ✅ 可变(修改原对象) |
| 线程安全性 | ✅ 天然线程安全(不可变) | ✅ 线程安全(方法加synchronized) | ❌ 非线程安全(方法无锁) |
| 执行效率 | 最低(频繁拼接创建新对象) | 中等(加锁有性能损耗) | 最高(无锁,单线程最优) |
| 底层实现 | char[]/byte[] |
char[]/byte[](可扩容) |
char[]/byte[](可扩容) |
| 初始化容量 | 固定(字符数组长度不变) | 初始容量16,自动扩容 | 初始容量16,自动扩容 |
| 适用场景 | 少量字符串操作、常量定义 | 多线程环境、频繁字符串操作 | 单线程环境、频繁字符串操作 |
3.2 核心设计差异:可变性的实现
StringBuffer和StringBuilder的可变性 体现在底层字符数组可扩容、可修改 ,且两者都继承自AbstractStringBuilder,底层实现基本一致:
java
// AbstractStringBuilder核心源码(可变性实现)
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value; // 无final修饰,可修改引用和内容
int count; // 实际存储的字符数
// 提供append()、insert()、delete()等修改方法,直接修改value数组
}
// StringBuffer继承AbstractStringBuilder,方法加synchronized
public final class StringBuffer extends AbstractStringBuilder implements Serializable, CharSequence {
@Override
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
}
// StringBuilder继承AbstractStringBuilder,方法无锁
public final class StringBuilder extends AbstractStringBuilder implements Serializable, CharSequence {
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
可变性核心原因
两者的底层value[]字符数组无final修饰 ,且类中提供了append()、delete()、replace()等直接修改字符数组的方法,操作时不会创建新对象,而是直接修改原对象的数组,因此是可变的。
3.3 自动扩容机制
StringBuffer和StringBuilder的初始容量为16个字符 ,当拼接的字符数超过当前数组长度时,会自动触发扩容 ,扩容规则为:
新容量 = 原容量 * 2 + 2 ,并将原数组的内容复制到新数组中。
💡 开发技巧:若已知字符串拼接的大致长度,可在创建时指定初始容量 (如new StringBuilder(100)),避免频繁扩容带来的数组复制性能损耗。
3.4 线程安全性的实现
StringBuffer的所有公共方法 都被synchronized关键字修饰,保证多线程环境下的方法执行原子性,因此线程安全,但加锁和释放锁会带来一定的性能损耗;StringBuilder的方法无任何同步修饰 ,执行效率更高,但多线程环境下同时修改会导致数据错乱,因此仅适用于单线程。
四、三大字符串类常用方法实战:拼接/截取/替换/查找 📦
字符串的核心操作包括拼接、截取、替换、查找、比较、转换 等,其中String类提供了最丰富的方法,StringBuffer和StringBuilder的核心方法为拼接相关(因设计初衷是解决拼接效率问题),且三者的方法名基本一致,便于记忆。
4.1 核心拼接方法:append()(Buffer/Builder)/+(String)
String:+ 拼接(底层自动优化为StringBuilder)
java
String s = "Java" + "EE" + "MySQL";
// 编译期优化:直接合并为"JavaEEMySQL",仅创建一个对象
String s1 = "JavaEE";
String s2 = s1 + "MySQL";
// 运行期拼接:底层自动创建StringBuilder,执行append()后转String
💡 注意:循环中使用String的+拼接会创建大量StringBuilder和String对象,效率极低,严禁使用!
StringBuffer/StringBuilder:append() 拼接(核心方法)
支持拼接任意数据类型(String、int、char、对象等),直接修改原对象,返回this(可链式调用):
java
// 单线程推荐StringBuilder,指定初始容量优化性能
StringBuilder sb = new StringBuilder(20);
sb.append("Java")
.append(8)
.append(" ")
.append(true);
System.out.println(sb.toString()); // Java8 true(转String)
// 多线程使用StringBuffer
StringBuffer sbf = new StringBuffer();
sbf.append("Java").append("EE");
System.out.println(sbf.toString()); // JavaEE
4.2 String类常用核心方法(开发高频)
String类提供了大量字符串操作方法,以下是开发中最常用的,按功能分类整理,便于记忆:
🔍 查找相关
char charAt(int index):获取指定索引的字符(索引从0开始);int indexOf(String str):查找子串第一次出现的索引,无则返回-1;int lastIndexOf(String str):查找子串最后一次出现的索引,无则返回-1;boolean contains(CharSequence s):判断是否包含指定子串。
✂️ 截取/分割
String substring(int beginIndex):从指定索引截取到末尾;String substring(int beginIndex, int endIndex):截取[beginIndex, endIndex)区间的子串(左闭右开);String[] split(String regex):按指定正则分割字符串,返回字符串数组。
✏️ 替换/修改
String replace(CharSequence target, CharSequence replacement):替换所有指定子串;String replaceFirst(String regex, String replacement):替换第一个匹配的子串;String toUpperCase():转为大写;String toLowerCase():转为小写;String trim():去除首尾空格(JDK11+用strip(),支持全角空格)。
📌 判断相关
boolean equals(Object anObject):判断内容是否相等(必用);boolean equalsIgnoreCase(String anotherString):忽略大小写判断内容相等;boolean isEmpty():判断是否为空字符串(长度为0);boolean startsWith(String prefix):判断是否以指定前缀开头;boolean endsWith(String suffix):判断是否以指定后缀结尾。
📏 长度/转换
int length():获取字符串长度;byte[] getBytes():转为字节数组;char[] toCharArray():转为字符数组;static String valueOf(Object obj):将任意对象转为字符串(万能方法,避免空指针)。
4.3 实战案例:String类常用方法综合使用
java
public class TestStringMethod {
public static void main(String[] args) {
String s = " JavaEE&MySQL&Redis ";
// 去除首尾空格
String s1 = s.trim();
System.out.println(s1); // JavaEE&MySQL&Redis
// 分割字符串
String[] arr = s1.split("&");
for (String str : arr) {
System.out.println(str); // JavaEE、MySQL、Redis
}
// 查找索引
System.out.println(s1.indexOf("MySQL")); // 6
System.out.println(s1.contains("Redis")); // true
// 截取子串
System.out.println(s1.substring(0, 5)); // JavaE
// 替换
System.out.println(s1.replace("&", "|")); // JavaEE|MySQL|Redis
// 转为小写
System.out.println(s1.toLowerCase()); // javaee&mysql&redis
// 任意对象转字符串
int num = 100;
System.out.println(String.valueOf(num)); // 100
}
}
4.4 StringBuffer/StringBuilder特有方法
除了继承AbstractStringBuilder的append(),两者还提供了常用的修改方法,支持链式调用:
delete(int start, int end):删除[start, end)区间的字符;insert(int offset, 任意类型):在指定索引插入任意类型数据;reverse():反转字符串;setCharAt(int index, char ch):修改指定索引的字符;toString():转为String对象(最终结果需转String使用)。
实战案例:StringBuilder常用方法
java
public class TestStringBuilder {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("Java");
// 追加
sb.append("EE"); // JavaEE
// 插入
sb.insert(4, "8"); // Java8EE
// 反转
sb.reverse(); // EE8avaJ
// 删除
sb.delete(0, 2); // 8avaJ
// 修改指定索引字符
sb.setCharAt(0, '9'); // 9avaJ
// 转String
String s = sb.toString();
System.out.println(s); // 9avaJ
}
}
五、实战场景:如何选择三大字符串类?🌟
开发中选择哪个字符串类,核心依据是两个场景条件 :是否频繁进行字符串修改(拼接/删除/插入) + 是否在多线程环境下使用,遵循以下原则可保证代码的效率和安全性:
5.1 选择String的场景
✅ 推荐场景:
- 字符串无需修改,仅做常量定义、存储、传递(如配置项、固定文本);
- 字符串操作次数极少(如少量的拼接、截取,非循环操作);
- 多线程环境下的字符串常量使用(天然线程安全)。
❌ 禁止场景:循环中进行字符串拼接(会创建大量对象,效率极低)。
5.2 选择StringBuilder的场景
✅ 推荐场景:
- 单线程环境 下的频繁字符串修改(拼接、删除、插入),如循环拼接、动态拼接字符串;
- 性能要求高的场景(无锁,执行效率最高)。
💡 开发首选 :单线程下的字符串动态操作,优先使用StringBuilder ,并建议指定初始容量(避免频繁扩容)。
5.3 选择StringBuffer的场景
✅ 推荐场景:
- 多线程环境 下的频繁字符串修改,如多线程日志拼接、多线程动态生成字符串;
- 要求线程安全,允许轻微性能损耗的场景。
❌ 禁止场景:单线程环境下使用(无意义的性能损耗,不如用StringBuilder)。
5.4 经典实战案例:循环拼接字符串的优化
反例:String循环拼接(效率极低,严禁使用)
java
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 每次创建新StringBuilder和String对象
}
正例:StringBuilder循环拼接(单线程,指定初始容量)
java
StringBuilder sb = new StringBuilder(10000); // 指定初始容量
for (int i = 0; i < 10000; i++) {
sb.append(i); // 直接修改原对象,无新对象创建
}
String s = sb.toString();
正例:StringBuffer循环拼接(多线程)
java
StringBuffer sbf = new StringBuffer(10000);
// 多线程拼接,如线程池执行
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
pool.submit(() -> {
sbf.append("test").append("|");
});
}
pool.shutdown();
System.out.println(sbf.toString());
六、高频误区&避坑指南 ⚠️
字符串开发中的误区多集中在不可变性、==与equals()使用、常量池、拼接优化等方面,避开这些坑能大幅提升代码的正确性和效率:
误区1:用==判断字符串内容是否相等
❌ 错误示例:
java
String s1 = new String("Java");
String s2 = new String("Java");
if (s1 == s2) { ... } // false,错误判断内容
✅ 正确做法:判断字符串内容相等,必须使用equals()方法 ,==仅能判断引用是否指向同一个对象。
误区2:循环中使用String的+拼接字符串
❌ 错误后果:创建大量StringBuilder和String对象,占用内存,触发频繁GC,效率极低;
✅ 正确做法:单线程用StringBuilder,多线程用StringBuffer,并指定初始容量。
误区3:认为String的+拼接永远效率低
❌ 错误认知:所有String拼接都效率低;
✅ 正确结论:编译期常量拼接 (如"Java"+"EE")会被JVM直接合并为一个字符串,无性能损耗,仅运行期拼接(如变量+常量)才会创建新对象。
误区4:忽略StringBuffer/StringBuilder的初始容量
❌ 错误做法:默认无参构造(初始容量16),频繁拼接导致多次扩容;
✅ 正确做法:已知拼接长度时,指定初始容量 ,避免数组复制的性能损耗,如new StringBuilder(100)。
误区5:认为String的trim()能去除所有空格
❌ 错误认知:trim()能去除所有空格;
✅ 正确结论:trim()仅能去除首尾的ASCII空格 (\u0020),无法去除全角空格、制表符等;JDK11+推荐使用strip()(去除所有空白字符)、stripLeading()(去除首部空白)、stripTrailing()(去除尾部空白)。
误区6:空指针异常:调用null字符串的方法
❌ 错误示例:
java
String s = null;
if (s.equals("Java")) { ... } // 抛出NullPointerException
✅ 正确做法:将常量写在前面 ,或使用Objects.equals():
java
// 方式1:常量在前
if ("Java".equals(s)) { ... }
// 方式2:使用Objects.equals()(推荐)
if (Objects.equals(s, "Java")) { ... }
误区7:混淆length()和size()
❌ 错误示例:String s = "Java"; s.size();(报错);
✅ 正确结论:字符串获取长度用length()方法 ,集合(List/Set/Map)获取长度用size()方法,数组获取长度用数组名.length(属性)。
七、JDK9+字符串底层优化:char[] → byte[] 🚀
JDK9对String、StringBuffer、StringBuilder的底层实现进行了优化,将**char[]字符数组改为 byte[]字节数组**,并增加了一个coder属性(编码标识),核心目的是节省内存空间。
7.1 优化原因
原char[]中每个字符占2个字节 (UTF-16编码),但实际开发中,大部分字符串都是拉丁字符 (如英文字母、数字),仅需1个字节(ISO-8859-1/UTF-8)即可存储,使用char[]会造成50%的内存浪费。
7.2 优化后的底层源码(JDK9+)
java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final byte[] value; // 改为byte[]
private final byte coder; // 编码标识:0=Latin1(1字节),1=UTF16(2字节)
private int hash; // 哈希码缓存
}
7.3 优化效果
- 拉丁字符(英文字母、数字):用
Latin1编码,每个字符占1字节,内存占用减少50%; - 非拉丁字符(中文、日文):用
UTF16编码,每个字符占2字节,与原char[]一致,无内存变化。
💡 该优化对开发无感知,所有字符串方法的使用方式不变,仅底层存储优化,提升了内存利用率。
✍️ 写在最后
- Java字符串三大核心类为
String、StringBuffer、StringBuilder,核心区别在于可变性、线程安全性、执行效率 ,String不可变、天然线程安全、效率最低,StringBuilder可变、非线程安全、效率最高,StringBuffer可变、线程安全、效率中等; String的不可变性由final类、private final char[]、无修改方法三大设计保证,其不可变性支撑了字符串常量池和哈希码缓存;- 字符串常量池是JVM的内存优化机制,直接赋值的字符串存在常量池,
new创建的字符串存在堆内存,判断内容相等必须用equals(); - 开发中类选择原则:少量操作/常量用
String,单线程频繁操作用StringBuilder(指定初始容量),多线程频繁操作用StringBuffer; - 避坑关键:禁止循环中用
String的+拼接,用equals()判断内容,常量在前避免空指针,JDK11+用strip()替代trim()。
字符串是Java开发中最基础、最常用的API,掌握三者的区别和用法是编写高效Java代码的前提。下一篇我们将学习Java包装类与自动装箱/拆箱,讲解8种基本数据类型对应的包装类、自动装箱拆箱的底层原理与实战坑点,完善Java基础数据类型体系💪!
❤️ 我是黎雁,专注Java基础与实战分享,关注我,一起从0到1吃透Java!
📚 后续文章预告:《Java包装类:自动装箱/拆箱+底层原理+实战避坑》
💬 评论区交流:你在字符串开发中遇到过哪些空指针或效率问题?或者对String的不可变性还有疑问,欢迎留言讨论~