String StringBuffer StringBuilder之间的区别

本文详细介绍了Java中的String、StringBuffer和StringBuilder类。String是不可变对象,每次修改都会创建新对象;StringBuffer和StringBuilder则允许字符串内容变化,其中StringBuffer线程安全,StringBuilder效率更高。Java9对字符串进行了优化,使用byte[]存储字符,节省空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.String

String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
这个是String类的解释,之前小咸儿看到这个情况,不能理解上述的解释,如下

String a = "123";
a = "456";
// 打印出来的a为456
System.out.println(a)

看到这里,小咸儿不明白了,这不是明明已经对他进行修改了吗?为什么还说他是一个不可变类呢?
经过小咸儿和小伙伴们的学习,明白String类不可变在哪里体现出来的,接下来就看一张上述a对象的内存存储空间图
在这里插入图片描述
可以看出来,再次给a赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成一个新的实例对象,并且指向“456”这个字符串,a则指向最新生成的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收。

2.StringBuffer

StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。

StringBuffer b = new StringBuffer("123");
b.append("456");
// b打印结果为:123456
System.out.println(b);

在看一下b对象的内存空间图:
在这里插入图片描述
所以说StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串。

3.StringBuilder

StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。

StringBuffer是如何实现线程安全的呢?
StringBuffer类中实现的方法:
在这里插入图片描述
StringBuilder类中实现的方法:
在这里插入图片描述
由此可见,StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。
记忆技巧:StringBuffer翻译为字符串缓存,缓存干活慢,但是安全。StringBuilder翻译为字符串创建者,创建者干活儿快,但不安全。

4.Java9的优化

Java9改进了字符串(包括String、StringBuffer、StringBuilder)的实现。在Java9以前字符串采用char[]数组来保存字符,因此字符串的每个字符占2字节;而Java9的字符串采用byte[]数组再加一个encoding-flag字段来保存字符,因此字符串的每个字符只占1字节。所以Java9的字符串更加节省空间,字符串的功能方法也没有受到影响。

