String直接繼承Object
含有一個char[] value,還有一個int hash默認值為0
new String()的構造產生的是一個值為””的字符數組
String(char value[], int offset, int count)當count=0且offset<=value.length時構造一個值為””的字符串。offset>0且offset+count<=value.length時復制該部分子串。其余情況都會拋錯。
字符數據類型是一個采用UTF-16編碼表示Unicode代碼點的代碼單元。大多數的常用Unicode字符使用一個代碼單元就可以表示,而輔助字符需要一對代碼單元表示。而length返回的是UTF-16下的代碼單元的數量,而codePointCount返回的是代碼點的數量。對於大部分人工輸入的字符,這兩者是相等的,會出現length比codePointCount長的通常是某些數學或者機器符號,需要兩個代碼單元來表示一個代碼點。
對於返回char[]的方法,底層調用的是System.arraycopy方法,這也是高效的數組拷貝函數。
getBytes方法會調用StringCoding.encode返回序列化后的byte[]
關於String a == String b的判斷,是指a和b指向內存中的同一個對象,凡是new String初始化的對象,都不會產生a==b的情況,因為他會新開辟一個對象空間,然后復制value的值,僅當b=a初始化時a==b成立。
public static void main(String args[]) { String a, b; a = "123"; b = "123"; System.out.println(a==b);//true a = "123"; b = new String("123"); System.out.println(a==b);//false a = new String("123"); b = new String("123"); System.out.println(a==b);//false a = "123"; b = new String(a); System.out.println(a==b);//false a = new String("123"); b = a; System.out.println(a==b);//true }
而a.equals(b)先判斷a == b是否成立,再判斷b是否是String類,然后逐個比較value數組的值是否相等。equalsIgnoreCase在此基礎上忽略大小寫的區別
a.compareTo(b)比較a和b第一個不相等字符的差值,若都相等則比較長度差值。compareToIgnoreCase多一個忽略大小寫的區別。regionMatches(int toffset, String other, int ooffset, int len)則是比較a從toffset開始和other從ooffset開始長度為len的部分是否相等。
startsWith(String prefix, int toffset)字符串從tooffset位置開始和prefix是否相等。endsWith(String suffix)字符串結尾和suffix等長部分是否相等。
hashCode()調用時,若hash值為0且字符串長度不為0,則要計算hash值,方法是value數組化為31進制
indexOf是返回第一個出現的指定值的位置,可以通過fromIndex來指定開始查找的位置,而indexOfSupplementary是忽略大小寫的該方法。lastIndexOf則是從尾部開始查找最后一個。
substring根據指定的位置返回一個新的子字符串,若指定位置不符合原字符串的長度,則拋錯。
a.concat(String str)新建一個字符串內容是a+str並返回,不會修改a原本的值
public static void main(String args[]) { String a, b; a = "123"; b = "123"; a.concat(b); System.out.println(a);//123 System.out.println(a.concat(b));//123123 a = a.concat(b); System.out.println(a);//123123 }
replace(char oldChar, char newChar)生成一個新的字符串,將原字符串中的oldChar字符全部替換為newChar,不會改變原字符串的值。replaceAll(String regex, String replacement)和前一個方法相比,參數regex是正則表達式,其余相同。
public static void main(String args[]) { String a; a = "12131"; a.replace("1", "a"); System.out.println(a);//12131 a = a.replace("1", "a"); System.out.println(a);//a2a3a }
split(String regex, int limit)將字符串按照給定的正則表達式分割為字符串組,limit是分割產生的數組最大數量,對於多余部分不做分割全部保留在最后一個字符串中。
public static void main(String args[]) { String a; a = "1,2,3,4"; String[] b = a.split(","); for(String t : b){ System.out.print(t + " ");//1 2 3 4 } System.out.println(""); String[] c = a.split(",", 3); for(String t : c){ System.out.print(t + " ");//1 2 3,4 } }
toCharArray()復制出一個新的char[]而不是直接返回value
trim()生成一個新的字符串,去掉頭部的所有空格
public native String intern()這個方法的作用是在常量池當中尋找是否已經存在該字符串,若已存在則返回該引用,若不存在則在常量池新建。從上面的源碼分析中,我們可以看出String的所有操作都是返回一個新的字符串,對自身是沒有修改的,String被設計為一個不可變的final對象,理由有以下幾點:
1. 字符串常量池的需要。字符串常量池的誕生是為了提升效率和減少內存分配。、因為String的不可變性,常量池很容易被管理和優化。
2. 安全性考慮。正因為使用字符串的場景如此之多,所以設計成不可變可以有效的防止字符串被有意或者無意的篡改。(通過反射或者Unsafe直接操作內存的手段也可以實現對所謂不可變String的修改)。
3. 作為HashMap、HashTable等hash型數據key的必要。因為不可變的設計,jvm底層很容易在緩存String對象的時候緩存其hashcode,這樣在執行效率上會大大提升。
public static void main(String args[]) { String a, b; a = "123"; b = new String(a).intern(); System.out.println(a == b);//true a = "12" + "3"; b = "123"; System.out.println(a == b);//true a = "12" + "3"; b = new String("123"); System.out.println(a == b);//false a = "12" + "3"; b = new String("123").intern(); System.out.println(a == b);//true a = new String("123"); b = a.intern(); System.out.println(a == b);//false }
從上面一段代碼的運行結果我們可以看到,intern()會從常量池尋找指定的字符串,指向同一個常量池對象的時候,a==b就是成立的。這里說明一下最后一個case,首先常量池存在了”123”,然后a獲得的引用是另一個”123”(因為是new String得到的對象),而b得到的是常量池中第一個”123”的引用,所以a!=b。對於字符串相加的操作"12" + "3",操作過后常量池內會有3個字符串,"12" "3" “123”