JDK源碼學習--String類

String類

String對象不可變,所以可以共享,當對象創建完之後,該對象的內容(字符序列)是不允許改變的,細看源碼發現內容改變則會new一個新的String對象,返回到原地址中。String類維護的char數組被final所修飾,String類中每一個看起來會修改String值的方法,實際上都是創建了一個全新的String對象,而原來的字符串還是存在的,並且產生垃圾


一、實現接口

	public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

String類實現了Serializable接口支持序列化和反序列化,實現了Comparable接口支持字符串的比較,實現CharSequence接口說明它是一個字符序列。


二、String成員變量

    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

String底層使用一個char字符數組來維護並且是final類型,不能被改變,操作字符串實際上就是操作這個字符數組,所以只要一個值改變就會生成一個新的String類型對象。


三、構造方法

1、無參構造器

初始化一個新創建的 String 對象,使其表示一個空字符序列。

	public String() {
        this.value = new char[0];
    }

2、String 參數

有參構造方法,常用的通過構造方法創建對象的方法。

	public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

延伸:創建字符串對象兩種方式

  • 直接賦值方式創建,對象存儲在方法區的常量池
String str1="ABCD";//直接賦值的方式
  • 通過本構造方法創建,字符串對象存儲在堆內存
String str2=new String("ABCD");//實例化的方式

那麼這兩種方式有什麼不同呢?
面試題1:上述的兩種方法分別創建了幾個String對象?

首先你需要知道JVM的內存模型:常量池(專門存儲常量的地方,在方法區中)分爲編譯常量池(存儲字節碼的相關信息)和運行常量池(存儲常量數據)。
當執行方式1的時候,會在常量池中添加一個新的ABCD字符,str指向常量池的ABCD
當執行方式2的時候,通過new操作符,在堆空間新開闢一塊空間用來存儲新的String對象。而此時常量池中已經有了ABCD字符,堆中的String對象直接指向常量池中的ABCD,而str則指向堆空間中的String對象。
在這裏插入圖片描述
方式1:如果常量池中,存在”ABCD”,那麼str1直接引用,此時不創建String對象.否則,先在常量池先創建”ABCD”內存空間,再引用.
方式2:首先通過new關鍵字會在堆空間創建一塊新的內存區域,至少會創建一個String對象。最多創建兩個String對象(當常量池裏面沒有“ABCD”時就會先在常量池裏面創建,然後在內存空間裏面創建一個)。

面試題2:兩種實例化方式字符串的比較

public class TestString {
    public static void main(String[] args) {
        String str1 = "Lance";
        String str2 = new String("Lance");
        String str3 = str2; 
        String str4 = "Lance";
       
        System.out.println(str1==str2);//false
        System.out.println(str1.equals(str2));//true
        System.out.println(str1==str3);//false
        System.out.println(str3==str2);//true
        System.out.println(str1==str4);//true
    }
}

使用==,只能比較引用的內存地址是否相同,使用equals方法,則比較的是字符串的內容。
當執行方式1的時候,會在常量池中添加一個新的Lance字符,str指向常量池的Lance
當執行方式2的時候,通過new在堆空間分配一塊空間存儲新的String對象。此時常量池中已經有了Lance字符,堆中的String對象直接指向常量池中的Lance,而str2則指向堆空間中的String對象。
str1==str2比較的是內存地址,因此返回false
str1.equals(str2)比較的是字符串內容,因此返回true
str1==str3引用傳遞,str3直接指向st2的堆內存地址,比較的是內存地址,因此返回false
str3==str2str3直接指向st2的堆內存地址,內存地址相同,返回true
str1==str4內存地址相同,返回true

==equals的區別
==在對字符串比較的時候,對比的是內存地址,而equals比較的是字符串內容,在開發的過程中,equals()通過接受參數,可以避免空指向。

String str = null;
if(str.equals("hello")){//此時會出現空指向異常
 ...
}
if("hello".equals(str)){//此時equals會處理null值,可以避免空指向異常
 ...
}

3、char[]參數

通過char數組參數來構造String對象,實際將入參char數組值複製給String對象的成員變量value[]。

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}

通過入參數組的一部分來構造String對象,offset爲數組被使用的開始位置,count元素的個數,傳入非法參數會報數組越界異常StringIndexOutOfBoundsException

 	public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

