1. String是使用char[]數組來存儲的,而且String值在建立以後就不能夠改變了。char[]數組的定義爲:java
/** The value is used for character storage. */ private final char value[];
char[]數組value使用final修飾,所以賦值以後就不能夠改變了。再看一下String的hashCode()方法的實現就更能說明這一點:數組
/** Cache the hash code for the string */ private int hash; // Default to 0
成員變量hash,用來緩存String對象的hash code。爲何能夠緩存?緩存
由於String對象不能夠改變,求hash code也不會變,所以有了緩存,不須要每次都求。代碼以下:less
/** * Returns a hash code for this string. The hash code for a * <code>String</code> object is computed as * <blockquote><pre> * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] * </pre></blockquote> * using <code>int</code> arithmetic, where <code>s[i]</code> is the * <i>i</i>th character of the string, <code>n</code> is the length of * the string, and <code>^</code> indicates exponentiation. * (The hash value of the empty string is zero.) * * @return a hash code value for this object. */ public int hashCode() { // hash值爲緩存值 int h = hash; // 若是緩存的hash值爲0,表示已經求過hash值,因此直接返回該值 // 若是是空字符串,那麼hash值爲0 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; }
2. String的構造函數有多個,空串的構造函數爲:函數
/** * Initializes a newly created {@code String} object so that it represents * an empty character sequence. Note that use of this constructor is * unnecessary since Strings are immutable. */ public String() { this.value = new char[0]; }
從代碼能夠看出,該構造方法生成一個空的char序列,就如註釋所說「使用該構造方法是沒有任何意義的」。優化
最經常使用的構造方法莫過於new String(String original):this
/** * 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. * * @param original * A {@code String} */ public String(String original) { this.value = original.value; this.hash = original.hash; }
該構造方法其實copy了original的value值和hash值,他們仍是使用的同一串char序列。可是又建立了一個新的String對象,和original是不一樣的對象了。編碼
3. 接下來經過一個例子來了解String是如何存儲的。瞭解以前先回顧下java內存分配的幾個術語:spa
棧:由JVM分配區域,用於保存線程執行的動做和數據引用。棧是一個運行的單位,Java中一個線程就會相應有一個線程棧與之對應。線程
堆:由JVM分配的,用於存儲對象等數據的區域。
常量池:在編譯的階段,在堆中分配出來的一塊存儲區域,用於存儲顯式(不是經過new生成的)的String,float或者integer.例如String str="abc"; abc這個字符串是顯式聲明,因此存儲在常量池。
例子:
String str1 = "abc"; String str2 = "abc"; String str3 = "ab" + "c";
String str4 = new String(str2);
//str1和str2引用自常量池裏的同一個string對象 System.out.println(str1 == str2); // true
//str3經過編譯優化,與str1引用自同一個對象 System.out.println(str1 == str3); // true
//str4由於是在堆中從新分配的另外一個對象,因此它的引用與str1不一樣 System.out.println(str1 == str4); // false
圖形化示例以下圖所示:
3. 經常使用的equals()方法就比較樸實了,就是依次比較字符是否相同,
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; }
4. String實現了Comparable接口,天然有其compareTo()方法
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; // 獲取兩個String串的最小長度 int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; // 依次比較兩個String串最小長度範圍內的相同位置的字符是否相同 // 若是不一樣,則返回Unicode編碼的差值 while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } // 若是最小長度範圍內的字符徹底相同,則返回兩個String串的長度之差 return len1 - len2; }