Java字符串截取案例:从入门到精通的完整指南
目录导读
字符串截取的常见场景
在日常Java开发中,字符串截取几乎是每个程序员都会遇到的技术点,无论你是解析日志文件、提取用户输入的关键信息,还是处理API返回的JSON片段,高效的字符串截取能力都直接关系到代码的健壮性和性能。

典型需求包括:
- 从固定长度字符串中提取子串(如身份证号前6位)
- 按特定分隔符切分(如CSV文件解析、URL参数拆分)
- 基于正则规则提取(如邮箱用户名、电话号码区号)
很多初学者容易混淆substring的索引规则或忽视空指针异常,本文将通过实际案例演示多种截取方法,并回答高频问题。
核心方法对比:substring、split、正则表达式
Java提供了多种字符串截取方式,选择正确的方法能让代码更简洁高效,下表总结了最常用的三种:
| 方法 | 适用场景 | 示例代码 | 注意事项 |
|---|---|---|---|
String.substring(int beginIndex, int endIndex) |
已知固定起始和结束索引 | "abcde".substring(1,3) → "bc" |
索引从0开始,endIndex不包含 |
String.split(String regex) |
按分隔符切分成数组 | "a,b,c".split(",") → ["a","b","c"] |
正则特殊字符需转义(如、) |
Pattern/Matcher 正则表达式 |
复杂模式匹配 | Pattern.compile("\\d{3}").matcher(str) |
适合动态规则,但性能略低 |
选择建议: 能直接用substring尽量不用正则,能用split尽量不用Pattern,遵循简单优先原则。
实战案例:按索引、按分隔符、按模式截取
1 按固定索引截取(substring)
// 案例:提取身份证号中的出生日期部分(前6位地区码后的8位) String id = "110101199003074571"; String birth = id.substring(6, 14); // 返回"19900307" System.out.println(birth); // 常见错误:索引越界 // String error = id.substring(0, 20); // 抛StringIndexOutOfBoundsException
2 按分隔符切分(split)
// 案例:解析HTTP请求路径
String path = "/api/user/123/profile";
String[] parts = path.split("/");
// parts = ["", "api", "user", "123", "profile"]
// 注意:split默认会对空字符串做特殊处理,若路径以"/"开头,第一个元素为空
// 去空处理:
String[] cleaned = path.split("/", -1); // 第二个参数-1保留空字符串
3 正则表达式截取(Pattern)
// 案例:从字符串中提取所有手机号
String text = "联系方式:13800138000 或 15912345678,备用:010-88886666";
Pattern pattern = Pattern.compile("1[3-9]\\d{9}");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(matcher.group()); // 输出13800138000, 15912345678
}
4 综合案例:从URL中提取参数值
String url = "https://example.com/search?q=java&page=1";
Pattern regex = Pattern.compile("[?&]q=([^&]+)");
Matcher m = regex.matcher(url);
if (m.find()) {
String queryValue = m.group(1); // 返回"java"
System.out.println(queryValue);
}
常见错误与避坑指南
1 索引越界(IndexOutOfBoundsException)
- 原因:
substring的endIndex大于字符串长度,或beginIndex为负数。 - 解决: 截取前增加边界检查:
if (str != null && str.length() >= endIndex) { str.substring(begin, end); }
2 空指针异常(NullPointerException)
- 原因: 对
null字符串直接调用substring或split。 - 解决: 使用
Objects.requireNonNull或三元运算符:String safe = (str == null) ? "" : str.substring(0, 5);
3 正则转义遗漏
- 原因:
split(".")实际含义是匹配任意字符,而不是小数点。 - 解决: 对特殊字符使用双反斜杠转义,如
split("\\.")。
4 性能陷阱
- 原因: 在循环中频繁使用
split或正则创建新的Pattern对象。 - 解决: 将
Pattern声明为static成员变量复用:private static final Pattern PHONE_PATTERN = Pattern.compile("1[3-9]\\d{9}");
性能优化建议
- 优先使用
substring: 它是O(1)复杂度(JDK 7+创建新字符串但引用底层数组),比正则快10-100倍。 - 复用
Pattern对象: 避免在循环中Pattern.compile,单次编译复用匹配。 - 使用
StringBuilder替代字符串拼接: 截取后若需组合多个子串,用StringBuilder而非。 - 大数据量时考虑流式处理: 如使用
BufferedReader逐行读取并split,而不是一次性加载整个大文件。
读者问答整理
Q1:substring(0, 4)和substring(0, 4)有什么区别?
A:完全一样,第一个参数是起始索引,第二个是结束索引(不包含),如果只传一个参数,则截取从起始索引到末尾。
Q2:split为什么返回的数组第一个元素往往是空字符串?
A:当字符串以分隔符开头时,split会保留空字符串作为第一个元素。",a,b".split(",") → ["", "a", "b"],若要丢弃空字符串,可使用split(",", -1)来保留。
Q3:正则截取时如何处理特殊字符如和?
A:使用Pattern.quote()自动转义:
String special = "Hello?World";
String[] parts = special.split(Pattern.quote("?")); // 正确
Q4:Java 8和Java 11的字符串截取性能有差异吗?
A:Java 8中substring使用String(char[], offset, count)共享底层数组,但存在内存泄露风险,Java 9+引入了紧凑字符串并优化了实现,建议使用最新JDK。
总结与扩展学习资源
本文从基础方法到高级案例,详细演示了Java字符串截取的多种实现方式,核心要点总结如下:
- 简单截取用
substring,列表解析用split,复杂匹配用正则。 - 始终进行
null和边界检查,避免运行时异常。 - 性能敏感场景注意复用对象和避免不必要的字符串创建。
扩展学习建议:
- Apache Commons Lang的
StringUtils.substringBetween()可简化多字符包围的截取。 - Google Guava的
Splitter类提供流式API处理分隔符。 - 阅读Java官方文档:
java.lang.String和java.util.regex.Pattern。
最后提示:资源分享可访问国内技术社区如CSDN、博客园,或国外Stack Overflow搜索相关案例,学习编程的唯一捷径是动手实践,希望你通过本文的案例快速掌握字符串截取技能。