java String 源碼閱讀筆記以及Unicode的學習

感悟

可以看到String中的方法,均是比較簡短的方法,且每一行的長度都在idea
代碼顯示框的一半長左右。非常便於閱讀。

源碼分析

string的不可變性

  //從源碼中可以看出string內部是通過數組實現的。其被final修飾,
  //即一旦定義就不會再發生改變
  private final char value[];

regionMatches 比較兩個字符串區域是否相等

public boolean regionMatches(boolean ignoreCase, int toffset,
        //......
        while (len-- > 0) {
            //......
            if (ignoreCase) {
                char u1 = Character.toUpperCase(c1);
                char u2 = Character.toUpperCase(c2);
                if (u1 == u2) {
                    continue;
                }
                //注意這裏,比較了大寫後,依然要比較小寫,對於格魯吉亞
                //字母,對大小寫的規則比較特殊,要檢查大小寫全部檢查纔行
                if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
                    continue;
                }
            }
            return false;
        }
        return true;
    }

split方法

對於將 . \轉義字符作爲分隔符需要使用split(“\.”) split(“\|”),否則其默認
使用空字符分割的

limit 參數控制模式應用的次數,因此影響結果數組的長度。如果該限制 n 大
於 0,則模式將被最多應用 n - 1 次,數組的長度將不會大於 n,而且數組的
最後項將包含超出最後匹配的定界符的所有輸入。如果 n 爲非正,則模式將
被應用盡可能多的次數,而且數組可以是任意長度。如果 n 爲零,則模式將
被應用盡可能多的次數,數組可有任何長度,並且結尾空字符串將被丟棄。

    public String[] split(String regex, int limit) {
      //當regex的長度爲1且不是“.$|()[{^?*+\\”中的時或者當長度爲2時
      //且第一個字符爲“\”轉義字符,第二個字符不是字符0-9 a-z A-Z 以及
      //utf-16之間的字符時,通過indexof處理,否則採用正則表達式處理
        char ch = 0;
        if (((regex.value.length == 1 &&
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 &&
              regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, value.length));
                    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);
    }

indexof方法

查找一個字符的位置

   public int indexOf(int ch, int fromIndex) {
        //如果是普通字符
        if (ch < Character.MIN_SUPPLEMENTARY_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);
        }
    }

//根據i位置處的char,分別獲得高位的btye值(放在char中)和低位的byte值和value數組比較
    private int indexOfSupplementary(int ch, int fromIndex) {
        if (Character.isValidCodePoint(ch)) {
            final char[] value = this.value;
            final char hi = Character.highSurrogate(ch);
            final char lo = Character.lowSurrogate(ch);
            final int max = value.length - 1;
            for (int i = fromIndex; i < max; i++) {
                if (value[i] == hi && value[i + 1] == lo) {
                    return i;
                }
            }
        }
        return -1;
    }

intern

一個和方法區相關的方法,在不同jdk版本中表現不一致(jdk6,jdk7),避免
使用吧。參考 http://blog.csdn.net/seu_calvin/article/details/52291082

string 相加的實現

  a = a + b;
  //編譯後會變成
   (new StringBuilder()).append(a).append(b).toString();
  //string是不可變的,這裏生成了一個新的對象

關於java中的字符集

Unicode 只是字符集,而沒有編碼方式。其有一套標準,要求用1-4個字節來表示一個字符。
UTF-8 、UTF-16、UTF-32是 Unicode 字符集的編碼方式

Unicode介紹

  1. Unicode的學名是”Universal Multiple-Octet Coded Character Set”,簡
    稱爲UCS,UCS可以看作是”Unicode Character Set”的縮寫
  2. 在UCS編碼中有一個叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符
  3. 在UCS編碼中有一個叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,UCS規範建議在傳輸字節流前,先傳輸字符”ZERO WIDTH NO-BREAK SPACE”,如果接收者收到FEFF,就表明這個字節流是Big-Endian的;如果收到FFFE,就表明這個字節流是Little-Endian的.字符”ZERO WIDTH NO-BREAK SPACE”又被稱作BOM。
  4. 在 Java 中直接使用Unicode 轉碼時會按照UTF-16LE 的方式拆分,並加上 BOM。 如果採用 UTF-16 拆分,在 Java 中默認採用帶有 BOM 的 UTF-16BE 拆分。
  5. 一個完整的Unicode字符叫代碼點CodePoint,而一個Java char 叫代碼單元code unit,佔兩個字節。
  6. string對象以UTF-16保存Unicode字符,需要用2個字符表示一個超大字符集。
  7. 漢字,這種表示方式爲Sruuogate,第一個字符叫Surrogate High,第二個就是Surrogate Low。判斷一個char是否是Surrogate區的字符,用Character的isHighSurrogate()/isLowSurrogate()方法。
  8. 我國本身的GB碼(國標碼)都沒有把全部漢字收錄,utf-16只是收錄了常用的漢字。

bmp code

ISO 10646 定義了一個 31 位的字符集。然而,在這巨大的編碼空間中,迄今爲止只分配了前 65534 個碼位 (0x0000 到 0xFFFD)。這個UCS的16位子集、稱爲基本多語言面 (Basic Multilingual Plane,BMP)。將被編碼在16位BMP以外的字符都屬於非常特殊的字符(比如象形文字),且只有專家在歷史和科學領域裏纔會用到它們。按當前的計劃,將來也許再也不會有字符被分配到從0x000000到0x10FFFF這個覆蓋了超過100萬個潛在的未來字符的 21 位的編碼空間以外去了。

總結

Unicode和UTF-16:1個字符佔2個字節(不管是哪國語言)

UTF-8:1個英文字符佔1個字節,一個漢字(包括日文和韓文等)佔3個字節

Java中的char默認採用Unicode編碼,所以Java中char佔2個字節

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