1.substring() 是干嘛的?
函数:substring(int beginIndex, int endIndex)
返回:截取给定范围的子串,介于原有字符串的起始下标 beginIndex 和结尾下标 endIndex-1 之间。
示例:
String str = "hello world";
String subStr = str.substring(0,5);
System.out.println(subStr)
/* output:
* hello
*/
2.在JDK6和7+的实现有什么区别?
2.1 在JDK6的实现
源码:
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}
解释:调用 substring()
的时候虽然创建了新的字符串,但字符串的值 value
仍然指向的是内存中的同一个数组,如下图所示。
2.2 在JDK7+的实现
源码:
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}
解释:substring()
通过 new String()
返回了一个新的字符串对象,在创建新的对象时通过 Arrays.copyOfRange()
深度拷贝了一个新的字符数组。如下图所示
2.3 对比
从源码上,JDK6 和 JDK7+ 在于 new String()
即 String
构造函数的区别。
JDK6 的实现
- 优点:节省空间,因为共用一个 value 数组。
- 缺点:容易造成内存泄漏。比如原字符串是一个很长很长的字符串,当我们调用
substring()
只截取其中很小一段子串,且后续操作只用到这个子串。但由于该子串还是引用了很长很长的字符串常量,导致这个很长很长的字符串常量无法回收,内存一直被占用着,就有可能引发内存泄露。
总结:JDK6 的实现可谓“成也萧何,败也萧何”
JDK7+的实现
- 优点:避免了 JDK6 的内存泄漏问题
- 缺点:无法像 JDK6 通过引用同一数组实现节省空间的能力
最后,虽然 JDK6 已经基本没人使用了,但通过它可以去了解常见的内存泄漏的情况,也提醒自己避免出现这种情况。
网友评论