String原始碼解讀以及的intern()方法探究
String的屬性
先看下String的屬性如下:
/**通過陣列儲存值*/
private final char value[];
/** 預設hashcode 為0 */
private int hash; // Default to 0
/**描述序列化類中的序列化欄位*/
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
hashCode()計算方式如下:
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
為啥子用31計算,傳說是能更多的避免hash值重複與編譯器優化,沒怎麼明白
建構函式
給出幾個常見的知道編碼方式的建構函式
/**用位元組陣列構建字串,並指定編碼名稱*/ public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException("charsetName"); checkBounds(bytes, offset, length); this.value = StringCoding.decode(charsetName, bytes, offset, length); } /**用位元組陣列構建字串,並指定編碼集*/ public String(byte bytes[], int offset, int length, Charset charset) { if (charset == null) throw new NullPointerException("charset"); checkBounds(bytes, offset, length); this.value = StringCoding.decode(charset, bytes, offset, length); } public String(byte bytes[], String charsetName) throws UnsupportedEncodingException { this(bytes, 0, bytes.length, charsetName); }
intern()方法
返回: 一個字串,內容與此字串相同,但它保證來自字串池中。
public String intern()
返回字串物件的規範化表示形式。一個初始時為空的字串池,它由類 String 私有地維護。
當呼叫 intern 方法時,如果池已經包含一個等於此 String 物件的字串(該物件由 equals(Object) 方法確定),則返回池中的字串。否則,將此 String 物件新增到池中,並且返回此 String 物件的引用。它遵循對於任何兩個字串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern()
才為 true。
所有字面值字串和字串賦值常量表達式都是內部的。
我們知道,一個Java程式執行後,String類會在記憶體的方法區中維護一個字串池。對一個字串呼叫intern()方法後,會先檢查池內是否有該字串,若有則返回;若沒有沒有則先建立再返回,確保返回的字串已經以字面量的形式存在於池中。
public class Test {
public static void main(String argv[])
{
String s1 = "HelloWorld";
String s2 = new String("HelloWorld");
String s3 = "Hello";
String s4 = "World";
String s5 = "Hello" + "World";
String s6 = s3 + s4;
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s1 == s6.intern());
System.out.println(s2 == s2.intern());
}
}
執行結果如下:
false
true
false
true
false
解釋一下: s1 建立的 HelloWorld 存在於方法區中的常量池其中的字串池,而 s2 建立的 HelloWorld 存在於堆中,故第一條 false 。 s5 的建立是先進行右邊表示式的字串連線,然後才對 s5 進行賦值。賦值的時候會搜尋池中是否有 HelloWorld 字串,若有則把 s5 引用指向該字串,若沒有則在池中新增該字串。顯然 s5 的建立是用了 s1 已經建立好的字面量,故 true 。 第三個比較容易弄錯,s6 = s3 + s4;中間多出來了一個StringBuilder物件, 其實相當於 s6 = new String(s3 + s4); s6 是存放於堆中的,不是字面量。所以 s1 不等於 s6 。 第四個 s6.intern() 首先獲取了 s6 的 HelloWorld 字串,然後在字串池中查詢該字串,找到了 s1 的 HelloWorld 並返回。這裡如果事前字串池中沒有 HelloWorld 字串,那麼還是會在字串池中建立一個 HelloWorld 字串再返回。總之返回的不是堆中的 s6 那個字串。 第四條也能解釋為什麼第五條是 false 。s2是堆中的 HelloWorld,s2.intern() 是字串池中的 HelloWorld 。