1 介面解釋
(1)Serializable 這個序列化介面沒有任何方法和域,僅用於標識序列化的語意。
(2)Comparable<String> 用於對兩個例項化物件比較大小
(3)CharSequence 這個介面是一個只讀的字元序列。包括length(),
charAt(int index), subSequence(int start, int end)這幾個API介面
2 主要變數
(1)private final char value[];
可以看到,value[]是儲存String的內容的,即當使用String str = “abcd”;
的時候,本質上,”abcd”是儲存在一個char型別的陣列中的。
(2) private int hash;
而hash是String例項化的hashcode的一個快取。因為String經常被用於比較,比如在HashMap中。
如果每次進行比較都重新計算hashcode的值的話,那無疑是比較麻煩的,而儲存一個hashcode的快取無疑能優化這樣的操作。
(3)private static final long serialVersionUID = -6849794470754667710L;
Java的序列化機制是通過在執行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來
的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序
列化,否則就會出現序列化版本不一致的異常,如果我們不希望通過編譯來強制劃分軟體版本,即實現序列化介面的實體能夠相容先前版本,
未作更改的類,就需要顯式地定義一個名為serialVersionUID,型別為long的變數,不修改這個變數值的序列化實體都可以相互進行
序列化和反序列化
(4) private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
3 構造方法
(1) public String()
(2) public String(String original)
(3) public String(char value[])
(4) public String(char value[], int offset, int count)
(5) public String(int[] codePoints, int offset, int count)
(6) public String(byte ascii[], int hibyte, int offset, int count)
(7) public String(byte ascii[], int hibyte)
(8) public String(byte bytes[], int offset, int length, String charsetName)
(9) public String(byte bytes[], int offset, int length, Charset charset)
(10)public String(byte bytes[], String charsetName)
(11)public String(byte bytes[], Charset charset)
(12)public String(byte bytes[], int offset, int length)
(13)public String(byte bytes[])
(14)public String(StringBuffer buffer)
(15)public String(StringBuilder builder)
String支援多種初始化方法,包括接收String,char[],byte[],StringBuffer等多種引數型別的初始化方法。
但本質上,其實就是將接收到的引數傳遞給全域性變數value[]。
4 內部方法
(1)public int length() {
return value.length;
}
(2)public boolean isEmpty() {
return value.length == 0;
}
(3)public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
知道了String其實內部是通過char[]實現的,那麼就不難發現length(),isEmpty(),charAt()這些方法其實就是在內部呼叫陣列的方法。
(4)//返回指定索引的程式碼點
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
//返回指定索引前一個程式碼點
(5) public int codePointBefore(int index) {
int i = index - 1;
if ((i < 0) || (i >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointBeforeImpl(value, index, 0);
}
//返回指定起始到結束段內字元個數
(6)public int codePointCount(int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
throw new IndexOutOfBoundsException();
}
return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
}
//返回指定索引加上codepointOffset後得到的索引值
(7)public int offsetByCodePoints(int index, int codePointOffset) {
if (index < 0 || index > value.length) {
throw new IndexOutOfBoundsException();
}
return Character.offsetByCodePointsImpl(value, 0, value.length,
index, codePointOffset);
}
//將字串複製到dst陣列中,複製到dst陣列中的起始位置可以指定。值得注意的是,該方法並沒有檢測複製到dst陣列後是否越界。
(8) void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
(9) public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
//獲取當前字串的二進位制
(10) public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
Objects.requireNonNull(dst);
int j = dstBegin;
int n = srcEnd;
int i = srcBegin;
char[] val = value; /* avoid getfield opcode */
while (i < n) {
dst[j++] = (byte)val[i++];
}
}
(11)public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
(12)public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
(13) 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;
}
(14)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;
}
(15)public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
這個主要是用來比較String和StringBuffer或者StringBuild的內容是否一樣。可以看到傳入引數是CharSequence ,
這也說明了StringBuffer和StringBuild同樣是實現了CharSequence。原始碼中先判斷引數是從哪一個類例項化來的,
再根據不同的情況採用不同的方案,不過其實大體都是採用上面那個for迴圈的方式來進行判斷兩字串是否內容相同。
(16)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;
}
這個就是String對Comparable介面中方法的實現了。其核心就是那個while迴圈,通過從第一個開始比較每一個字元,
當遇到第一個較小的字元時,判定該字串小。
(17)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;
}
這個就是String對Comparable介面中方法的實現了。其核心就是那個while迴圈,通過從第一個開始比較每一個字元,當遇到第一個較小的字元時,判定該字串小
(18)
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
這個也是比較字串大小,規則和上面那個比較方法基本相同,差別在於這個方法忽略大小寫
(19)public boolean regionMatches(int toffset, String other, int ooffset,
int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
比較該字串和其他一個字串從分別指定地點開始的n個字元是否相等。看程式碼可知道,其原理還是通過一個while去迴圈對應的比較區域進行判斷,但在比較之前會做判定,判定給定引數是否越界。
(20) public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
判斷當前字串是否以某一段其他字串開始的,和其他字串比較方法一樣,其實就是通過一個while來迴圈比較。
(21)public int indexOf(int ch, int fromIndex) {
final int max = value.length;
if (fromIndex < 0) {
fromIndex = 0;
} else if (fromIndex >= max) {
// Note: fromIndex might be near -1>>>1.
return -1;
}
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.value;
for (int i = fromIndex; i < max; i++) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return indexOfSupplementary(ch, fromIndex);
}
}
(22) public int indexOf(int ch) {
return indexOf(ch, 0);
}
(23) public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
這個方法可以返回字串中一個子串,看最後一行可以發現,其實就是指定頭尾,然後構造一個新的字串。
(24) public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
concat的作用是將str拼接到當前字串後面,通過程式碼也可以看出其實就是建一個新的字串。
(25) public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
替換操作,主要是將原來字串中的oldChar全部替換成newChar。看這裡實現,主要是先找到第一個所要替換的字串的位置 i ,
將i之前的字元直接複製到一個新char陣列。然後從 i 開始再對每一個字元進行判斷是不是所要替換的字元。
(26) public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ` `)) {
st++;
}
while ((st < len) && (val[len - 1] <= ` `)) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
這個函式平時用的應該比較多,刪除字串前後的空格,原理是通過找出前後第一個不是空格的字串,返回原字串的該子串。
(27) public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
返回一個新的字元型別的字串
本文借鑑於 https://www.cnblogs.com/liste…,情聯絡刪除