<think>嗯,用户之前问过关于String类能否被继承的问题,现在又接着问StringStringBufferStringBuilder区别。看来他们可能是在学习Java字符串处理相关的知识,或者是在准备面试题。我需要详细分析这三个类的区别,确保回答全面且易懂。 首先,要明确这三个类的用途和设计目的。String是不可变的,而StringBufferStringBuilder是可变的。但用户可能需要知道为什么会有这样的设计,以及在实际应用中如何选择。 然后,线程安全是一个重点。StringBuffer是线程安全的,因为它的方法都是同步的,而StringBuilder不是。需要解释清楚线程安全的概念,以及在不同场景下的影响。 性能比较也很重要。通常StringBuilder在单线程下更快,但为什么?需要提到同步带来的开销,以及字符串操作的频繁程度如何影响性能。 接下来,可能需要举例说明它们的用法差异。比如字符串拼接时,使用String会生成多个对象,而使用StringBuilderStringBuffer可以避免这种情况,提高效率。 用户可能还会关心什么时候应该用哪个类。比如在单线程环境下推荐使用StringBuilder,多线程则用StringBuffer,而String适用于不需要修改字符串内容的情况。 此外,需要提到JVM对String的优化,比如字符串常量池,以及如何通过final关键字保证不可变性。还有,StringBufferStringBuilder的API几乎相同,但同步处理不同。 要避免过于技术化的术语,用简单易懂的语言解释。可能还要对比它们的继承结构,比如都继承自AbstractStringBuilder,但String是独立的。 最后,总结三者区别,强调线程安全、可变性和性能,并给出使用建议。这样用户能有一个清晰的比较,方便记忆和应用。</think>--- ### 🚩 核心区别总结 | 特性 | `String` | `StringBuffer` | `StringBuilder` | |--------------------|---------------------------|---------------------------|---------------------------| | **可变性** | 不可变 | 可变 | 可变 | | **线程安全** | 天然安全(不可变) | 安全(同步方法) | 不安全(无同步) | | **性能** | 高频修改性能差 | 线程安全但较慢 | 单线程下最快 | | **内存开销** | 易产生大量中间对象 | 对象复用 | 对象复用 | | **适用场景** | 常量字符串/少量操作 | 多线程字符串操作 | 单线程字符串操作 | --- ### 1️⃣ **不可变的 `String`** ```java String str = "Hello"; str += " World"; // 实际生成新对象:效率低下 ``` #### 核心特点: - **不可变性**:每次操作都生成新对象(适合缓存、哈希键等场景) - **字符串常量池优化**:相同字面量复用对象(`"a" + "b"` 编译期优化为 `"ab"`) - **线程安全**:无需额外同步(天然线程安全) #### 典型使用场景: - 配置文件读取 - HTTP 请求参数 - 数据库连接字符串等**不需要修改**的场景 --- ### 2️⃣ **线程安全的 `StringBuffer`** ```java StringBuffer sb = new StringBuffer(); sb.append("Hello").append(" World"); // 直接修改原对象 ``` #### 核心特点: - **同步方法**:所有关键方法用 `synchronized` 修饰(线程安全但性能较低) - **初始容量 16**:自动扩容(每次扩容 `旧容量*2 + 2`) - **方法链调用**:支持链式写法(`append().insert().delete()`) #### 典型使用场景: - 多线程环境下的日志拼接 - 并发服务器中的请求处理 - **JDK 1.0** 开始存在(早期方案) --- ### 3️⃣ **高性能的 `StringBuilder`** ```java StringBuilder sb = new StringBuilder(); sb.append("Java").reverse(); // 直接修改原对象 ``` #### 核心特点: - **无同步开销**:方法未加锁(单线程性能比 `StringBuffer` 高约 **10-15%**) - **初始容量 16**:扩容逻辑与 `StringBuffer` 相同 - **JDK 1.5+** 引入:为单线程场景优化 #### 典型使用场景: - 单线程下的 JSON/XML 构建 - 循环体内的字符串处理 - 高频字符串操作(如大数据处理) --- ### ⚡️ 性能对比测试 ```java // 测试代码片段 public void testPerformance() { int N = 100000; // String 测试 long start = System.currentTimeMillis(); String s = ""; for (int i = 0; i < N; i++) { s += i; // 每次循环创建新对象 } System.out.println("String: " + (System.currentTimeMillis() - start) + "ms"); // StringBuffer 测试 start = System.currentTimeMillis(); StringBuffer sb1 = new StringBuffer(); for (int i = 0; i < N; i++) { sb1.append(i); } System.out.println("StringBuffer: " + (System.currentTimeMillis() - start) + "ms"); // StringBuilder 测试 start = System.currentTimeMillis(); StringBuilder sb2 = new StringBuilder(); for (int i = 0; i < N; i++) { sb2.append(i); } System.out.println("StringBuilder: " + (System.currentTimeMillis() - start) + "ms"); } ``` #### 典型结果(10万次循环): - `String`: 约 **5000ms**(产生大量垃圾对象) - `StringBuffer`: 约 **10ms** - `StringBuilder`: 约 **5ms** --- ### 🛠️ 选型建议 1. **优先用 `String`** - 数据不经常变化 - 需要作为常量使用(如 `Map` 的键) 2. **多线程操作 → `StringBuffer`** - 多线程日志处理 - 并发环境下的字符串组装 3. **单线程操作 → `StringBuilder`** - 循环体内字符串操作 - SQL 语句拼接 - 高频字符串处理 --- ### 📌 底层实现揭秘 #### 共有特性(`StringBuffer`/`StringBuilder`): ```java // AbstractStringBuilder 中的核心存储 char[] value; // 动态数组存储字符 int count; // 实际字符数量 ``` #### 扩容机制: 1. 计算新容量 = `原容量*2 + 2` 2. 如果仍不足,直接扩容到**目标长度** 3. 调用 `Arrays.copyOf()` 复制数据 --- ### ❌ 常见误区 1. **错误认知**:`StringBuilder` 在所有场景都比 `StringBuffer` 快 **真相**:在单线程下更快,但在多线程中频繁同步时可能更慢 2. **错误写法**:在循环中用 `+` 拼接字符串 **修正**:应使用 `StringBuilder`(编译器不会优化循环内的 `+` 操作) 3. **过度优化**:预设容量(仅在超大数据处理时有意义) ```java // 合理预设容量(减少扩容次数) StringBuilder sb = new StringBuilder(1024); ``` --- **总结**:理解三者的差异是写出高效 Java 代码的基础,根据线程安全和性能需求做出精准选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值