目录
一、String类
1. 概述
2. 创建方式
(1)直接赋值
(2)使用new关键字
3. 性能优化
4. 常用方法
(1)==
(2)equals()
(3)concat()
二、StringBuilder类
1. 概述
2. 核心特性
3. 常用方法
(1)append
(2)insert
(3)delete
(4)reverse
(5)toString
4. 性能优化技巧
(1)预设容量(Pre-sizing)
(2)链式调用优化(Method Chaining)
(3)避免循环内重复创建
(4)批量操作替代单个操作
三、StringBuffer类
1. 概述
2. 核心特性
3. 适用场景
四、StringJoiner类
1. 概述
2. 核心特性
3. 基本构造方式
4. 常用方法
(1)add
(2)merge
(3)toString
五、字符串拼接的底层原理
1. 使用+操作符拼接
(1)没有变量(纯字面量拼接)
(2)有变量(涉及变量拼接)
2. 使用String.concat()方法
3. 使用StringBuilder或StringBuffer
Java中的字符串处理类包括String
、StringBuilder
、StringBuffer
和StringJoiner
等,它们各自有不同的特性和底层实现原理。
一、String
类
1. 概述
String
类位于java.lang
包中,是Java中最基础的字符串类,其底层是一个被final
修饰的字符数组char[] value
( JDK9及之后,为了节省内存空间,改为使用byte[]
数组存储字符 ),这意味着一旦创建,其内容不可更改。这种不可变性使得String
在多线程环境下是安全的,但同时也带来了性能开销,因为每次修改都需要创建新的对象。
2. 创建方式
(1)直接赋值
比如 String s = "abc";
。当使用双引号直接赋值时,系统会检查该字符串在串池中是否存在,如果不存在就创建新的字符串,如果存在就复用。
(2)使用new
关键字
比如 String s = new String("abc");
。这种方式会创建新的字符串对象,每new一次就会在堆内存里开辟一块新的空间并创建新的对象,占用额外的内存。
常用构造方法
-
String()
:创建一个空字符串。 -
String(String original)
:根据传入的字符串创建一个新的字符串对象。 -
String(char[] value)
:根据字符数组创建一个新的字符串对象。
public static void main(String[] args) {char[] chars = {'a', 'b', 'c', 'd'};String s = new String(chars);System.out.println(s); // abcd}
-
String(byte[] bytes, Charset charset)
:根据字节数组和指定的字符集创建一个新的字符串对象。
public static void main(String[] args) {byte[] bytes = {97, 98, 99, 100};String s = new String(bytes);System.out.println(s); // abcd}
3. 性能优化
-
使用字符串常量池避免重复创建相同字符串。
-
避免频繁修改字符串,因为每次修改都会导致新对象的创建。
4. 常用方法
(1)==
如果是基本数据类型, ==
比较的是数据值;如果是引用数据类型, ==
比较的是地址值。字符串是引用数据类型,所以比较的是地址值。
public static void main(String[] args) {String s1 = new String("abc"); // s1记录的是堆里面的地址值String s2 = "abc"; // s2记录的是串池中的地址值System.out.println(s1 == s2); // false}
(2)equals()
equals()
比较的是字符串内容。如果要忽略大小写的比较,可以使用 equalsIgnoreCase()
。
public static void main(String[] args) {String s1 = new String("abc");String s2 = "abc";System.out.println(s1.equals(s2)); // true}
(3)concat()
concat()
方法会创建一个新的字符串对象,效率较低。
二、StringBuilder
类
1. 概述
StringBuilder
是可变字符串类(可以看作一个容器),其底层同样是一个字符数组(char[] value
),但与String
不同的是,它允许动态修改内容,因此比String
更高效。
2. 核心特性
-
可变性(Mutable) :内容可被修改而不产生新对象。
-
非线程安全(Not Thread-Safe) :比线程安全的StringBuffer性能更高。
-
动态扩容(Dynamic Expansion) :自动增加内部缓冲区容量。
-
高效操作(Efficient Operations) :避免频繁内存分配。
3. 常用方法
(1)append
StringBuilder sb = new StringBuilder("abc");// 基本类型追加(自动转换为字符串)sb.append(def)sb.append(123)sb.append(2.5)sb.append(true)// 对象追加(调用toString())sb.append(Object obj)// 子串追加(支持偏移量)sb.append(char[] str, int offset, int len)sb.append(CharSequence s, int start, int end)
(2)insert
sb.insert(int offset, boolean b)sb.insert(int offset, char c)sb.insert(int offset, int i)sb.insert(int offset, Object obj)
(3)delete
sb.delete(int start, int end) // 删除[start,end)区间内容 sb.deleteCharAt(int index) // 删除指定位置字符
(4)reverse
sb.reverse() // 原地反转字符序列
(5)toString
String str = sb.toString(); // 转换为不可变字符串
4. 性能优化技巧
(1)预设容量(Pre-sizing)
// 预估最终长度设置初始容量 StringBuilder sb=new StringBuilder(1024);
(2)链式调用优化(Method Chaining)
sb.append("a").append("b").append("c").reverse().toString();
(3)避免循环内重复创建
// 错误示范(每次循环都新建)for(...){ sb=new StringBuilder(); }// 正确做法(复用同一个)StringBuilder sb=new StringBuilder();for(...){ sb.setLength(0); }
(4)批量操作替代单个操作
// 低效方式 for(char c:chars){ sb.append(c); }// 高效方式 sb.append(chars);
三、StringBuffer
类
1. 概述
StringBuffer
与 StringBuilder
非常相似,但是其特点是线程安全。因为其方法被 synchronized
修饰,这使得它适用于多线程环境,但因此带来了性能开销。
2. 核心特性
-
线程安全(Thread-Safe) :所有公开方法都使用
synchronized
。 -
可变性(Mutable) :内容可被修改而不产生新对象。
-
动态扩容(Dynamic Expansion) :自动增加内部缓冲区容量。
-
兼容性(Compatibility) :从Java1.0开始就存在。
3. 适用场景
适用于多线程环境下需要修改字符串的场景。在多线程环境下使用时,可以避免锁竞争问题,但在单线程环境下不如StringBuilder
高效。
四、StringJoiner
类
1. 概述
StringJoiner
是Java8引入的一个专门用于构造由分隔符分隔的字符序列的工具类,它简化了集合元素连接、CSV格式生成等常见场景下的字符串拼接操作。
2. 核心特性
-
分隔符控制(Delimiter) :可指定固定分隔符连接元素。
-
前后缀支持(Prefix/Suffix) :可为结果添加统一的前后缀。
-
空值处理(Null Handling) :提供灵活的空元素处理策略。
-
链式调用(Fluent API) :支持方法链式调用风格。
3. 基本构造方式
// 仅指定分隔符(无前后缀)new StringJoiner(",")// 完整构造(带前后缀)new StringJoiner(",", "[", "]")
4. 常用方法
(1)add
StringJoiner sj = new StringJoiner(",");// 添加单个元素(支持任意CharSequence)sj.add("A").add("B").add("C");// 添加多个元素的快捷方式(Java8+)List<String> list = Arrays.asList("X","Y","Z");list.forEach(joiner::add);
(2)merge
// 合并另一个StringJoiner的内容(忽略其前后缀)StringJoiner sj1 = new StringJoiner(",", "[", "]");StringJoiner sj2 = new StringJoiner(",", "[", "]");sj1.add("A").add("B");sj2.add("X").add("Y");sj1.merge(sj2);System.out.println(sj1); // 输出:[A,B,X,Y]
(3)toString
String str = sj1.toString();System.out.println(str); // 输出:[A,B,X,Y]
五、字符串拼接的底层原理
1. 使用+操作符拼接
(1)没有变量(纯字面量拼接)
当拼接的内容全部是字符串字面量时,Java编译器会在编译阶段直接将其优化为一个常量字符串,优化后的字符串会被存储在字符串常量池中,避免运行时创建额外的对象。
String result = "Hello" + "World";// 上述代码会被编译器优化为:String result = "HelloWorld";
性能特点:
-
高效:由于编译期优化,运行时无需创建StringBuilder对象,性能最佳。
-
内存节省:字符串直接存储在常量池中,减少内存开销。
(2)有变量(涉及变量拼接)
当拼接的内容包括变量时:JDK8以前,系统底层会创建一个StringBuilder对象,依次调用append()方法将每个字符串片段添加到StringBuilder对象中,然后调用toString()方法将StringBuilder对象转换为String对象,而toString()方法的底层是直接new了一个字符串对象。JDK8及以后,系统会预估字符串拼接之后的总大小,把要拼接的内容都放在数组里,本质上也是产生了一个新的字符串。
String str1 = "Hello";String str2 = "World";String result = str1 + str2;// 上述代码会被编译器转换为:String str1 = "Hello";String str2 = "World";String result = new StringBuilder().append(str1).append(str2).toString();
性能特点:
-
运行时开销:每次拼接都会创建StringBuilder对象,增加运行时开销。
-
内存开销:生成的字符串存储在堆内存中,而不是常量池中。
2. 使用String.concat()方法
String.concat()方法用于将指定字符串连接到此字符串的末尾。它实际上是通过创建一个新的字符数组,将两个字符串的内容复制到新数组中,然后构造一个新的String对象。
性能特点:
- concat()方法每次调用都会创建一个新的String对象,因此在频繁拼接字符串时性能较差,适用于少量字符串的拼接。
3. 使用StringBuilder或StringBuffer
StringBuilder和StringBuffer都提供了可变的字符序列,允许对字符串内容进行修改而不需要创建新的对象。执行流程:创建一个StringBuilder或StringBuffer对象,调用append()方法将每个字符串片段添加到对象中,调用toString()方法将对象转换为String对象。
性能特点:
-
StringBuilder是非线程安全的,但性能较高。StringBuffer是线程安全的,但性能稍低(因为添加了同步机制)。
-
在需要频繁拼接字符串的场景中,使用StringBuilder或StringBuffer可以显著提高性能。
-
StringBuilder适用于单线程环境,而StringBuffer适用于多线程环境。