@文章来源:https://www.programcreek.com/2013/09/the-substring-method-in-jdk-6-and-jdk-7/
JDK6和JDK7中提供的substring(int beginIndex,int endIndex)方法的实现是不同的。这篇文章用来讲解其不同之处。为了简单起见,在这文章中,substring()方法代表了substring(int beginIndex,int endIndex)方法。
1.substring方法的作用
substring(int beginIndex,int endIndex)方法返回一个字符串从beginIndex开始到endIndex-1结束的子字符串
String x=”abcdef”;
x=x.substring(1,3);
System.out.println(x);
运行结果:
bc
2.substring()方法内部执行的细节
你可能会知道,因为变量x为不可变的对象,当x被x.substring(1,3)重新赋值的时候,其会指向堆内存中的一个新的字符串对象,就像如下图一般:
3.substring()方法在JDK6中的实现
字符串对象的实现是一个字符数组。在JDK6中,String类包含了3个成员变量:char[] value,int offset,int count。他们分别被用来存储真实的字符数组,字符串在字符数组中的第一个字符的索引,字符串中字符的数目。
当substring()方法被调用的时候,其会新创建一个String对象并返回,但是其新创建的String对象的字符数组value的指针仍指向原String对象的value数组的对象。两个String对象的不同之处仅在于成员变量count和offset的值。
如下代码仅包含解释该问题的关键部分
//JDK6
String(int offset,int count,char[] value)
{
this.value=value;
this.offset=offset;
this.count=count;
}
public String substring(int beginIndex,int endIndex)
{
return new String(offset+beginIndex,endIndex-beginIndex,value);
}
4.在JDK6中substring()方法存在的问题
如果你有一个非常长的字符串,但你每次使用substring()方法去提取你所需的那很小的一部分字符串,由于你每次仅需要很少的一部分字符串,由于其在堆内存中保存下了一整个数组,为此其会出现性能上的问题(内存泄露)。对于JDK6,解决这个问题的方法是使用如下的方式去提取子串,其会返回一个真正的子串对象:
x=x.substring(beginIndex,endIndex)+””;
/*
*以上代码的实现等价于x=(new StringBuilder()).append(x.substring(1, 3)).append("").toString();
*
*其原因可以参见StringBuilder中对于字符串中append方法的实现,由于在append方法中
*调用了System.arraycopy()方法,为此,其会返回一个真正的字符串
*
*/
5.substring()方法在JDK7中的实现
在JDK7中,substring()方法进行了改进,在JDK7中substring()方法会在堆内存中创建一个真正的(字符)数组
public String(char[] value,int offset,int count)
{
this.value=Arrays.copyOfRange(value,offset,offset+count);
}
public String substring(int beginIndex,int endIndex)
{
int subLen=endIndex-beginIndex;
return new String(value,beginIndex,subLen);
}