java.lang.String源碼分析

java.lang.String是使用頻率非常高的類。要想更好的使用java.lang.String類,瞭解其源代碼實現是非常有必要的

一、String類

String類是被final所修飾的,所以不允許被繼承和修改,String類實現了Serializable、Comparable、CharSequence這三個接口,Serializable接口使得String可序列化;Comparable爲String提供了比較器,使其可進行排序;CharSequence接口有length(),charAt(int index),subSequence(int start,int end)方法。

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

二、String屬性

String聲明瞭4個變量

    //存儲字符串的字符數組。該數組爲final變量,一旦賦值,將不會更改。
    private final char value[];
    //一個int型的變量hash用來存放計算後的該String的哈希值    
    private int hash; // Default to 0
    //提供序列化的ID
    private static final long serialVersionUID = -6849794470754667710L;
    //聲明瞭一個可序列化的字段
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

 三、String構造方法

java.lang.String對象構造方法比較多,列舉如下: 

    public String()
    public String(String original)
    public String(char value[])
    public String(char value[], int offset, int count)
    public String(int[] codePoints, int offset, int count)
    @Deprecated
    public String(byte ascii[], int hibyte, int offset, int count)
    @Deprecated
    public String(byte ascii[], int hibyte)
    public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException
    public String(byte bytes[], int offset, int length, Charset charset)
    public String(byte bytes[], String charsetName) throws UnsupportedEncodingException
    public String(byte bytes[], Charset charset)
    public String(byte bytes[], int offset, int length)
    public String(byte bytes[])
    public String(StringBuffer buffer)

在 public String(StringBuffer buffer) 中,傳入形參爲StringBuffer,StringBuffer爲線程安全類。則在此構造方法內部進行了synchronized關鍵字鎖同步。代碼如下:

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

在 public String(StringBuilder builder) 中,傳入形參爲StringBuilder,StringBuilder爲非線程安全類。則在此構造方法內部內部未做同步處理,對比 public String(StringBuffer buffer) 。代碼如下:

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

四、String常用方法

java.lang.String對象中封裝方法非常多,僅針對常用方法源代碼進行分析。如:equals(),replace(), indexOf(),startsWith(),compareTo(),regionMathes(),hashCode()。 

public boolean equals(Object anObject)

用於比較兩對象存儲內容是否相同。採用比較巧妙的方式進行排除比較:(1)先“==”比較兩對象是否是同一對象,若是,直接返回true, 否則進一步判斷;(2)判斷待比較對象類型是否是java.lang.String,若不是,直接返回false,否則進一步判斷;(3)判斷兩字符串長度是否相等,若不是直接返回false,否則進一步判斷;(4)從字符數組中第一個字符開始,依次進行比較,一旦發現不相同字符直接返回false,若所在字符均相同則返回true。對字符數組中字符依次進行比較是一件非常耗時的操作,將此操作放在最後執行,先利用其它條件進行對其進行判斷。

    public boolean equals(Object anObject) {
        //如果引用的是同一個對象,返回真
        if (this == anObject) {
            return true;
        }
        //如果不是String類型的數據,返回假
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            //如果char數組長度不相等,返回假
            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;
    }

public String replace(char oldChar, char newChar)

將字符串中指定字符替換爲新的字符。(1)先判斷待替換字符和新字符是否相同,若相同,則直接返回原字符串,若不同,則繼續執行;(2)找出第一次出現待替換字符位置i,創建新的等長字符數組,將該位置之前的字符依次放入新的字符數組中;(3)從位置i處依次遍歷比較原字符數組中字符是否是待替換字符,若是,則將新字符放入新字符數組對應位置,若不是,則將原字符數組中字符放入對應位置。巧妙做了一個小優化,直接找出第一次出現待替換字符的位置,再從此處開始遍歷,提高效率。

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

public String replace(CharSequence target, CharSequence replacement)

該方法是我們通常意義所用到的 public String replace(String target, String replacement) ,java.lang.String實現了java.lang.CharSequence接口。方法內部調用正則表達式匹配替換來實現。

    public String replace(CharSequence target, CharSequence replacement) {
          return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
                  this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }

public int indexOf(String str)

該方法是找出目標字符串是第一次出現指定子字符串的位置,若不存在,則返回-1,若存在,則返回位置座標。具體實現是調用 static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int 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,
             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);
 
         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 */
             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;
     }

public int compareTo(String anotherString)

該方法是對字符串集合進行排序的基礎,通過此方法可比較兩字符串大小,原理很簡單,源代碼如下:

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

public boolean startsWith(String prefix)

判斷目標字符串是否以指定字符子串開關,該方法內部是調用 public boolean startsWith(String prefix, int toffset) 方法實現,原理很簡單,代碼如下:

    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;
        /*如果開始查找的位置小於0或大於當前字符串長度與指定前綴長度的差值,則返回false*/
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
        //從此字符串的指定索引開始比較是否與指定前綴相等
        while (--pc >= 0) {
            if (ta[to++] != pa[po++]) {
                //不相等返回false
                return false;
            }
        }
        //相等返回true
        return true;
    }

public int hashCode()

其hashCode()代碼如下:

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

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

將一個String字符串,按照給定的參數複製到目標字符數組的方法。其中傳入4個參數:int類型的srcBegin爲字符串中要複製的第一個字符的索引;int類型的srcEnd爲字符串中要複製的最後一個字符之後的索引(要複製的最後一個字符位於索引 srcEnd-1 處);char類型的數組dst[]爲目標數組;int類型的desBegin爲目標數組中的起始偏移量。

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

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

public String trim() 

返回字符串的副本,忽略前導空白和尾部空白。這在開發中也是很常用的方法,源代碼如下:

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

public String substring(int beginIndex, int endIndex) 

返回一個新字符串,它是此字符串的一個子字符串。該子字符串從指定的 beginIndex 處開始,直到索引 endIndex - 1 處的字符。因此,該子字符串的長度爲 endIndex-beginIndex。該方法包含兩個int類型的參數,分別是: beginIndex - 起始索引(包括),endIndex - 結束索引(不包括)。源碼如下:

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        /*如果起始索引爲0並且結束索引爲此字符串的長度則返回此字符串,否則創建新的字符串*/
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章