在 Java 中,
String
是一个非常重要且常用的类,用于表示和操作文本数据。
String
是一个
不可变 的对象,也就是说,一旦创建了一个
String
对象,它的值就无法更改了。
1. String 类的特点
- 不可变性:
String
对象的内容一旦创建,就不能更改。对String
对象进行任何修改操作,实际上是创建了一个新的String
对象。 - 常量池:为了节省内存,Java 对
String
做了优化,所有的String
字面量会存储在一个叫做 字符串常量池(String Table)的地方。如果有相同的字符串文字(例如"hello"
),Java 会复用常量池中的String
对象,而不是每次都创建新的String
对象。 - 线程安全:由于不可变性,
String
是线程安全的,可以在多线程环境中安全使用。
2. 创建 String 对象
java提供了多种不同的方式来创建 String
对象:
String str1 = "Hello"; // 字面量创建,字符串会存储在常量池中
String str2 = new String("Hello"); // 使用 new 关键字创建,会在堆内存中创建新的对象
其余构造函数可以查看JDK23说明文档
- 字面量方式:当使用字面量创建
String
(如"Hello"
)时,Java 会首先检查常量池中是否已经存在"Hello"
字符串。如果存在,就返回常量池中的引用;如果不存在,就将其添加到常量池。 new
关键字方式:当使用new String()
创建字符串时,Java 总是会在堆中创建一个新的String
对象。
3. 常用方法
String
类提供了许多常用方法,用于操作字符串。以下是一些常见的方法:
length()
:返回字符串的长度。
String str = "Hello";
int length = str.length(); // 返回 5
charAt(int index)
:返回指定索引位置的字符。
char c = str.charAt(1); // 返回 'e'
substring(int start)
:返回从指定索引开始到末尾的子字符串。
String sub = str.substring(2); // 返回 "llo"
substring(int start, int end)
:返回从start
到end
索引之间的子字符串。
String sub = str.substring(1, 4); // 返回 "ell"
indexOf(String str)
:返回子字符串在原字符串中第一次出现的位置。
int index = str.indexOf("l"); // 返回 2
equals(String another)
:比较两个字符串的内容是否相同。
boolean isEqual = str.equals("Hello"); // 返回 true
equalsIgnoreCase(String another)
:忽略大小写比较字符串。
boolean isEqualIgnoreCase = str.equalsIgnoreCase("HELLO"); // 返回 true
toLowerCase()
:将字符串转换为小写。
String lower = str.toLowerCase(); // 返回 "hello"
toUpperCase()
:将字符串转换为大写。
String upper = str.toUpperCase(); // 返回 "HELLO"
trim()
:去掉字符串两端的空白字符。
String trimmed = " Hello ".trim(); // 返回 "Hello"
replace(CharSequence target, CharSequence replacement)
:替换字符串中的某个子串。
String replaced = str.replace("e", "a"); // 返回 "Hallo"
split(String regex)
:根据正则表达式分割字符串。
String[] words = str.split("l"); // 返回 ["He", "", "o"]
contains(CharSequence sequence)
:检查字符串是否包含指定的子字符串。
boolean contains = str.contains("ell"); // 返回 true
4. 字符串拼接
java中可以使用+
直接拼接两个字符串
public static void main(String[] args) {
String str1 = "ab";
String str2 = str1 + "c";
System.out.println(str2);
}
输出:
abc
由于 String
是不可变的,str1 + "c"
会创建一个新的字符串,然后将地址赋值给str2.
在执行多个 + 拼接操作时,编译器会使用 StringBuilder 来实现.
5. 字符串常量池与字符串拼接的分析
1. 字面量:
public static void main(String[] args) {
String str1 = "abc";
String str2 = "a"+"b"+"c";
System.out.println(str1==str2);
}
输出:
true
分析:
- 使用==号比较时,基本数据类型比较值,引用数据类型比较地址.
- 字符串在存储时,直接赋值被视为字符串常量存储于
string table
(以前在方法区中,现在移动到堆内存中) - str1初始化后拿到位于
string table
中的字符串abc
的地址 - str2被编译器优化 等价于
String str2 = "abc";
位于string table
中的abc
被复用,str2同样初始化为位于string table
中的abc
的地址 - ==运算符比较两者,地址相同,返回true.
2. 字符串变量运算
String str2 = str1 + "c"; // str1是一个String类型的变量
String str3 = str1 + "c";
System.out.println(str2==str3);
输出:
false
分析:
- str1初始化时,字符串"ab"初始化到
string table
中,str1赋值为string table
中的ab
字符串的地址. - 在使用变量初始化时,使用StringBuilder优化字符串拼接的过程,首先创建一个StringBuilder对象添加字符串后,使用toString()方法创建一个字符串,将字符串的地址赋值给str2,因此str2的初始化等价于以下代码:
StringBuilder sb = new StringBuilder();
String str2 = sb.append(str1).append("c").toString();
- 会创建新的字符串对象,因此str2和str3的地址不同
- ==运算符比较两者,地址不同,返回false.