Java的String.substring() 详细讲解
Java的String.substring() 详细讲解
前言
在 Java 日常开发中,字符串截取是最常用的操作之一。
java.lang.String 类提供的 substring() 方法是实现这一功能的核心。
一、两种常见使用方法
substring() 方法的作用是从当前字符串中提取出一个子字符串,并将其作为一个全新的 String 对象返回。
原字符串本身不会被修改(因为 Java 中的 String 是不可变的)。
它提供了两个重载版本:
1.1 单参数版本
substring(int beginIndex)
- 功能:从指定的
beginIndex位置开始,一直截取到字符串的末尾。 - 示例:
1 | String str = "HelloWorld"; |
1.2 双参数版本
substring(int beginIndex, int endIndex)
- 功能:截取从
beginIndex开始,到endIndex结束的子字符串。 - 核心记忆点:左闭右开区间
[beginIndex, endIndex)。即包含beginIndex位置的字符,但不包含endIndex位置的字符。 - 示例:
1 | String str = "HelloWorld"; |
技巧:截取出来的子字符串的长度,刚好等于
endIndex - beginIndex。
二、3 个规则
为了避免在生产环境中抛出异常,使用该方法时需要特别注意以下规则:
- 索引从 0 开始:Java 字符串的第一个字符索引是
0,最后一个字符索引是length() - 1。 - 允许等于
length():- 在双参数版本中,
endIndex最大可以等于str.length()。这表示截取到原字符串的末尾。 - 如果
beginIndex和endIndex相等,将返回一个空字符串""。
- 在双参数版本中,
- 越界会抛异常:如果违反规则,会触发运行时异常
StringIndexOutOfBoundsException。beginIndex < 0endIndex < 0beginIndex > endIndexbeginIndex或endIndex > str.length()
三、 底层原理
substring() 的底层实现在 JDK 6 和 JDK 7+ 之间发生过一次重大变更:
JDK 6 及以前(内存泄漏风险):
调用
substring()产生的新字符串,底层依然共享着原字符串的char[]数组,只是改变了offset和count属性。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class String {
char[] value;
int offset;
int count;
}
// substring 本质:
new String(value, offset, count)
// 内存发生场景
String big = new String(new char[1000000]); // 100万字符
String small = big.substring(0, 10);
// 你以为:small 只占 10 个字符
// 但实际:small 引用整个 100万 char[] small → char[1000000]
// 即使:big = null; small 还活着, small 指向原始 char[] GC 认为 char[] 还在用
// 整个 100万字符数组无法释放
// substring 只是“视图切片”,不是“数据复制”- 风险:
- 如果你有一个极其巨大的字符串,只是用
substring截取了一小段并长期持有引用, - 会导致那个巨大的
char[]无法被垃圾回收(GC),从而引发内存泄漏。
- 如果你有一个极其巨大的字符串,只是用
- 风险:
JDK 7 及以后(现行机制):
为了解决上述问题,现在的
substring()会在堆内存中创建一个全新的char[]数组(或byte[]数组,JDK 9 之后 String 底层改为了byte[]),并将需要截取的内容复制过去。原字符串如果不再被引用,就可以被正常 GC 回收。1
2
3new String(Arrays.copyOfRange(value, begin, end));
// 结果 small → char[10],big → char[1000000]
// 当big = null; GC 可以:回收 100万 char[], small 不受影响
四、 实战踩坑提示
在实际业务中,我们经常需要根据某个特定字符来截取。
为了安全起见,通常需要搭配 indexOf() 方法使用,并做好边界检查。
这里使用获取文件路径中的文件名来举例:文件路径usr/local/bin/note.md获取的文件名:note.md
1 | public String getFileName(String filePath) { |
总结
subString使用:
substring()用于截取字符串,返回的是新对象,原字符串不变(String 不可变)。核心规则:左闭右开
[begin, end),长度 =end - begin。使用时要注意索引边界,否则会抛
StringIndexOutOfBoundsException。
底层关键点
JDK 6 及以前:共享原
char[]→ 只是“切片视图”,可能导致内存泄漏。JDK 7+:复制新数组 → 真正“独立字符串”,避免内存占用问题。
实战建议
涉及截取位置时,优先配合
indexOf / lastIndexOf使用一定要做边界判断(null / 空字符串 / 越界)
牢记:现在的 substring 是安全的,但旧版本存在坑