String
Java String 类概览
String 类是 Java 中最常用的类之一,用于处理字符串。以下是 String 类的主要特性和操作:
特性/操作 | 描述 |
---|---|
不可变性 | String 对象一旦创建就不能被修改 |
创建方式 | 使用双引号 “” 或 String 构造函数 |
字符串池 | Java 维护字符串常量池以优化内存使用 |
比较操作 | equals() 比较内容,compareTo() 比较顺序 |
连接操作 | 使用 + 运算符或 concat() 方法 |
String的常用Method
方法 | 描述 | 示例 |
---|---|---|
length() | 返回字符串的长度 | "Hello".length() 返回 5 |
charAt(int index) | 返回指定索引处的字符 | "Java".charAt(0) 返回 ‘J’ |
substring(int beginIndex, int endIndex) | 返回一个新的字符串,它是此字符串的一个子字符串 | "Hello".substring(0, 3) 返回 “Hel” |
equals(Object anObject) | 将此字符串与指定的对象比较 | "Hello".equals("Hello") 返回 true |
toLowerCase() | 使用默认语言环境的规则将此 String 中的所有字符都转换为小写 | "HELLO".toLowerCase() 返回 “hello” |
toUpperCase() | 使用默认语言环境的规则将此 String 中的所有字符都转换为大写 | "hello".toUpperCase() 返回 “HELLO” |
trim() | 返回字符串的副本,忽略前导空白和尾部空白 | " hello ".trim() 返回 “hello” |
replace(char oldChar, char newChar) | 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的 | "hello".replace('l', 'w') 返回 “hewwo” |
contains(CharSequence s) | 当且仅当此字符串包含指定的 char 值序列时,返回 true | "Hello".contains("He") 返回 true |
split(String regex) | 根据给定正则表达式的匹配拆分此字符串 | "a,b,c".split(",") 返回 [“a”, “b”, “c”] |
下面的流程图展示了 String 对象的生命周期和常见操作:
这个流程图说明了 String 对象从创建到使用的过程,包括字符串池的检查、字符串操作和不可变性原则。理解这个过程有助于更高效地使用 String 类。
Stirng的继承关系
在 Java 中,String
类最终继承自java.lang.Object
类,并且它实现了java.io.Serializable
、java.lang.Comparable<String>
和java.lang.CharSequence
接口。以下是用 Mermaid 语法展示的更详细的关系图:
说明如下:
Object
类:
- 所有 Java 对象的根类,
String
继承了Object
的一些通用方法,如toString()
、equals()
和hashCode()
等。
Serializable
接口:
- 标记接口,表示一个类的对象可以被序列化和反序列化。
String
类的对象可以被写入到输出流并从输入流中读取,从而实现序列化和反序列化操作。
Comparable<String>
接口:
- 定义了一个类型参数为
String
的泛型接口,用于定义对象之间的自然顺序。String
类实现了这个接口,意味着可以比较两个字符串的字典顺序。通过compareTo
方法可以比较两个字符串的大小关系。
CharSequence
接口:
- 表示一个字符序列。
String
类实现了这个接口,提供了一些方法来访问字符串中的字符,如length()
返回字符串的长度,charAt(int index)
返回指定索引处的字符,subSequence(int start, int end)
返回一个子序列。
缺点
由于String
的不可变性,在进行大量字符串操作时可能会创建很多中间对象,这可能会影响性能。为了避免这种情况,可以使用StringBuilder
或StringBuffer
类,它们是可变的字符串类,适用于频繁修改字符串的场景。
字符串的特性
字符串的连接
- 字符串连接:
String c = a + b
进行字符串连接操作。在 Java 中,使用+
运算符连接两个字符串时,实际上会创建一个新的String
对象。- 这个过程中,Java 会先创建一个
StringBuilder
(或StringBuffer
,在单线程环境下通常是StringBuilder
)对象,然后调用append
方法将a
和b
的内容添加到StringBuilder
中,最后调用toString
方法将StringBuilder
转换为String
对象,并将其赋值给c
。
String a = "abc";
String b = "MASU";
String c = a + b;
System.out.println(c); // 输出 "abcMASU"
注意事项
频繁进行字符串连接操作可能会在性能上产生一些开销,特别是在循环中进行大量的字符串连接时。因为每次连接都会创建新的 StringBuilder
和 String
对象。在这种情况下,可以考虑使用 StringBuilder
或 StringBuffer
显式地进行字符串连接操作,以提高性能。
StringBuilder builder = new StringBuilder();
builder.append("abc");
builder.append("MASU");
String c = builder.toString();
StringBuffer
基本介绍
StringBuffer
是 Java 中用于处理可变字符串的类。与String
类不同,String
对象是不可变的.StringBuffer
允许对字符串进行修改,例如添加、插入、删除字符等操作,且不会产生新的对象(除非重新赋值)。这在需要频繁修改字符串内容的场景中非常有用,能够避免创建大量中间字符串对象,从而提高性能。
StringBuffer 的继承结构图
- 常用方法
- 追加(append)方法
append
方法用于在StringBuffer
对象的末尾添加各种类型的数据,如字符、字符串、数字等。例如:
- 追加(append)方法
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.out.println(sb.toString());//输出"Hello World"
- 它有多个重载版本,可以接受
char
、int
、long
、float
、double
、boolean
、char[]
、String
等多种类型的参数。 - 插入(insert)方法
- 用于在
StringBuffer
对象的指定位置插入各种类型的数据。例如:
- 用于在
StringBuffer sb = new StringBuffer("Hello");
sb.insert(2, "inserted");
System.out.println(sb.toString());//输出"Heinsertedllo"
-
它的第一个参数是插入的位置索引(从 0 开始),第二个参数是要插入的内容,同样有多个重载版本,可插入不同类型的数据。
-
删除(delete)方法
-
可以删除
StringBuffer
对象中指定位置的字符。例如:StringBuffer sb = new StringBuffer("Hello"); sb.delete(1, 3); System.out.println(sb.toString());//输出"Hlo"
-
-
其参数
delete(start, end)
表示删除从start
(包含)到end
(不包含)位置的字符。 -
替换(replace)方法
- 用于替换
StringBuffer
对象中指定位置的字符。例如:
- 用于替换
StringBuffer sb = new StringBuffer("Hello");
sb.replace(1, 3, "replaced");
System.out.println(sb.toString());//输出"Hreplacedlo"
- 它的参数
replace(start, end, str)
表示将从start
(包含)到end
(不包含)位置的字符替换为str
。 - 反转(reverse)方法
- 可以将
StringBuffer
对象中的字符序列反转。例如:
- 可以将
StringBuffer sb = new StringBuffer("Hello");
sb.reverse();
System.out.println(sb.toString());//输出"olleH"
- 与 StringBuilder 的比较
StringBuilder
和StringBuffer
功能相似,都用于可变字符串的操作。主要区别在于StringBuffer
是线程安全的,其内部方法大多是synchronized
(同步)的,这使得它在多线程环境下能够正确运行,但性能上会有一定损耗。而StringBuilder
不是线程安全的,在单线程环境下性能更好,通常在不需要考虑线程安全的情况下,推荐使用StringBuilder
。
- 容量管理
StringBuffer
对象有一个容量(capacity)的概念,它表示StringBuffer
内部用于存储字符序列的缓冲区大小。当向StringBuffer
中添加字符,导致字符序列长度超过当前容量时,StringBuffer
会自动扩容。- 扩容的策略一般是:
新容量 = 旧容量 * 2 + 2
。例如,初始容量为 16,当字符序列长度超过 16 时,新容量可能会变为16*2 + 2=34
。可以通过capacity
方法查看当前容量,如System.out.println(sb.capacity());
。
StringBuffer常用方法
方法名 | 介绍 | 示例 |
---|---|---|
append(Object obj) | 将指定对象的字符串表示形式追加到当前StringBuffer 对象的末尾。可以接收多种数据类型,如String 、char 、int 、double 等 | StringBuffer sb = new StringBuffer("abc"); sb.append("def"); // sb的值变为"abcdef" |
insert(int offset, Object obj) | 在当前StringBuffer 对象的指定位置(offset )插入指定对象的字符串表示形式。offset 表示索引位置,从 0 开始计数 | StringBuffer sb = new StringBuffer("abc"); sb.insert(1, "x"); // sb的值变为"axbc" |
delete(int start, int end) | 删除当前StringBuffer 对象中从start (包括)到end (不包括)之间的字符 | StringBuffer sb = new StringBuffer("abcde"); sb.delete(1, 3); // sb的值变为"ade" |
replace(int start, int end, String str) | 将当前StringBuffer 对象中从start (包括)到end (不包括)之间的字符替换为指定的字符串str | StringBuffer sb = new StringBuffer("abcde"); sb.replace(1, 3, "xx"); // sb的值变为"axxde" |
reverse() | 反转当前StringBuffer 对象中的字符序列 | StringBuffer sb = new StringBuffer("abc"); sb.reverse(); // sb的值变为"cba" |
substring(int start) | 返回一个新的String ,它包含当前StringBuffer 对象从start 位置到末尾的字符序列 | StringBuffer sb = new StringBuffer("abcde"); String sub = sb.substring(2); // sub的值为"cde" |
substring(int start, int end) | 返回一个新的String ,它包含当前StringBuffer 对象从start (包括)到end (不包括)之间的字符序列 | StringBuffer sb = new StringBuffer("abcde"); String sub = sb.substring(1, 3); // sub的值为"bc" |
capacity() | 返回当前StringBuffer 对象的容量。容量是指用于存储字符序列的内部缓冲区大小,一般初始容量为 16,当内容超过容量时会自动扩容 | StringBuffer sb = new StringBuffer("abc"); System.out.println(sb.capacity()); // 可能输出16 |
length() | 返回当前StringBuffer 对象中字符序列的长度 | StringBuffer sb = new StringBuffer("abc"); System.out.println(sb.length()); // 输出3 |
setCharAt(int index, char ch) | 将当前StringBuffer 对象中指定索引(index )位置的字符设置为ch | StringBuffer sb = new StringBuffer("abc"); sb.setCharAt(1, 'x'); // sb的值变为"axc" |
StringBuilder
StringBuffer 的继承结构图
StringBuffer和StringBuilder的比较用表格
比较项目 | StringBuffer | StringBuilder |
---|---|---|
可变性 | 可变,允许通过多种方法(如 append、insert、delete 等)对字符串内容进行修改,修改操作在原对象上进行,不会产生新的对象(除非重新赋值) | 可变,提供与 StringBuffer 类似的修改方法,如 append、insert、delete 等,也是在原对象基础上进行修改 |
线程安全性 | 线程安全。其内部大部分方法都使用了synchronized 关键字修饰,这使得在多线程环境下,多个线程访问同一个 StringBuffer 对象时可以正确地同步,保证数据的一致性和正确性,但这也导致了一定的性能损耗 | 非线程安全。在单线程环境下,由于不需要进行同步操作,其性能比 StringBuffer 更好。在多线程环境下如果没有正确的外部同步机制,可能会导致数据不一致的问题 |
性能(单线程) | 由于同步机制的存在,在单线程频繁操作字符串的场景下,性能稍逊一筹 | 在单线程环境下,特别是频繁进行字符串操作(如大量的 append 操作)时,性能优于 StringBuffer,因为它不需要额外的同步开销 |
性能(多线程) | 可以安全地在多线程环境下使用,虽然同步机制会带来性能损失,但保证了数据的准确性 | 在多线程环境下,如果没有外部的同步措施,可能会出现数据错误,不过如果正确地进行外部同步(如使用锁),在性能优化较好的情况下可能会接近或优于 StringBuffer 的性能,但代码复杂度会增加 |
适用场景 | 适用于多线程环境下对字符串进行修改的场景,例如在多个线程可能同时访问和修改同一个字符串对象的服务器端程序、共享资源处理等情况 | 适用于单线程环境下对字符串进行高效操作的场景,如在一个简单的本地工具程序、临时字符串处理等情况下进行大量的字符串构建或修改操作 |
初始容量 | 可以通过构造函数指定初始容量,默认容量为 16。如果预估字符串长度变化较大,可以指定合适的初始容量以减少扩容操作 | 与 StringBuffer 类似,可以通过构造函数指定初始容量,默认容量为 16,也能在一定程度上通过合理设置初始容量优化性能 |
扩容策略 | 当内容长度超过容量时会自动扩容,一般扩容策略是新容量 = 旧容量 * 2 + 2 | 扩容策略与 StringBuffer 相同,当内容长度超过容量时会自动扩容,新容量 = 旧容量 * 2 + 2 |