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 。