4、int[]參數

通過int數組構造String對象,第一步計算char的精確大小,第二步分配並填充char[],Character.isBmpCodePoint(int codePoint)確定指定的字符(Unicode代碼點)是否在Basic Multilingual Plane(BMP)中。Character.isValidCodePoint©確定指定的代碼點是否是一個有效的Unicode代碼點值,如果指定的代碼點值在MIN_CODE_POINT和MAX_CODE_POINT(含)之間此方法返回true,否則返回false。

	public String(int[] codePoints, int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count;

        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }

5、byte[]參數

通過使用指定的字符集解碼指定的 byte 子數組,構造一個新的 String,checkBounds(bytes, offset, length)是對參數進行檢查,該方法是私有的只能在String類中調用。

public String(byte bytes[], int offset, int length, String charsetName)
        throws UnsupportedEncodingException {
    if (charsetName == null)
        throw new NullPointerException("charsetName");
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(charsetName, bytes, offset, length);
}

通過使用指定的 charset 解碼指定的 byte 子數組,構造一個新的 String。Charset與charsetName是java中表示字符集的兩種不同形式。它們之間相互轉換如下:
字符串轉Charset對象:Charset charset = Charset.forName(“UTF-8”);
Charset對象轉字符串:String s = charset.displayName();

	public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

通過使用指定的 charset 解碼指定的 byte 數組,構造一個新的 String,內部調用上述方法。

public String(byte bytes[], String charsetName)
        throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
}

通過使用指定的 charset 解碼指定的 byte 數組,構造一個新的 String,內部調用上述方法。

public String(byte bytes[], Charset charset) {
    this(bytes, 0, bytes.length, charset);
}

通過使用平臺的默認字符集解碼指定的 byte 子數組,構造一個新的 String

public String(byte bytes[], int offset, int length) {
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(bytes, offset, length);
}

通過使用平臺的默認字符集解碼指定的 byte 數組,構造一個新的 String。

public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}

6、StringBuilder,StringBuffer參數

通過StringBuffer分配一個新的字符串,它包含字符串緩衝區參數中當前包含的字符序列。StringBuffer內部維護了一個char[],這裏將StringBuffer數組中的內容複製給String對象中的數組,並且加了synchronized塊保證線程安全。

	public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

通過StringBuilder分配一個新的字符串,它包含字符串生成器參數中當前包含的字符序列,同樣是將StringBuilder數組中的內容複製給String對象中的數組。

public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

7、常用方法

length():

獲取字符串長度,返回字符串中所包含的字符數目,即char數組的長度

public int length() {
        return value.length;
}

isEmpty():

判斷字符串是否爲空,即判斷value數組的長度爲0即可

 	public boolean isEmpty() {
        return value.length == 0;
    }

charAt(int index):

按下標獲取單個字符,返回指定索引處的 char 值。

	public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

codePointAt(int index):

獲取字符串指定索引位置的字符的代碼點。

	public int codePointAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointAtImpl(value, index, value.length);
    }

codePointBefore(int index):

獲取字符串指定索引位置前一個字符的代碼點。

	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);
    }

codePointCount(int beginIndex, int endIndex):

獲取字符串beginIndex-endIndex區間內代碼點個數

	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);
    }

getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)

獲取子串,把它拷貝到目標字符數組

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin < 0) { // 如果源數組開始位置<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);
}

getBytes(String charsetName)

使用給定的 charset 將此 String 編碼到 byte 序列,並將結果存儲到新的 byte 數組。

	public byte[] getBytes(String charsetName)throws UnsupportedEncodingException {
        if (charsetName == null) throw new NullPointerException();
        return StringCoding.encode(charsetName, value, 0, value.length);
    }
    public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return StringCoding.encode(charset, value, 0, value.length);
    }
    public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);
    }

equals(Object anObject)

equals方法重寫了Object類的equals方法,會判斷兩個字符串的每一個字符是否相等。比較兩個引用指向的String對象內容是否相同,將此字符串與指定的對象比較。當且僅當該參數不爲 null,並且是與此對象表示相同字符序列的 String 對象時,結果才爲 true。

