字符串的不可变性
一旦一个String对象被创建并被赋值(初始化)这个对象的值就不会变化;
一旦一个String对象在内存中创建,它将是不可改变的,所有的String类中方法并不是改变String对象自己,而是重新创建一个新的String对象。
例如:
public class Reverse {
public static void main(String[] args)
{
String c1=new String("abc");
String c2=new String("abc");
String c3=c1;//将c1对象的引用赋值给c3
System.out.println("c1==c2:"+ (c1==c2)); //false,虽然两个对象的值相同,但是对象在不同的内存空间,是两个不同的对象引用,所以为false;
System.out.println("c1.equals(c2):"+c1.equals(c2));//true,equals方法用于比较值,所以结果为true;
System.out.println("c3==c1:"+(c3==c1));//true
System.out.println("c1.equals(c3):"+c1.equals(c3));//true
c1="han";// 并不是改变了原有对象的值,而是创建了一个新的字符串对象,这个对象的值是han,并把这个对象的引用赋值给了c1。
System.out.println(c1+" "+c3);
System.out.println(""+(c3==c1));//false c1="han" c3="abc"
}
}
JDK6和JDK7中 substring 的原理及区别
substring()的作用就是截取父字符串的某一部分,substring(int beginIndex, int endIndex);
JDK6中,String是通过字符数组实现的,String类包含三个成员变量:char value[], int offset,int count。他们分别用来存储真正的字符数组,数组的第一个位置索引以及字符串中包含的字符个数。
JDK7中,String类去掉了属性offset和count,因为截取后创建新的数组,不需要offset和count两个属性;
JDK6:在原来数组的基础上通过偏移量offset和数量count决定新的字符串(如图和代码块);
JDK7:copy并截取部分数组作为新的字符串;
JDK6实现:
JDK7实现:
//JDK 6
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);
}
//JDK 7
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);
}
总结:
如果原来的字符串被截取后,还是会经常使用,并不需要释放资源,那么比较适合使用JDK6中的方式;
如果原本很长的字符串,需要的只是一小段字符序列,原来的字符串不再被使用,那么比较适合使用JDK7中的方式;
在JDK6中,虽然使用的字符串较短,但是这个非常长的字符数组一直在被引用,所以无法被回收,就可能导致内存泄露。