Java中关于String类型的几个问题
如何比较两个字符串?用"="还是equals()?
简单来说,“==”是用来检测俩引用是不是指向内存中的同一个对象,而equals()方法则检测的是两个对象的值是否相等。只要你想检测俩字符串是不是相等的,你就必须得用equals()方法。
如果你知道“字符串保留(string intern)”的概念那就更好了:
String的intern()方法返回字符串对象的规范化表示形式。它遵循以下规则:对于任意两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()==t.intern()才为true。为什么呢?
一个初始时为空的字符串池,它由类String私有地维护。当调用intern()方法时,如果池已经包含一个等于此String对象的字符串(该对象由equals()方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并且返回此String对象的引用。
所有字面值字符串和字符串赋值常量表达式都是内部的。
String str1 = "abc";
String str2 = new String("abc"); //true
System.out.println(str1 == str2); //false
System.out.println(str1.equals(str2)); //true
System.out.println(str1 == str2.intern()); //truesubstring()方法具体是都干了些啥?
在JDK6中,这个方法只会在标识现有字符串的字符数组上 给一个窗口来表示结果字符串,但是不会创建一个新的字符串对象。如果需要创建个新字符串对象,可以这样在结果后面"+"一个空的字符串:
str.substring(m, n) + "";
这么写的话就会创建一个新的字符数组来表示结果字符串。同时,这么写也有一定的几率让你的代码跑的更快,因为垃圾回收器会吧没有在使用的大字符串回收而留下子字符串。
Oracle JDK7中的substring()方法会创建一个新的字符数组,而不用之前存在的。下面举出详细的列子说明substring()在JDK6和JDK7中的区别。
String str = "abcdef"; str = str.substring(1,3); System.out.println(str);
当substring()被调用时,发生了什么?
你肯定知道,字符串str是final不可变的,所以当str被重新赋值为str.substring(1,3)时,它应该指向一个全新的字符串,就像下面图片显示的那样:

但是这张图并不完全正确,它并没有说明内存堆中真正发生了什么。那么substring()方法的到底是怎样工作的呢?在JDK6和JDK7中又有什么区别呢?
JDK6中的substring()
实际上String类的原理是将字符串存储在一个字符数组中的,在JDK6中,类String包括3个属性域:char value[], int offset, int count,分别用来存储实际的字符数组、数组的第一个索引、字符的个数。
当substring()方法被调用时,一个新的字符串被创建,但是这个字符串的字符数组value仍然指向了原来那个数组的内存堆!惟一不同的是两个字符串的offset和count属性值不同,如下图:

下面是简化后的String类源码:
//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);
}可以很明显的看出,String对象中的value域并没有变化,还是指向之前的那个value。
JDK6中的substring()方法引出的一个问题
如果有一个很长很长的字符串,你每次只需要截取其中很短的一小段,但是如果你用substring()的话实际会得到很长很长的字符串的引用,所以当使用的时候必然导致性能降低!
JDK7中的substring()
这个问题在JDK7中被改进了,创建了一个新的字符数组:

String类在JDK7中的源码:
//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);
}可以看到,在构造函数中,创建了一个全新的字符数组value,并且String类中不再使用offset和count做为属性了。
String & StringBuilder & StringBuffer的区别
String vs StringBuilder:StringBuilder是可变的,这就意味你在创建对象之后还可以去修改它的值。StringBuilder vs StringBuffer:StringBuffer是同步的,意味着它是线程安全的,但是就会比StringBuilder慢些。
版权声明
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。 本站博文除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。