public final class String
implments java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
// 用來存儲字符串的值
private final char value[];
/** Cache the hash code for the string */
// 用來緩存hash code 調用hashCode方法時會首先對hash的值進行判斷,若是已經存在值, 因爲String是不可變的,直接返回hash便可,不用從新對該String對象的hash code的進行計算
// 默認值爲0
private int hash; // Default to 0
// 其餘屬性和方法...
}
從源碼String
源碼能夠看出,String
是final
修飾符修飾的類,表示String
不能被繼承。String
底層使用final
修飾的char[]
數組來儲存字符串的值。java
構造方法數組
String
有多種構造構造,能夠傳入char[]
、String
、StringBuffer
、StringBuilder
等屬性構造String
對象。緩存
因爲String
爲不可變類型,當調用String
中方法對String
修改操做時,都會調用相應的構造方法生成一個新的對象並返回,原String
對象並不會產生改變。下面以replace()
方法爲例:安全
public String replace(char oldChar, char newChar) {
// 只有old != new才進行處理 不然直接返回
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
// 經過while循環找到第一個oldChar的位置
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
// 建立一個新的char數組 用來存新的String對象的值
char buf[] = new char[len];
// 將第一個oldChar前的全部char都存入buf數組中
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
// 將剩餘val數組中等於oldChar的值修改成newChar後存入buf中
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
// 根據buf數組生成新的String對象
return new String(buf, true);
}
}
return this;
}
equals方法
併發
//Object中的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
//String重寫Object的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;
}
能夠看出Object
中的equals
方法直接使用「==
」判斷兩個對象是否相等,"==
"對基本類型進行判斷是,比較的是基本類型的值,而對引用類型的對象進行判斷時,比較的是兩個對象的引用的值是否相等,也就是說"==
"判斷兩個對象與對象的內容無關,只與對象的地址有關,當且僅當兩個對象的地址一致時(爲同一個對象)纔會返回true。如:app
String s1 = new String("string");
String s2 = new String("string");
s1 == s2; // false "=="只判斷地址 s1 s2是單獨的兩個對象
String
重寫了equals
方法,從源碼能夠看出,equals方法傳入的參數類型爲Object
,調用equals
時,首先會使用"==
''比較this
和待比較對象anObject
的地址是否相等,若相等則表示這個兩個對象爲同一個對象,直接返回true
便可。若是不相等,使用instanceof
判斷anObject
是否爲String
類型,若不是則返回false
,不然進行下一步操做,先判斷兩個String
對象的value
數組的長度是否相等,再循環比較數組中的每一個元素是否相等。String
的equals
比較的是兩個String
對象的內容——也就是value
數組是否相等。性能
String s1 = new String("string");
String s2 = new String("string");
s1.equals(s2); // true s1 s2是單獨的兩個對象 可是他們的內容都是 "string"
除equals
方法外,因爲String
實現了comparable
接口,也能夠經過comparable
的compareTo
方法判斷兩個字符串是否相等ui
compareTo
this
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
從源碼能夠看出,compareTo
方法的參數類型爲String
與equals
的Object
不一樣,返回值爲int
與equals
的boolean
也不同,compareTo
方法首先挨個比較兩個字符串中較短字符串的全部元素與較長字符串中對應位置的元素的大小,當對應元素不相等時返回差值,當較短字符串都比較完畢後返回兩個字符串的長度的差值。當返回值爲0時代表這兩個元素相等。返回值<0時,表示按字典順序this
在anotherString
的前面,反之亦然。atom
因爲String
實現Comparable
接口,String
列表或數組能夠經過Collections.sort
或者Arrays.sort
方法進行自動排序;同時String
對象也能夠做爲有序映射(TreeMap
)中的鍵或者有序集合(TreeSet
)中的元素,無需指定比較器。
其餘
equals
和compareTo
都用對應的xxxIgnoreCase
方法,equalsIngoreCase
與equals
相似,先比較地址再比較長度最後經過regionMatches
比較兩個字符串忽略小寫以後的內容是否相等
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
compareToIgnoreCase
經過比較器靜態內部類CaseInsensitiveComparator
的compare
方法來實現,比較邏輯與compareTo
基本相同只是多了一些大小寫轉換後判斷的操做
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
其餘方法:
indexOf()
lastIndexOf()
contains()
trim()
split()
...
final
修飾String
類,String
類不可繼承。
final
修飾char
類型的value
數組,初始化事後,value
指向的數組不能修改,而且每次對對象進行修改時都會經過構造器建立一個新的對象,保證了String
的不可變。
設計成不可變的緣由
安全
高效
使用JVM字符串常量池來緩存字符串,只有當字符串爲不可變時,才能實現字符串常量池,因爲String類型使用很頻繁,字符串常量池的存在能有效提升程序的運行效率。
(JDK1.7以後永久代換成了元空間,將字符串常量池從方法區移到了堆上)
String
的常見的建立方式有兩種:
經過字面量的方式建立 編譯時決定
String s1 = "string";
String s2 = "string";
s1 == s2 // true 字面量建立的String s1 s2都指向字符串常量池中的"string"
經過new String的方式建立 運行時決定
String s3 = new String("string");
String s4 = new String("string");
s3 == s4 // false s3 s4 指向的是 堆上的內容爲"string"的對象,此時有兩個這種對象
字面量方式建立首先會查找字符串常量池中是否已經存在該字符串,有則直接指向該字符串,不然先在常量池中建立該字符串,而後將引用指向建立的字符串;而經過new建立String,必定會在堆上建立一個字符串對象,而後判斷常量池是否已經存在該字符串的值,若是不存在則會在常量池中建立該字符串,而後將引用的值指向該字符串(s3
和 s4
指向堆中對象的地址 而堆中保存字符串常量池中「string「的地址)。
弄清字符串是在編譯時 仍是 運行時 進入常量池
String s1 = "Hello World";
String s2 = "Hello ";
String s3 = "World";
s1 == "Hello " + "World"; //true 字面量相加 直接在編譯期徹底肯定 而且放入字符串常量池中
s1 == s2 + s3; // false 引用相加 不能在編譯期肯定 s2 + s3的值
final String s4 = "Hello ";
final String s5 = "World";
s1 == s4 + s5; // true 在編譯器可以肯定final修飾的s4和s5指向的值
intern
public native String intern();
intern
是一個native
方法,當使用intern方法時,首先會檢查字符串常量池中,是否已經存在該字符串,若是已存在,直接返回該字符串,不然建立以後再返回。
String s1 = "String";
String s2 = new String("String");
s1 == s2; // false
s1 == s2.intern() // true
String
、StringBuffer
StringBuilder
因爲String
爲不可變類型,每次對String進行修改時都會產生新的String
對象,在拼接字符串的時候可能會出現不少無用的String
對象,性能會很低,此時就須要StringBuffer
來對字符串進行拼接。StringBuffer
和StringBuilder
繼承自AbstractStringBuilder
,提供了append
、insert
等方法對字符串進行拼接和修改。
StringBuffer
爲線程安全,使用sychronized
對方法加鎖實現線程安全,相應的效率也會變低,在非併發的條件下可使用StringBuilder
提升效率。
以上爲我的總結的Java String相關知識,若有不對之處,敬請批評指正!