主要屬性和方法
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[]陣列來儲存字串的值,
構造方法

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,如:
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方法判斷兩個字串是否相等
compareTo
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的前面,反之亦然,
由于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() -
...
String不可變型別
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相關知識,如有不對之處,敬請批評指正!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261263.html
標籤:其他
