一、属性解读
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
1、String中使用字符数组value[]保存内容,由修饰final可知String一旦被赋值,它的内容就不能改变,这是String和StringBuilder、StringBuffer的主要区别;之前我以为是因为final修饰了String类造成了String不可变,这是错误的,final修饰类只是使类变成不可继承。由于value是不可变的,多个String对象可能会共享一个value中的值。
2、offset表示String对象的内容的起始字符在value[]中的偏移,因为多个对象可能公用一个value,所以有的对象可能只用了value的一部分字符。
比如:对于substring函数,就会直接将原串的value赋给子串,而只是变更子串的offset和count
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);//子串的offset和count变更
}
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
又如,String(new StringBuilder(“xx").toString());
public String(StringBuilder builder) {
String result = builder.toString();
this.value = result.value;
this.count = result.count;
this.offset = result.offset;
}
3、count表示String对象的内容中字符的个数,它并不是指value[]的长度。所以,用count来判断对象内容是否为空或取长度。
public boolean isEmpty() {
return count == 0;
}
public int length() {
return count;
}
4、hash存储String对象的hashCode
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;//String的offset位置是对象第一个字符在value[]中的偏移
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];//挨个将字符取出进行hash,这样相同字符串的hashCode肯定是一样的
}
hash = h;
}
return h;
}
对于equals方法,先比较对象的引用再比较字符
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
二、String对象的产生
1、直接引用常量池字符
String s1 = "Hello"; //将引用指向位于方法区的常量池中的“Hello”
2、使用常量池字符串构造
String s2 = new String("Hello");//创建新的对象,并将常量池中的值赋给它,对应源码方法如下:
public String(String original) {
int size = original.count;
char[] originalValue = original.value;
char[] v;
if (originalValue.length > size) {
// The array representing the String is bigger than the new
// String itself. Perhaps this constructor is being called
// in order to trim the baggage, so make a copy of the array.
int off = original.offset;
v = Arrays.copyOfRange(originalValue, off, off+size);
} else {//其实,若这个字符串在内存中是一个完整的char[],即不是某个value[]的一部分,则不需要赋值产生新的char[]
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalValue;
}
this.offset = 0;
this.count = size;
this.value = v;
}
3、通过加号+产生字符串
3.1 常量字符的+
String s4 = "He" + "ll";//编译后为:String s4 = "Hell";
3.2 字符串对象+常量字符或字符串对象
String s5 = s1 + "XX";//编译后的代码为:String s5 = (new StringBuilder(String.valueOf(s1))).append("XX").toString();//实际产生了3个对象
3.2 常量字符串 + 常量字符或字符串对象
String s55 = "XX" + s1;//编译之后优化为(new StringBuilder("XX")).append(s1).toString();//产生了两个对象