String、StringBuilder和StringBuffer从代码到示例详细讲解
文章目录
前言
String、StringBuilder 和 StringBuffer 是 Java 中用于处理字符串的三种主要类,它们各自有自己的特性和使用场景。下面是对这三个类的详细解释,包括它们的区别以及代码示例。
一、String
String 是 Java 中不可变的字符序列。一旦创建了一个 String 对象,就不能修改它的内容。每次对 String 进行修改操作(如连接、替换等),实际上都会创建一个新的 String 对象。
特点:
- 不可变性:一旦创建,其内容不能更改。
- 字符串池:对于字符串常量,JVM 会维护一个字符串池,以提高性能和减少内存占用。
- 适用于少量字符串操作或不需要修改的场景(字符串拼接操作可能会导致大量对象创建和内存分配,性能较低)。
1. 使用示例
String str1 = "Hello";
String str2 = str1 + " World"; // 创建一个新的String对象
System.out.println(str2); // 输出:Hello World
在第一行中,str1 被初始化为一个字符串对象,内容为 “Hello”。这里的 “Hello” 是一个字符串字面量,Java虚拟机(JVM)会在字符串常量池中查找是否有相同的字符串常量。如果常量池中不存在这个字符串,JVM会在常量池中创建一个新的字符串对象,并将其引用赋给str1。如果常量池中已经存在这个字符串,则直接返回该字符串的引用。
在第二行中,str1 和 " World" 被连接在一起,并赋值给str2。由于String是不可变的,str1 + " World" 这个操作并不是在str1原有的字符串上进行修改,而是创建了一个新的String对象。这个新对象包含了str1和" World"连接后的内容,即"Hello World"。同样地,这个新字符串对象也会在字符串常量池中查找或创建。
值得注意的是,虽然" World"是一个字符串字面量,而str1是一个变量,但在str1 + " World"这个操作中,由于String的不可变性,实际上发生了如下几个步骤:
- 创建一个新的StringBuilder对象(或StringBuffer对象,取决于JVM的实现和编译器的优化)。
- 将str1的内容追加到StringBuilder对象中。
- 将" World"的内容追加到StringBuilder对象中。
- 使用StringBuilder对象的toString()方法创建一个新的String对象。
- 将新创建的String对象的引用赋给str2。
因此,虽然表面上看起来是连接了两个字符串,但实际上涉及到多个对象的创建和复制操作。
2. 为什么不可变?
在源代码层面上主要由以下几点来确保字符串的不可变性:
- String类的内部使用了一个私有的、final的字符数组(char[]
value)来存储字符串的内容。final修饰符确保了这个数组引用不会被修改,也就是说一旦String对象创建完成,这个字符数组就不能被重新指向其他数组。 - 字符串的所有修改操作,比如substring(), concat(), replace(),
等等,都会返回一个新的String对象,而不是修改原来的对象。这些操作在内部通常会创建新的字符数组来存储修改后的字符串。 - String类没有提供任何可以改变其内部状态的方法(即修改字符数组内容的方法)。
由于这些设计上的特点,String对象在创建后其内容就是固定的,不能被修改。每次对String对象进行修改操作,实际上都是创建了一个新的String对象,原来的对象不会被改变,从而确保了字符串的不可变性。
并且这种不可变性能带来了很多好处,比如:
- 安全性:字符串常作为参数传递,不可变性可以避免在传递过程中被意外修改。
- 缓存哈希码:因为字符串是不可变的,它的哈希码(hash code)只需要计算一次并缓存起来,这提高了哈希表等数据结构的性能。
- 字符串池:字符串常量池能够高效存储和复用字符串对象,减少内存占用。
二、StringBuilder
StringBuilder 是一个可变的字符序列,用于构建和修改字符串。它提供了许多修改字符串的方法,如 append、insert、delete、replace 等,这些操作都不会创建新的对象,而是在原对象上进行修改。
特点:
- 可变性:可以修改其内容,而无需创建新对象。
- 适用于大量字符串拼接和修改的场景。
- 非线程安全:在多线程环境下使用时需要小心。
1. 使用示例
StringBuilder sb = new StringBuilder