public boolean equals(Object anObject) {
    if (this == anObject) { // 如果兩個引用指向的是同一個String對象
        return true;
    }
    if (anObject instanceof String) { // 如果第2個引用指向的對象是String實例
        String anotherString = (String)anObject; // 強制類型轉換
        int n = value.length; // 獲取第1個引用指向的String對象的字符串長度
        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;
}

equalsIgnoreCase(String anotherString)

將此 String 與另一個 String 比較,不考慮大小寫。如果兩個字符串的長度相同,並且其中的相應字符都相等(忽略大小寫),則認爲這兩個字符串是相等的。

	public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }
    
    判斷兩個字符串在忽略大小寫的情況下是否相等,主要調用regionMatches方法
    public boolean regionMatches(boolean ignoreCase, 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) {
            char c1 = ta[to++];
            char c2 = pa[po++];
            //在這裏先行判斷,如果相等就直接跳過後面即可,可以提高效率
            if (c1 == c2) {
                continue;
            }
            if (ignoreCase) {
                // If characters don't match but case may be ignored,
                // try converting both characters to uppercase.
                // If the results match, then the comparison scan should
                // continue.
                char u1 = Character.toUpperCase(c1);
                char u2 = Character.toUpperCase(c2);
                //都轉換成大寫的形式,如果相等,則跳過
                if (u1 == u2) {
                    continue;
                }
                // Unfortunately, conversion to uppercase does not work properly
                // for the Georgian alphabet, which has strange rules about case
                // conversion.  So we need to make one last check before
                // exiting.
                if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
                    continue;
                }
            }
            return false;
        }
        return true;
    }

compareTo(String anotherString)

compareTo實現Comparable接口的方法用於比較兩個String對象的大小,如果兩個字符串的字符序列相等,則返回0;不相等時,從兩個字符串第0個字符開始比較,返回第一個不相等的字符差。另一種情況,較長的字符串的前面部分恰好是較短的字符串,則返回他們的長度差。

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;
}

startsWith(String prefix, int toffset) & startsWith(String prefix)

測試此字符串從指定索引開始的子字符串是否以指定前綴開始

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;
}
 
//測試此字符串是否以指定的前綴開始。 
public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);
}

endsWith(String suffix)

調用了startsWith方法,測試此字符串是否以指定的後綴結束。

public boolean endsWith(String suffix) {
    return startsWith(suffix, value.length - suffix.value.length);
}

hashCode()

重寫Object的hashCode方法獲取散列碼方法,返回此字符串的哈希碼。String 對象的哈希碼根據以下公式計算: s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1] 使用 int 算法,這裏 s[i] 是字符串的第 i 個字符,n 是字符串的長度,^ 表示求冪。(空字符串的哈希值爲 0。)

public int hashCode() {
    // 獲取字符串緩存散列碼
    int h = hash;
    if (h == 0 && value.length > 0) { // 如果字符串緩存散列碼爲0並且字符串數組長度大於0
        // 定義字符數組指針
        char val[] = value;
 
        // 遍歷每個字符
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i]; // 31 * h會被JVM優化成(h << 5) - h
        }
        hash = h; // 修改字符串緩存散列碼
    }
    return h;
}

indexOf 方法集

//獲取指定字符在字符串中第一次出現的索引位置
public int indexOf(int ch) {
    return indexOf(ch, 0); // 從0開始查找
}

// 獲取在此字符串中第一次出現指定字符處的索引,從指定的索引開始搜索。Unicode指統一碼
public int indexOf(int ch, int fromIndex) {
    // 獲取字符數組長度
    final int max = value.length;
    if (fromIndex < 0) { // 如果起始位置<0
        fromIndex = 0; // 起始位置置0
    } else if (fromIndex >= max) { // 如果起始位置>=字符數組長度
        return -1;
    }

    // Character.MIN_SUPPLEMENTARY_CODE_POINT是BmpCode代碼點,如果ch是非輔助代碼點或者負值(無效代碼點)
    if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { 
        final char[] value = this.value; // 創建字符數組的指針
        for (int i = fromIndex; i < max; i++) { // 從fromIndex開始遍歷每個字符
            if (value[i] == ch) { // 如果找到ch字符
                return i;
            }
        }
        return -1;
    } else { // 尋找ch在輔助部分中的索引
        return indexOfSupplementary(ch, fromIndex);
    }
}
 
