String:字串常量池
一、設計思想
為字串開闢一個字串常量池,建立字串常量時,首先堅持字串常量池是否存在該字串。如存在該字串物件,返回其引用;若不存在,例項化該字串並放入常量池中,並返回其引用。
二、實現基礎
(1)String類字串例項化後的物件是不可變的,初始化是什麼則這個物件就永遠是什麼,相當於是常量,因此String的物件們可以維護成一個常量池。
(2)執行時字串常量池中有一個表,總是為池中所有的字串物件維護一個引用,所以常量池中的這些字串不會被GC回收。
三、操作常量池的情況
凡是" "形式定義的字串一定會操作常量池。
不滿足上面的情況,但是被編譯成String str = " "形式的,會操縱常量池(從中取該常量,如果取不到,就建立一個)
(1)直接賦值
String str = "Java";
當"Java"字串物件已經存在於常量池中時,str直接指向常量池中的物件。如果不存在,在常量池中建立"Java",str指向它。
(2)運算子過載
String str = "Ja" + "va";
Java中可以使用+進行兩個字串的拼接。會被直接編譯成str = "Java",會操作常量池。事實上這句話在常量池中建立了3個物件:"Ja"、"va"、"Java"(如果常量池中原本沒有這些物件)。
注意,如果是下面的情況:
String temp = "va"; String str = "Ja" + temp;
或者:
String str = "Ja" + new String("va");
此時str不會在編譯時不會被自動拼接,即不會被編譯成str = "Java"的形式,也就不會在常量池中建立"Java"的物件。但是還是會在常量池中建立"Ja"和"va"。
四、圖形化的例子
String m = "hello,world"; String n = "hello,world"; String u = new String(m); String v = new String("hello,world");
1.會分配一個11長度的char[ ]物件['h','e','l','l','o',',','w','o','r','l','d'],並在常量池分配一個由這個char陣列組成的字串物件"hello,world",然後由m去引用這個字串。
2.用n去引用常量池裡邊的字串"hello,world",所以和m引用的是同一個物件。
3.在堆中生成一個新的字串,但內部的字元陣列引用著m內部的字元陣列。看一下原始碼就比較直觀了:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0
/** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * * @paramoriginal *A {@code String} */ public String(String original) { this.value = original.value; this.hash = original.hash; }
4.同樣會在堆中生成一個新的字串,但內部的字元陣列引用常量池裡邊的字串"hello,world"內部的字元陣列,也就是m/n內部字元陣列。
使用圖來表示的話,情況就大概是這樣的(使用虛線只是表示兩者其實沒什麼特別的關係):

圖形化.png
測試:
String m = "hello,world"; String n = "hello,world"; String u = new String(m); String v = new String("hello,world"); System.out.println(m == n); //true System.out.println(m == u); //false System.out.println(m == v); //false System.out.println(u == v); //false
五、String的equals()和intern()
(1)在Java中用==判斷左右兩邊非基本資料型別的引用,是指判斷兩個引用是否是引用同一個物件。String的equals()方法則是判斷兩個字串內部引用的字元陣列物件的值是否相同(注意不要求是引用同一個陣列物件)。原始碼如下:
/** * Compares this string to the specified object.The result is {@code * true} if and only if the argument is not {@code null} and is a {@code * String} object that represents the same sequence of characters as this * object. * * @paramanObject *The object to compare this {@code String} against * * @return{@code true} if the given object represents a {@code String} *equivalent to this string, {@code false} otherwise * * @see#compareTo(String) * @see#equalsIgnoreCase(String) */ public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; 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; }
(2)intern()方法:如果常量池中有與本字串相同的(equals)字串,就直接返回池中物件,如果沒有就在常量池中建立該並返回其引用。
因此對兩個字串使用intern()和==,也可以起到和equals()一樣的功能