String 常用 API 与字符串综合处理 | JavaSE
String 常用 API 与字符串综合处理
一、学习目标
完成本章后,你应该能够:
- 熟练使用
length()、charAt()、toCharArray()获取和遍历字符串数据。 - 熟练使用
equals()、equalsIgnoreCase()完成字符串内容比较。 - 熟练使用
substring()完成字符串截取,并准确理解“包前不包后”的索引规则。 - 使用
replace()、contains()、startsWith()、split()完成常见字符串处理。 - 能够根据业务需求组合多个 String API,解决手机号脱敏、登录校验、文本分割、字符统计等问题。
- 能够 Debug 常见的字符串索引越界、
==比较错误、split()正则表达式误用等问题。
二、核心知识
2.1 String API 到底是什么
String 不只是一个用来保存文本的类型。
Java 为 String 提供了大量实例方法,用于完成:
- 获取字符串长度
- 根据索引获取字符
- 字符串比较
- 字符串截取
- 字符串替换
- 内容查找
- 前缀判断
- 字符串分割
- 字符数组转换
例如:
String text = "JavaSE";
int length = text.length();
char first = text.charAt(0);
可以把一个 String 对象理解为:
字符串数据
+
一整套字符串处理能力
2.2 String 不可变带来的重要规则
上一章已经学习:
String 是不可变字符串。
因此,本章学习 String API 时必须建立一个非常重要的认知:
很多 String 方法不会修改原字符串,而是返回新的 String。
例如:
String text = "Java";
text.replace("Java", "Python");
System.out.println(text);
结果仍然是:
Java
因为:
text.replace("Java", "Python");
只是产生了一个新的字符串结果。
如果需要保存结果,应写成:
text = text.replace("Java", "Python");
或者:
String newText = text.replace("Java", "Python");
所以看到 String API 时,经常应该思考:
这个方法是在读取字符串,还是在根据原字符串生成新的结果?
2.3 String 常用 API 总览
本阶段重点掌握:
| API | 作用 |
| --------------------------------- | ---------------------- |
| length() | 获取字符串长度 |
| charAt(index) | 获取指定索引位置字符 |
| toCharArray() | 转换成字符数组 |
| equals() | 比较字符串内容 |
| equalsIgnoreCase() | 忽略大小写比较内容 |
| substring(beginIndex) | 从指定索引截取到末尾 |
| substring(beginIndex, endIndex) | 截取指定区间 |
| replace(target, replacement) | 替换字符串内容 |
| contains(s) | 判断是否包含指定内容 |
| startsWith(prefix) | 判断是否以指定内容开头 |
| split(regex) | 按规则分割字符串 |
这些 API 不应该单独死记。
更重要的是建立:
需求
↓
选择 String API
↓
组合 API
↓
得到业务结果
三、使用方法
3.1 length():获取字符串长度
基本格式:
字符串.length()
例如:
String name = "JavaSE";
System.out.println(name.length());
输出:
6
一个非常常见的遍历方式就是:
String text = "Java";
for (int i = 0; i < text.length(); i++) {
System.out.println(text.charAt(i));
}
3.2 charAt():根据索引获取字符
格式:
字符串.charAt(index)
例如:
String text = "Java";
System.out.println(text.charAt(0));
System.out.println(text.charAt(1));
System.out.println(text.charAt(2));
System.out.println(text.charAt(3));
得到:
J
a
v
a
索引仍然遵循 Java 中最重要的规则:
J a v a
0 1 2 3
因此:
text.charAt(4);
会产生索引越界问题。
合法索引范围:
0 ~ length() - 1
所以遍历字符串通常写:
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
}
注意不是:
i <= text.length()
3.3 toCharArray():字符串转字符数组
如果需要逐个处理字符串中的字符,可以:
String text = "Java";
char[] chars = text.toCharArray();
for (char c : chars) {
System.out.println(c);
}
效果:
J
a
v
a
于是遍历字符串主要有两种方式。
方式一:
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
}
方式二:
char[] chars = text.toCharArray();
for (char c : chars) {
System.out.println(c);
}
如何选择?
如果需要索引:
charAt()
通常更方便。
如果主要针对每个字符进行处理:
toCharArray()
也非常自然。
3.4 equals():比较字符串内容
前面已经学习:
==
比较引用类型时,关注的通常是:
两个变量保存的引用是否相同。
但实际业务中,我们大多数时候需要比较:
两个字符串的内容是不是一样。
应该使用:
equals()
例如:
String correctUsername = "admin";
String inputUsername = "admin";
if (correctUsername.equals(inputUsername)) {
System.out.println("用户名正确");
}
推荐:
correctUsername.equals(inputUsername)
而不是:
inputUsername.equals(correctUsername)
如果 inputUsername 可能是 null,后者可能产生:
NullPointerException
而已知常量通常不会是 null。
例如:
"admin".equals(inputUsername)
也是常见写法。
3.5 equalsIgnoreCase():忽略大小写比较
例如:
String command = "LOGIN";
System.out.println(command.equalsIgnoreCase("login"));
结果:
true
这适用于某些不区分英文大小写的场景,例如:
YES / yes / Yes
EXIT / exit / Exit
例如:
if ("exit".equalsIgnoreCase(command)) {
System.out.println("程序退出");
}
需要注意:
是否应该忽略大小写取决于业务规则。
例如密码一般不应该随意忽略大小写。
3.6 substring():字符串截取
这是 String 中非常重要的方法。
从某个位置一直截取到结尾
String text = "JavaSE";
String result = text.substring(4);
System.out.println(result);
结果:
SE
可以理解为:
J a v a S E
0 1 2 3 4 5
↑
开始
3.7 substring(beginIndex, endIndex)
格式:
substring(开始索引, 结束索引)
规则:
包含开始索引,不包含结束索引。
即:
[beginIndex, endIndex)
这就是 Java 中经常提到的:
包前不包后。
例如:
String text = "JavaSE";
String result = text.substring(0, 4);
System.out.println(result);
得到:
Java
因为:
索引:
0 1 2 3 4 5
J a v a S E
substring(0, 4)
取:
0、1、2、3
不取:
4
因此截取结果长度可以直接计算:
endIndex - beginIndex
3.8 手机号脱敏案例
假设:
String phone = "18612346520";
希望得到:
186****6520
分析:
186 1234 6520
↑ ↑
保留 保留
前三位:
phone.substring(0, 3)
后四位:
phone.substring(7)
组合:
String phone = "18612346520";
String result =
phone.substring(0, 3)
+ "****"
+ phone.substring(7);
System.out.println(result);
结果:
186****6520
这个案例非常典型,因为它体现了:
需求分析
→ 确定索引
→ substring
→ 字符串拼接
而不是单纯背 API。
3.9 replace():字符串替换
例如:
String text = "Java is good, Java is powerful";
String result = text.replace("Java", "Python");
System.out.println(result);
会得到一个新的字符串结果。
注意原字符串:
text
本身不会被修改。
因此:
String text = "Java";
text.replace("Java", "Python");
System.out.println(text);
仍然输出:
Java
正确保存结果:
text = text.replace("Java", "Python");
3.10 contains():判断是否包含指定内容
例如:
String title = "JavaSE字符串完整教程";
boolean result = title.contains("JavaSE");
System.out.println(result);
输出:
true
实际开发中常见于:
- 简单关键词判断
- 文本过滤
- 文件名检测
- 搜索匹配
- 内容校验
例如:
String fileName = "JavaSE学习笔记.md";
if (fileName.contains("Java")) {
System.out.println("找到 Java 相关文档");
}
3.11 startsWith():判断字符串开头
例如:
String url = "https://yulanlin.cn";
System.out.println(url.startsWith("https://"));
输出:
true
常见应用:
判断 URL 协议
判断文件名前缀
判断命令格式
判断编号格式
例如:
String studentId = "20260001";
if (studentId.startsWith("2026")) {
System.out.println("2026级学生");
}
3.12 split():字符串分割
例如:
String data = "Java,Python,C++";
String[] languages = data.split(",");
for (String language : languages) {
System.out.println(language);
}
结果:
Java
Python
C++
也就是说:
"Java,Python,C++"
↓ split(",")
["Java", "Python", "C++"]
split() 的返回值是:
String[]
不是一个 String。
3.13 split() 有一个非常重要的规则
split() 的参数不是普通字符串匹配规则,而是:
正则表达式(Regular Expression)。
例如:
String text = "www.yulanlin.cn";
如果希望按照 . 分割,不能想当然地写:
text.split(".");
因为正则表达式中的:
.
具有特殊含义。
通常应该转义:
String[] parts = text.split("\\.");
这里出现两层规则:
正则中的点:
\.
Java字符串中反斜杠还需要转义:
\\.
最终:
text.split("\\.")
现在暂时只需要记住:
split()接收正则表达式,不要把所有符号都当普通文本。
完整正则表达式知识可以在后续需要时专门学习。
3.14 String API 综合示例
import java.util.Arrays;
public class StringApiDemo {
public static void main(String[] args) {
String text = "JavaSE-String-API";
System.out.println(text.length());
System.out.println(text.charAt(0));
System.out.println(
Arrays.toString(text.toCharArray())
);
System.out.println(
text.substring(0, 6)
);
System.out.println(
text.substring(7)
);
System.out.println(
text.replace("String", "Text")
);
System.out.println(
text.contains("SE")
);
System.out.println(
text.startsWith("Java")
);
String data = "Java,Python,C++";
String[] languages = data.split(",");
System.out.println(
Arrays.toString(languages)
);
}
}
这里真正需要掌握的不是复制代码,而是能够看到需求后主动想到:
长度 → length
指定字符 → charAt
字符遍历 → charAt / toCharArray
内容比较 → equals
截取 → substring
替换 → replace
包含 → contains
前缀 → startsWith
分割 → split
四、原理与进阶
4.1 length() 不一定等于“肉眼看到的字符数量”
对于普通英文、数字以及大量常见中文字符:
"Java".length()
和我们肉眼看到的字符个数通常一致。
但 Java 的 String 底层涉及 Unicode。
length() 返回的是字符串中的:
UTF-16 code unit 数量。
某些 Unicode 字符可能需要两个 char 表示。
例如部分 Emoji 就可能出现:
肉眼看到:1 个符号
String.length():2
因此:
length()
更准确的理解应该是:
String 内部 UTF-16 字符单元的数量。
对于当前 JavaSE 入门阶段,大多数普通字符串处理仍然可以按照索引和 char 理解。
但进入国际化文本处理时,需要进一步理解:
char
Unicode Code Point
UTF-16 Code Unit
之间的区别。
4.2 为什么很多 String 方法都返回 String
例如:
substring()
replace()
都会返回:
String
原因与 String 的不可变性直接相关。
假设:
String original = "Java";
String result = original.replace("Java", "Python");
逻辑关系是:
original
│
└── "Java"
执行 replace
original
│
└── "Java"
result
│
└── "Python"
不是:
"Java"
↓
原地修改
↓
"Python"
这也是后续学习:
StringBuilder
的直接原因。
4.3 String API 的本质:声明式数据处理
例如手机号脱敏。
可以自己逐个字符复制。
但使用:
substring()
更清晰。
因为 API 本身已经表达:
我要截取字符串
这就是学习 API 的价值之一:
用更高层、更接近业务意图的方式表达程序。
五、实践应用
5.1 登录校验
系统保存:
String correctUsername = "itheima";
String correctPassword = "123456";
用户输入:
String username;
String password;
核心判断:
if (correctUsername.equals(username)
&& correctPassword.equals(password)) {
System.out.println("登录成功");
}
这里必须使用:
equals()
而不是依赖:
==
5.2 文本关键词检测
例如博客标题:
String title = "JavaSE String 完整教程";
判断是否为 Java 内容:
if (title.contains("Java")) {
System.out.println("Java相关文章");
}
5.3 数据解析
假设后端收到:
1001,星雨笔录,JavaSE
可以:
String data = "1001,星雨笔录,JavaSE";
String[] parts = data.split(",");
String id = parts[0];
String name = parts[1];
String category = parts[2];
这种思维以后会大量出现在:
文件读取
网络数据
CSV
日志
简单协议解析
等场景中。
真正复杂的数据交换则会进一步使用:
JSON
XML
对象序列化
HTTP
等技术。
六、常见问题
6.1 String 方法调用后为什么原字符串没有变化?
错误:
String s = "Java";
s.replace("Java", "Python");
System.out.println(s);
String 不可变。
应该接收返回结果:
s = s.replace("Java", "Python");
6.2 为什么 substring 经常越界?
例如:
String text = "Java";
text.substring(0, 5);
字符串长度:
4
最大可作为结束边界的是:
4
因此:
substring(0, 4)
合法。
核心规则:
0 <= beginIndex <= endIndex <= length()
6.3 substring 为什么是“包前不包后”?
例如:
substring(2, 5)
取:
2
3
4
不取:
5
结果长度:
5 - 2 = 3
这种半开区间:
[start, end)
在 Java API 和算法中非常常见。
6.4 为什么不能使用 == 判断用户输入的字符串?
因为:
==
针对引用类型主要比较引用关系。
业务中要比较字符串内容,应使用:
equals()
6.5 split(".") 为什么结果不符合预期?
因为:
split()
参数是正则表达式。
. 在正则表达式中具有特殊意义。
分割普通点号通常使用:
split("\\.")
6.6 charAt(length()) 为什么越界?
假设:
String s = "Java";
长度:
4
但合法索引:
0
1
2
3
因此:
s.charAt(s.length())
相当于:
s.charAt(4)
越界。
正确遍历:
for (int i = 0; i < s.length(); i++) {
}
6.7 replace 和 replaceAll 是不是一样?
不是。
当前章节重点掌握:
replace()
例如:
text.replace("Java", "Python");
它按照指定的字面字符序列进行替换。
而:
replaceAll()
的第一个参数使用的是:
正则表达式
因此不能因为名字相似就认为它们完全等价。
七、练习与验收
7.1 知识问答
- String 常用 API 主要解决哪些字符串处理问题?
length()的返回值表示什么?charAt(index)的合法索引范围是什么?charAt()与toCharArray()在字符串遍历中有什么区别?==与equals()比较 String 时有什么区别?equalsIgnoreCase()适用于什么场景?substring(beginIndex, endIndex)为什么叫“包前不包后”?substring(beginIndex, endIndex)得到的字符串长度如何计算?- 为什么调用
replace()后原 String 不会发生改变? contains()与startsWith()分别判断什么?split()返回什么类型?- 为什么
split(".")可能无法实现“按照点号分割”? - String 不可变与大量字符串修改之间有什么关系?
- 为什么
length()在处理某些 Emoji 时可能和肉眼看到的字符数量不同?
7.2 代码阅读
阅读代码,不运行程序,写出结果并说明原因:
public class Test {
public static void main(String[] args) {
String text = "JavaSE";
System.out.println(text.length());
System.out.println(text.charAt(4));
System.out.println(text.substring(0, 4));
System.out.println(text.substring(4));
System.out.println(text.contains("SE"));
System.out.println(text.startsWith("Java"));
}
}
继续阅读:
public class Test {
public static void main(String[] args) {
String text = "Java";
text.replace("Java", "Python");
System.out.println(text);
String result = text.replace("Java", "Python");
System.out.println(result);
}
}
回答:
- 两次输出分别是什么?
- 为什么第一次调用
replace()没有改变text? result与text是否表示相同内容?
7.3 手写代码
任务一:手机号脱敏
给定:
18612346520
输出:
186****6520
要求:
- 使用
substring() - 不允许直接写最终结果
任务二:字符统计
输入任意字符串,统计其中:
- 英文字母数量
- 数字数量
- 空格数量
- 其他字符数量
要求:
- 使用
length()+charAt()
或者:
- 使用
toCharArray()
任务三:用户名校验
定义:
正确用户名:admin
接收用户输入。
要求:
- 使用字符串内容比较
- 正确时输出登录成功
- 错误时输出用户名错误
任务四:字符串分割
给定:
Java,Python,C++,JavaScript
将其分割后逐个输出语言名称。
7.4 Debug
下面代码存在问题:
public class DebugDemo {
public static void main(String[] args) {
String username = new String("admin");
if (username == "admin") {
System.out.println("登录成功");
}
for (int i = 0; i <= username.length(); i++) {
System.out.println(username.charAt(i));
}
}
}
要求:
- 找出全部问题。
- 说明每个问题属于什么类型。
- 修改代码。
- 解释为什么修改后的代码正确。
继续 Debug:
String url = "www.yulanlin.cn";
String[] parts = url.split(".");
要求:
- 判断代码的设计意图。
- 分析为什么
split(".")存在问题。 - 修改代码,使其按照普通点号完成分割。
7.5 综合训练
设计一个简单的用户注册信息处理程序。
用户输入:
用户名
手机号
邮箱
程序完成:
- 判断用户名是否为空。
- 输出用户名长度。
- 手机号进行脱敏。
- 判断邮箱中是否包含
@。 - 判断邮箱是否以指定域名结尾。
- 输出处理后的用户信息。
要求:
- 先写需求分析。
- 再确定分别需要哪些 String API。
- 最后完成程序。
7.6 本章验收
完成本章后,应能够闭卷回答:
length
charAt
toCharArray
equals
equalsIgnoreCase
substring
replace
contains
startsWith
split
分别解决什么问题。
同时能够:
- 手写手机号脱敏。
- 手写字符串遍历。
- 正确比较用户输入字符串。
- 根据分隔符拆分字符串。
- Debug 字符串索引越界。
- 解释为什么 String API 经常需要接收返回值。
- 解释
split()与普通字符串分割思维之间的正则表达式边界。