一、繼承體系
String是不可變類,所謂不可變類,就是創(chuàng)建該類的實例后,該實例的屬性是不可改變的。同時String類被final修飾,不可被繼承。

二、成員變量
// 存儲字符串的字符數(shù)組,final關鍵字修飾了,這是String是不可變類的緣由所在
private final char value[];
// hash碼
private int hash; // Default to 0
// 序列碼
private static final long serialVersionUID = -6849794470754667710L;
三、常用方法
1、public boolean equals(Object anObject)方法
public boolean equals(Object anObject) {
//如果引用的是同一個對象,返回真
if (this == anObject) {
return true;
}
//如果不是String類型的數(shù)據(jù),返回假
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
//如果char數(shù)組長度不相等,返回假
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
//從后往前單個字符判斷,如果有不相等,返回假
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
//每個字符都相等,返回真
return true;
}
}
return false;
}
equals方法經(jīng)常用得到,它用來判斷兩個對象從實際意義上是否相等,String對象判斷規(guī)則:
- 內(nèi)存地址相同,則為真。
- 如果對象類型不是String類型,則為假。否則繼續(xù)判斷。
- 如果對象長度不相等,則為假。否則繼續(xù)判斷。
- 從后往前,判斷String類中char數(shù)組value的單個字符是否相等,有不相等則為假。如果一直相等直到第一個數(shù),則返回真。
注:若被比較的入?yún)ο鬄榭眨瑒t會被報NEP,所以使用該方法需要讓調(diào)用者和入?yún)⒌腟tring對象都不為null才可使用。
2、int compareTo(String anotherString)方法
public int compareTo(String anotherString) {
//自身對象字符串長度len1
int len1 = value.length;
//被比較對象字符串長度len2
int len2 = anotherString.value.length;
//取兩個字符串長度的最小值lim
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
//從value的第一個字符開始到最小長度lim處為止,如果字符不相等,返回自身(對象不相等處字符-被比較對象不相等字符)
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
//如果前面都相等,則返回(自身長度-被比較對象長度)
return len1 - len2;
}
這個方法寫的很巧妙,先從0開始判斷字符大小。如果兩個對象能比較字符的地方比較完了還相等,就直接返回自身長度減被比較對象長度,如果兩個字符串長度相等,則返回的是0,巧妙地判斷了三種情況。
ps:同樣的入?yún)⒑驼{(diào)用者String對象若有一個為空,則都會報NEP。
3、String concat(String str)方法
public String concat(String str) {
int otherLen = str.length();
//如果被添加的字符串為空,返回對象本身
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
// 由此可以看出String的字符串操作都會新生成一個對象返回
return new String(buf, true);
}
四、特殊說明
1、String是不可變類
2、String不可被繼承
3、String維護了一個字符串常量池(字符串內(nèi)部列表)
JAVA中所有的對象都存放在堆里面,包括String對象。字符串常量保存在JAVA的.class文件的常量池中,在編譯期就確定好了。虛擬機為每個被裝載的類型維護一個常量池。常量池就是該類型所用常量的一個有序集合,包括直接常量(string、integer和float point常量)和對其他類型、字段和方法的符號引用。
例如:
String s = new String( "myString" );
其中字符串常量是"myString",在編譯時被存儲在常量池的某個位置。在解析階段,虛擬機發(fā)現(xiàn)字符串常量"myString",它會在一個內(nèi)部字符串常量列表中查找,如果沒有找到,那么會在堆里面創(chuàng)建一個包含字符序列[myString]的String對象s1,然后把這個字符序列和對應的String對象作為名值對( [myString], s1 )保存到內(nèi)部字符串常量列表中。如下圖所示:

如果虛擬機后面又發(fā)現(xiàn)了一個相同的字符串常量myString,它會在這個內(nèi)部字符串常量列表內(nèi)找到相同的字符序列,然后返回對應的String對象的引用。維護這個內(nèi)部列表的關鍵是任何特定的字符序列在這個列表上只出現(xiàn)一次。
例如,String s2 = "myString",運行時s2會從內(nèi)部字符串常量列表內(nèi)得到s1的返回值,所以s2和s1都指向同一個String對象。但是String對象s在堆里的一個不同位置,所以和s1不相同。
JAVA中的字符串常量可以作為String對象使用,字符串常量的字符序列本身是存放在常量池中,在字符串內(nèi)部列表中每個字符串常量的字符序列對應一個String對象,實際使用的就是這個對象。
字符串截留intern()方法
在某些上下文環(huán)境下,僅僅保留某個字符串的一份copy能夠提高內(nèi)存的使用和效率。String類的intern()方法可以截留字符串,如果String對象包含的字符序列不在字符串常量內(nèi)部列表中,那么就把這個String對象包含的字符序列和String對象的引用作為名值對保存到內(nèi)部列表中,最后intern()返回一個指向String對象本身的引用;如果String對象包含的字符序列在字符串常量內(nèi)部列表中,那么就返回列表的名值對中的對應的字符串對象引用,而String對象本身的就會被丟棄。
例如,s.intern()就會返回和s2相同的引用,而以前的s對象就會被垃圾回收。
使用intern()要注意,被放到字符串內(nèi)部列表中的字符串對象是不會被垃圾回收的,生命周期和整個程序相同,所以如果使用不當會造成內(nèi)存泄露。
五、測試
1、代碼
public class StringTest {
public static void main(String[] args) {
test1();
}
/**
* String的字符常量池及intern()方法
*/
private static void test1(){
String s1 = new String("ABC");
String s2 = new String("ABC");
System.out.println("s1 == s2 : " + (s1==s2));// false
String s3 = "ABC";
String s4 = "ABC";
String s5 = "AB" + "C";
System.out.println("s1 == s3 : " + (s1==s3));// false
System.out.println("after s1.intern(), s1 == s3 : " + (s1.intern()==s3));// true
System.out.println("s3 == s4 : " + (s3==s4));// true
System.out.println("s3 == s5 : " + (s3==s5));// true
String s6 = "ABC";
String s7 = "AB";
String s8 = s7 + "C";
System.out.println("s6 == s8 : " + (s6==s8));// false
}
}
2、打印結(jié)果
s1 == s2 : false
s1 == s3 : false
after s1.intern(), s1 == s3 : true
s3 == s4 : true
s3 == s5 : true
s6 == s8 : false