//返回指定字符在此字符串中最後一次出現處的索引。
public int lastIndexOf(int ch) {
    return lastIndexOf(ch, value.length - 1);
}
 
//返回指定字符在此字符串中最後一次出現處的索引,從指定的索引處開始進行反向搜索。
public int lastIndexOf(int ch, int fromIndex) {
    if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
        final char[] value = this.value;
        int i = Math.min(fromIndex, value.length - 1);
        for (; i >= 0; i--) {
            if (value[i] == ch) {
                return i;
            }
        }
        return -1;
    } else {
        return lastIndexOfSupplementary(ch, fromIndex);
    }
}
 
//返回指定子字符串在此字符串中第一次出現處的索引。
public int indexOf(String str) {
    return indexOf(str, 0);
}
 
//返回指定子字符串在此字符串中第一次出現處的索引,從指定的索引開始。
public int indexOf(String str, int fromIndex) {
    return indexOf(value, 0, value.length,
            str.value, 0, str.value.length, fromIndex);
}
 
 
// 獲取參數子串在該字符串中從起始位置開始第一次出現的位置
static int indexOf(char[] source, int sourceOffset, int sourceCount,
        String target, int fromIndex) {
    return indexOf(source, sourceOffset, sourceCount,
                   target.value, 0, target.value.length,
                   fromIndex);
}
 
    /* @param   source       the characters being searched.//這裏就是value數組
     * @param   sourceOffset offset of the source string./ //源字符串的偏移量
     * @param   sourceCount  count of the source string.    //這裏是value數組的長度
     * @param   target       the characters being searched for.  //待搜索目標字符串
     * @param   targetOffset offset of the target string.   //待搜索目標字符串的偏移量
     * @param   targetCount  count of the target string.   //待搜索目標字符串的長度
     * @param   fromIndex    the index to begin searching from. //起始位置
     */
    // 獲取參數子串在該字符串中從起始位置開始第一次出現的位置
    // source是目標串(該字符串),target是模式串(子串)
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
        char[] target, int targetOffset, int targetCount,
        int fromIndex) {
        if (fromIndex >= sourceCount) {//越界了
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (targetCount == 0) {
            return fromIndex;
        }
 
        char first = target[targetOffset];//待搜索字符串第一個字符
        int max = sourceOffset + (sourceCount - targetCount);//搜索第一個匹配的字符時所能達到的最大值,因爲要保證後面的長度>=targetCount
 
        // 樸素匹配算法
        //下面這裏就是核心搜索算法了,會先匹配第一個字符,然後依次向後移,直到完全匹配
        //或者是匹配到max仍然沒有匹配成功
        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            /* Look for first character. */
            if (source[i] != first) {// 如果第一個字符不匹配
                while (++i <= max && source[i] != first);// 尋找第一個匹配上的字符
            }
 
            /* Found first character, now look at the rest of v2 */
            //可以注意這裏i下標只是用來匹配第一個字符,因爲有可能部分匹配時,需要從先在匹配
            //所以這裏重新應用下標j
            if (i <= max) {// 匹配除了第一個字符的其他部分
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j]
                        == target[k]; j++, k++);
 
                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }//當匹配失敗時,返回-1
 
//返回指定子字符串在此字符串中最右邊出現處的索引。
public int lastIndexOf(String str) {
    return lastIndexOf(str, value.length);
}
 
public int lastIndexOf(String str, int fromIndex) {
    return lastIndexOf(value, 0, value.length,
            str.value, 0, str.value.length, fromIndex);
}

substring方法集

