说明:本文基于JDK8进行讨论
substring()方法
substring()
方法是 Java 中 String 类的一个常用方法,用于从一个字符串中提取其中的子串。
开发中比如我们需要提取一些指定位置的字符串,比如身份证号码的出生年月日等等。
方法主要有下面两个重载方法
String substring(int beginIndex)
String substring(int beginIndex, int endIndex)
从源码看,第一个方法就是把字符数组的length
替换成第二个方法的endIndex
参数而已
这里尤其需要注意第二个方法的参数beginIndex,endIndex
- beginIndex:起始索引,包括起始索引。
- endIndex:结束索引,不包含。(很重要,经常忽略造成越界异常)
下面我们举个例子对这两个方法进行说明
String str1 = "hello Java";
String sub1 = str1.substring(2); // 提取从索引6开始到末尾的子串 "llo Java"
String sub2 = str1.substring(2, 7); // 提取从索引2到5的子串 "llo J"
String sub3 = str1.substring(0); // 提取整个字符串 "hello Java"
String sub4 = str1.substring(0, str1.length()); // 提取整个字符串 "hello Java"
String sub5 = str1.substring(4, 4); // 空字符串 ""
再分析下:
public String substring(int beginIndex, int endIndex) {
// 检查起始索引是否小于0
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
// 检查结束索引是否大于原始字符串长度
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
// 计算子串的长度
int subLen = endIndex - beginIndex;
// 检查子串长度是否小于0
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
// 如果子串的起始索引为0,且结束索引为原始字符串长度,则返回原始字符串对象
// 否则,通过提取原始字符数组的一部分创建一个新的字符串对象并返回
return ((beginIndex == 0) && (endIndex == value.length)) ?
this : new String(value, beginIndex, subLen);
}
继续看new String(value, beginIndex, subLen)
方法
public String(char value[], int offset, int count) {
// 各种检查
...
...
// 使用Arrays.copyOfRange方法从原始数组中复制一定范围的字符到新数组作为value
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
public static char[] copyOfRange(char[] original, int from, int to) {
// 计算新数组的长度
int newLength = to - from;
// 如果新数组长度小于0,抛出异常
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
// 创建新的字符数组
char[] copy = new char[newLength];
// 使用 System.arraycopy 方法将原始数组中指定范围的元素复制到新数组中
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
从上面的源码可以看出,在这个方法中,底层实际上重新new了一个String,使用 Arrays.copyOfRange()
关于Arrays.copyOfRange()方法的使用,从原始字符数组中复制所需的字符到新的字符数组中,然后用这个新的字符数组创建一个新的字符串对象。
这样做是确保了新的字符串对象拥有自己的字符数组,不再与原始字符串共享。这样做是为了避免潜在的内存泄漏问题,同时也确保了原始字符串如果不再被引用时可以被垃圾回收,因为它的字符数组不再被新的子字符串引用。
所以在JDK 8中,String.substring()
方法通过创建新的 String 对象以及新的字符数组来实现的,可以避免潜在的内存泄漏问题。
注意:
- substring() 方法获取的是字符串的一个子串,但原始字符串本身不会被修改。
- 参数索引必须大于等于0且小于字符串的长度(
0 <= index < length()
)。 - substring() 方法提取的子串包括 beginIndex 索引处的字符,但不包括 endIndex 索引处的字符。
- 若 beginIndex == endIndex,则返回的子串为空字符串。