//返回一個新的字符串,它是此字符串的一個子字符串。該子字符串從指定索引處的字符開始,直到此字符串末尾。
public String substring(int beginIndex) {
    if (beginIndex < 0) { // 如果起始下標<0
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex; // 獲取截取長度
    if (subLen < 0) { // 如果截取長度<0
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
 
//返回一個新字符串,它是此字符串的一個子字符串。該子字符串從指定的 beginIndex 處開始,直到索引 endIndex - 1 處的字符。因此,該子字符串的長度爲 endIndex-beginIndex。 
public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) { // 如果起始下標<0
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) { // 如果末尾下標>字符數組長度
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex; // 獲取截取長度
    if (subLen < 0) { // 如果截取長度<0
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
}

concat(String str)

將指定字符串連接到此字符串的結尾,如果參數字符串的長度爲 0,則返回此 String 對象。否則,創建一個新的 String 對象,用來表示由此 String 對象表示的字符序列和參數字符串表示的字符序列連接而成的字符序列。

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);
}
 
void getChars(char dst[], int dstBegin) {
            System.arraycopy(value, 0, dst, dstBegin, value.length);
    }

replace(char oldChar, char newChar)

將字符串中所有的舊字符oldChar,替換爲新的字符newChar。邏輯是:先找到字符串中第一次出現oldChar字符的位置i。將之前的字符數組複製給新數組buf,然後從i後將字符數組中的內容複製給buf,只不過如果字符爲oldChar則替換爲newChar,然後再通過buf創建新的字符串返回。

	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;
    }

matches(String regex)

校驗此字符串是否匹配給定的正則表達式,匹配返回true,不匹配返回false。

	public boolean matches(String regex) {
        return Pattern.matches(regex, this);
    }

contains(CharSequence s)

判斷給定字符串中是否包含指定的字符序列。實際是調用indexOf方法,查找字符序列在字符串中的位置來判斷

	public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }

split方法集

		//根據指定規則切割字符串,切割全部子串。內部調用split(String regex, int limit)方法,limit爲0
		public String[] split(String regex) { 
        	return split(regex, 0);
        }
		//根據給定正則表達式的匹配拆分此字符串。
        public String[] split(String regex, int limit) {
        char ch = 0;
        if (((regex.value.length == 1 && //判斷參數長度是否爲1
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || //判斷參數不在特殊符號".$|()[{^?*+\\"中 
             (regex.length() == 2 && //判斷參數長度是否爲2
              regex.charAt(0) == '\\' &&  //第一位爲轉義符"\\"
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && //第二位不是0-9之間 '0'轉換爲int爲48 '9'轉換爲int爲57 
              ((ch-'a')|('z'-ch)) < 0 && //判斷不在 a-z之間
              ((ch-'A')|('Z'-ch)) < 0)) && //判斷不在A-Z之間
            (ch < Character.MIN_HIGH_SURROGATE || 
             ch > Character.MAX_LOW_SURROGATE))  //判斷分隔符不在特殊符號中
        {
            int off = 0;//當前索引
            int next = 0;//下一個分割符出現的索引
            boolean limited = limit > 0;//只分割前limit份還是全部分割,limit=0代表全部分割
            ArrayList<String> list = new ArrayList<>();//創建一個集合,用於存放切割好的子串
            while ((next = indexOf(ch, off)) != -1) {//判斷是否包含下個分隔符,如果有則進入循環
                if (!limited || list.size() < limit - 1) {//判斷是全部分割或當前分割次數小於總分割次數
                    list.add(substring(off, next));//切割當前索引到下一個分隔符之間的字符串並添加到list中
                    off = next + 1; //繼續切割下一下子串
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, value.length));//切割當前索引到字符串結尾的子字符串並添加到list
                    off = value.length;//將當前索引置爲字符串長度
                    break;//結束循環
                }
            }
            // If no match was found, return this
            if (off == 0) //如果找不到分隔符則返回只有本字符串的數組
                return new String[]{this};

            // Add remaining segment
            if (!limited || list.size() < limit)//如果是全部分割,或者沒有達到分割數,則追加最後一項
                list.add(substring(off, value.length));

            // Construct result
            int resultSize = list.size();
            if (limit == 0) {//移除多餘集合項
                while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];//創建對應長度數組,因爲返回結果爲字符串數組
            return list.subList(0, resultSize).toArray(result);//集合轉數組並返回
        }
        return Pattern.compile(regex).split(this, limit);//其它情況用正則的切割規則去切割
    }

方法集

join方法作用是將字符序列數組或集合通過分割符delimiter連接成一個字符串。第一個方法使用的是可變參數,第二個方法使用的可迭代參數,通過遍歷數組或集合將數組元素或集合元素添加到StringBuilder,添加前會先加入一個分割符delimiter,然後將StringBuilder中的內容返回,

	public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // Number of elements not likely worth Arrays.stream overhead.
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }
	public static String join(CharSequence delimiter,
            Iterable<? extends CharSequence> elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

toLowerCase()、toUpperCase()

英文大小寫轉換方法

	public String toLowerCase() {
        return toLowerCase(Locale.getDefault());
    }
    public String toUpperCase() {
        return toUpperCase(Locale.getDefault());
    }

trim()

這個方法是去掉首尾的空格,而實現方式也非常簡單,分別找到第一個非空格字符的下標,與最後一個非空格字符的下標,然後返回之間的子字符串。注意這裏由於應用了substring方法,所以len變量的控制要小心

	public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;   
        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }

toString()

返回字符串對象的字符串形式

	public String toString() {
        return this;
    }

toCharArray()

將此字符串轉換爲一個新的字符數組。

	public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }

format方法集

	//使用指定的格式字符串和參數返回一個格式化字符串
	public static String format(String format, Object... args) {
        return new Formatter().format(format, args).toString();
    }
	//使用指定的語言環境、格式字符串和參數返回一個格式化字符串。
    public static String format(Locale l, String format, Object... args) {
        return new Formatter(l).format(format, args).toString();
    }

valueOf方法集

//返回 Object 參數的字符串表示形式。
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
 
//返回 char 數組參數的字符串表示形式。字符數組的內容已被複制,後續修改不會影響新創建的字符串。 
public static String valueOf(char data[]) {
    return new String(data);
}
 
//返回 char 數組參數的特定子數組的字符串表示形式。 
//offset 參數是子數組的第一個字符的索引。count 參數指定子數組的長度。字符數組的內容已被複制,後續修改不會影響新創建的字符串。 
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定數組中表示該字符序列的 String。 
public static String copyValueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定數組中表示該字符序列的 String。 
public static String copyValueOf(char data[]) {
    return new String(data);
}
 
//返回 boolean 參數的字符串表示形式。 
public static String valueOf(boolean b) {
    return b ? "true" : "false";
}
 
//返回 char 參數的字符串表示形式。 
public static String valueOf(char c) {
    char data[] = {c};
    return new String(data, true);
}
 
//返回 int 參數的字符串表示形式。 
public static String valueOf(int i) {
    return Integer.toString(i);
}
 
//返回 long 參數的字符串表示形式。 
public static String valueOf(long l) {
    return Long.toString(l);
}
 
//返回 float 參數的字符串表示形式。 
public static String valueOf(float f) {
    return Float.toString(f);
}
 
//返回 double 參數的字符串表示形式。 
public static String valueOf(double d) {
    return Double.toString(d);
}
 
//本地方法,把該字符串存入常量池,返回此字符串的引用
public native String intern();</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
 
//返回 char 數組參數的字符串表示形式。字符數組的內容已被複制,後續修改不會影響新創建的字符串。 
public static String valueOf(char data[]) {
    return new String(data);
}
 
//返回 char 數組參數的特定子數組的字符串表示形式。 
//offset 參數是子數組的第一個字符的索引。count 參數指定子數組的長度。字符數組的內容已被複制,後續修改不會影響新創建的字符串。 
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定數組中表示該字符序列的 String。 
public static String copyValueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定數組中表示該字符序列的 String。 
public static String copyValueOf(char data[]) {
    return new String(data);
}
 
//返回 boolean 參數的字符串表示形式。 
public static String valueOf(boolean b) {
    return b ? "true" : "false";
}
 
//返回 char 參數的字符串表示形式。 
public static String valueOf(char c) {
    char data[] = {c};
    return new String(data, true);
}
 
//返回 int 參數的字符串表示形式。 
public static String valueOf(int i) {
    return Integer.toString(i);
}
 
//返回 long 參數的字符串表示形式。 
public static String valueOf(long l) {
    return Long.toString(l);
}
 
//返回 float 參數的字符串表示形式。 
public static String valueOf(float f) {
    return Float.toString(f);
}
 
//返回 double 參數的字符串表示形式。 
public static String valueOf(double d) {
    return Double.toString(d);
}

上一篇:JDK源碼學習-Object類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章