1. 程式人生 > >Java中的程式碼點和程式碼單元

Java中的程式碼點和程式碼單元

這篇文章講的很細,但是對於初學者也很難理解,在後面的筆記中,我會陳述自己的簡單理解。 

 

摘要

    本文介紹 Java 平臺支援增補字元的方式。增補字元是 Unicode 標準中程式碼點超出 U+FFFF 的字元,因此它們無法在 Java 程式語言中描述為單個的 16 位實體(例如char資料型別)。這些字元一般極少用,但是,有些會在諸如中文或日文人名中用到,因此,在東亞國家,政府應用程式通常會要求支援這些字元。

    Java 平臺目前正在改進,以便支援對增補字元的處理,這種改進對現有的應用程式影響微乎其微。新的低層 API 在需要時能夠使用單個的字元執行。不過,大多數文字處理 API 均使用字元序列,例如String類或字元陣列。現在,這些均解釋為 UTF-16 序列,而且,這些 API 實現已轉變為正確地處理增補字元。這些改進已融入 

Java 2 平臺 5.0 版,標準版 (J2SE)。

    除詳細解釋這些改進之外,本文同時為應用程式開發人員確定和實現必要的更改提供指導,以支援整個 Unicode 字符集的使用。

    背景

    Unicode 最初設計是作為一種固定寬度的 16 位字元編碼。在 Java 程式語言中,基本資料型別char初衷是通過提供一種簡單的、能夠包含任何字元的資料型別來充分利用這種設計的優點。不過,現在看來,16 位編碼的所有 65,536 個字元並不能完全表示全世界所有正在使用或曾經使用的字元。於是,Unicode 標準已擴充套件到包含多達 1,112,064 個字元。那些超出原來的 16 位限制的字元被稱作增補字元。Unicode 標準 2.0 版是第一個包含啟用增補字元設計的版本,但是,直到 3.1 版才收入第一批增補字符集。由於 J2SE 的 5.0 版必須支援 Unicode 標準 4.0 版,因此它必須支援增補字元。

    對增補字元的支援也可能會成為東亞市場的一個普遍商業要求。政府應用程式會需要這些增補字元,以正確表示一些包含罕見中文字元的姓名。出版應用程式可能會需要這些增補字元,以表示所有的古代字元和變體字元。中國政府要求支援 GB18030(一種對整個 Unicode 字符集進行編碼的字元編碼標準),因此,如果是 Unicode 3.1 版或更新版本,則將包括增補字元。臺灣標準 CNS-11643 包含的許多字元在 Unicode 3.1 中列為增補字元。香港政府定義了一種針對粵語的字符集,其中的一些字元是 Unicode 中的增補字元。最後,日本的一些供應商正計劃利用增補字元空間中大量的專用空間收入 50,000 多個日文漢字字元變體,以便從其專有系統遷移至基於 Java 平臺的解決方案。

    因此,Java 平臺不僅需要支援增補字元,而且必須使應用程式能夠方便地做到這一點。由於增補字元打破了 Java 程式語言的基礎設計構想,而且可能要求對程式設計模型進行根本性的修改,因此,Java Community Process 召集了一個專家組,以期找到一個適當的解決方案。該小組被稱為 JSR-204 專家組,使用Unicode 增補字元支援的 Java 技術規範請求的編號。從技術上來說,該專家組的決定僅適用於 J2SE 平臺,但是由於 Java 2 平臺企業版 (J2EE) 處於 J2SE 平臺的最上層,因此它可以直接受益,我們期望 Java 2 平臺袖珍版 (J2ME) 的配置也採用相同的設計方法。

    不過,在瞭解 JSR-204 專家組確定的解決方案之前,我們需要先理解一些術語。

    程式碼點、字元編碼方案、UTF-16:這些是指什麼?

    不幸的是,引入增補字元使字元模型變得更加複雜了。在過去,我們可以簡單地說“字元”,在一個基於 Unicode 的環境(例如 Java 平臺)中,假定字元有 16 位,而現在我們需要更多的術語。我們會盡量介紹得相對簡單一些 — 如需瞭解所有詳細的討論資訊,您可以閱讀Unicode 標準第 2 章或 Unicode 技術報告 17“字元編碼模型”。Unicode 專業人士可略過所有介紹直接參閱本部分中的最後定義。

    字元是抽象的最小文字單位。它沒有固定的形狀(可能是一個字形),而且沒有值。“A”是一個字元,“€”(德國、法國和許多其他歐洲國家通用貨幣的標誌)也是一個字元。

    字符集是字元的集合。例如,漢字字元是中國人最先發明的字元,在中文、日文、韓文和越南文的書寫中使用。

    編碼字符集是一個字符集,它為每一個字元分配一個唯一數字。Unicode 標準的核心是一個編碼字符集,字母“A”的編碼為 004116 和字元“€”的編碼為20AC16.Unicode 標準始終使用十六進位制數字,而且在書寫時在前面加上字首“U+”,所以“A”的編碼書寫為“U+0041”。

    程式碼點是指可用於編碼字符集的數字。編碼字符集定義一個有效的程式碼點範圍,但是並不一定將字元分配給所有這些程式碼點。有效的 Unicode 程式碼點範圍是 U+0000 至 U+10FFFF.Unicode 4.0 將字元分配給一百多萬個程式碼點中的 96,382 程式碼點。

    增補字元是程式碼點在 U+10000 至 U+10FFFF 範圍之間的字元,也就是那些使用原始的 Unicode 的 16 位設計無法表示的字元。從 U+0000 至 U+FFFF 之間的字符集有時候被稱為基本多語言面 (BMP)。因此,每一個 Unicode 字元要麼屬於 BMP,要麼屬於增補字元。

    字元編碼方案是從一個或多個編碼字符集到一個或多個固定寬度程式碼單元序列的對映。最常用的程式碼單元是位元組,但是 16 位或 32 位整數也可用於內部處理。UTF-32、UTF-16 和 UTF-8 是 Unicode 標準的編碼字符集的字元編碼方案。

    UTF-32 即將每一個 Unicode 程式碼點表示為相同值的 32 位整數。很明顯,它是內部處理最方便的表達方式,但是,如果作為一般字串表達方式,則要消耗更多的記憶體。

    UTF-16 使用一個或兩個未分配的 16 位程式碼單元的序列對 Unicode 程式碼點進行編碼。值 U+0000 至 U+FFFF 編碼為一個相同值的 16 位單元。增補字元編碼為兩個程式碼單元,第一個單元來自於高代理範圍(U+D800 至 U+DBFF),第二個單元來自於低代理範圍(U+DC00 至 U+DFFF)。這在概念上可能看起來類似於多位元組編碼,但是其中有一個重要區別:值 U+D800 至 U+DFFF 保留用於 UTF-16;沒有這些值分配字元作為程式碼點。這意味著,對於一個字串中的每個單獨的程式碼單元,軟體可以識別是否該程式碼單元表示某個單單元字元,或者是否該程式碼單元是某個雙單元字元的第一個或第二單元。這相當於某些傳統的多位元組字元編碼來說是一個顯著的改進,在傳統的多位元組字元編碼中,位元組值 0x41 既可能表示字母“A”,也可能是一個雙位元組字元的第二個位元組。

    UTF-8 使用一至四個位元組的序列對編碼 Unicode 程式碼點進行編碼。U+0000 至 U+007F 使用一個位元組編碼,U+0080 至 U+07FF 使用兩個位元組,U+0800 至 U+FFFF 使用三個位元組,而 U+10000 至 U+10FFFF 使用四個位元組。UTF-8 設計原理為:位元組值 0x00 至 0x7F 始終表示程式碼點 U+0000 至 U+007F(Basic Latin 字元子集,它對應 ASCII 字符集)。這些位元組值永遠不會表示其他程式碼點,這一特性使 UTF-8 可以很方便地在軟體中將特殊的含義賦予某些 ASCII 字元。

    下表所示為幾個字元不同表達方式的比較:

Unicode  U+0041 U+00DF U+6771 U+10400
表示字形
UTF-32 碼單
00000041
000000DF
00006771
00010400
UTF-16 碼單
0041
00DF
6771
D801 DC00
UTF-8 碼單
41
C3 9F
E6 9D B1
F0 90 90 80


    另外,本文在許多地方使用術語字元序列或char序列概括 Java 2 平臺識別的所有字元序列的容器:char[], java.lang.CharSequence的實現(例如String類),和java.text.CharacterIterator的實現。

    這麼多術語。它們與在 Java 平臺中支援增補字元有什麼關係呢?

 

   Java 平臺中增補字元的設計方法

    JSR-204 專家組必須作出的主要決定是如何在 Java API 中表示增補字元,包括單個字元和所有形式的字元序列。專家組考慮並排除了多種方法:

        重新定義基本型別char,使其具有 32 位,這樣也會使所有形式的char序列成為 UTF-32 序列。
        在現有的 16 位型別char的基礎上,為字元引入一種新的 32 位基本型別(例如,char32)。所有形式的 Char 序列均基於 UTF-16.
        在現有的 16 位型別char的基礎上,為字元引入一種新的 32 位基本型別(例如,char32)。String和StringBuffer接受並行 API,並將它們解釋為 UTF-16 序列或 UTF-32 序列;其他char序列繼續基於 UTF-16.
        使用int表示增補的程式碼點。String和StringBuffer接受並行 API,並將它們解釋為 UTF-16 序列或 UTF-32 序列;其他char序列繼續基於 UTF-16.
        使用代理char對,表示增補程式碼點。所有形式的char序列基於 UTF-16.
        引入一種封裝字元的類。String和StringBuffer接受新的 API,並將它們解釋為此類字元的序列。
        使用一個CharSequence例項和一個索引的組合表示程式碼點。

    在這些方法中,一些在早期就被排除了。例如,重新定義基本型別char,使其具有 32 位,這對於全新的平臺可能會非常有吸引力,但是,對於 J2SE 來說,它會與現有的 Java 虛擬機器1、序列化和其他介面不相容,更不用說基於 UTF-32 的字串要使用兩倍於基於 UTF-16 的字串的記憶體了。新增一種新型別的char32可能會簡單一些,但是仍然會出現虛擬機器和序列化方面的問題。而且,語言更改通常需要比 API 更改有更長的提前期,因此,前面兩種方法會對增補字元支援帶來無法接受的延遲。為了在餘下的方法中篩選出最優方案,實現小組使用四種不同的方法,在大量進行低層字元處理的程式碼(java.util.regex包)中實現了對增補字元支援,並對這四種方法的難易程度和執行表現進行了比較。

    最終,專家組確定了一種分層的方法:

        使用基本型別int在低層 API 中表示程式碼點,例如Character類的靜態方法。
        將所有形式的char序列均解釋為 UTF-16 序列,並促進其在更高層級 API 中的使用。
        提供 API,以方便在各種char和基於程式碼點的表示法之間的轉換。

    在需要時,此方法既能夠提供一種概念簡明且高效的單個字元表示法,又能夠充分利用通過改進可支援增補字元的現有 API.同時,還能夠促進字元序列在單個字元上的應用,這一點一般對於國際化的軟體很有好處。

    在這種方法中,一個char表示一個 UTF-16 程式碼單元,這樣對於表示程式碼點有時並不夠用。您會注意到,J2SE 技術規範現在使用術語程式碼點和 UTF-16 程式碼單元(表示法是相關的)以及通用術語字元(表示法與該討論沒有關係)。API 通常使用名稱codePoint描述表示程式碼點的型別int的變數,而 UTF-16 程式碼單元的型別當然為char.我們將在下面兩部分中瞭解到 J2SE 平臺的實質變化 — 其中一部分介紹單個程式碼點的低層 API,另一部分介紹採用字元序列的高層介面。

    開放的增補字元:基於程式碼點的 API

    新增的低層 API 分為兩大類:用於各種char和基於程式碼點的表示法之間轉換的方法和用於分析和對映程式碼點的方法。

    最基本的轉換方法是Character.toCodePoint(char high, char low)(用於將兩個 UTF-16 程式碼單元轉換為一個程式碼點)和Character.toChars(int codePoint)(用於將指定的程式碼點轉換為一個或兩個 UTF-16 程式碼單元,然後封裝到一個char[]內。不過,由於大多數情況下文字以字元序列的形式出現,因此,另外提供codePointAt和codePointBefore方法,用於將程式碼點從各種字元序列表示法中提取出來:Character.codePointAt(char[] a, int index)和String.codePointBefore(int index)是兩種典型的例子。在將程式碼點插入字元序列時,大多數情況下均有一些針對StringBuffer和StringBuilder類的appendCodePoint(int codePoint)方法,以及一個用於提取表示程式碼點的int[]的String構建器。

    幾種用於分析程式碼單元和程式碼點的方法有助於轉換過程:Character 類中的isHighSurrogate和isLowSurrogate方法可以識別用於表示增補字元的char值;charCount(int codePoint)方法可以確定是否需要將某個程式碼點轉換為一個或兩個char.但是,大多數基於程式碼點的方法均能夠對所有 Unicode 字元實現基於char的舊方法對 BMP 字元所實現的功能。以下是一些典型例子:

    Character.isLetter(int codePoint)可根據 Unicode 標準識別字母。
    Character.isJavaIdentifierStart(int codePoint)可根據 Java 語言規範確定程式碼點是否可以啟動識別符號。
    Character.UnicodeBlock.of(int codePoint)可搜尋程式碼點所屬的 Unicode 字元子集。
    Character.toUpperCase(int codePoint)可將給定的程式碼點轉換為其大寫等值字元。儘管此方法能夠支援增補字元,但是它仍然不能解決根本的問題,即在某些情況下,逐個字元的轉換無法正確完成。例如,德文字元“"?"”應該轉換為“SS”,這需要使用String.toUpperCase方法。

    注意大多數接受程式碼點的方法並不檢查給定的int值是否處於有效的 Unicode 程式碼點範圍之內(如上所述,只有 0x0 至 0x10FFFF 之間的範圍是有效的)。在大多數情況下,該值是以確保其有效的方法產生的,在這些低層 API 中反覆檢查其有效性可能會對系統性能造成負面的影響。在無法確保有效性的情況下,應用程式必須使用Character.isValidCodePoint方法確保程式碼點有效。大多數方法對於無效的程式碼點採取的行為沒有特別加以指定,不同的實現可能會有所不同。

    API 包含許多簡便的方法,這些方法可使用其他低層的 API 實現,但是專家組覺得,這些方法很常用,將它們新增到 J2SE 平臺上很有意義。不過,專家組也排除了一些建議的簡便方法,這給我們提供了一次展示自己實現此類方法能力的機會。例如,專家組經過討論,排除了一種針對String類的新構建器(該構建器可以建立一個保持單個程式碼點的String)。以下是使應用程式使用現有的 API 提供功能的一種簡便方法:

/**
* 建立僅含有指定程式碼點的新 String. 
*/ 
String newString(int codePoint) { 
    return new String(Character.toChars(codePoint));

    您會注意到,在這個簡單的實現中,toChars方法始終建立一箇中間數列,該數列僅使用一次即立即丟棄。如果該方法在您的效能評估中出現,您可能會希望將其優化為針對最為普通的情況,即該程式碼點為 BMP 字元:

/**
* 建立僅含有指定程式碼點的新 String.
* 針對 BMP 字元優化的版本。
 */ 
String newString(int codePoint) { 
    if (Character.charCount(codePoint) == 1) { 
        return String.valueOf((char) codePoint);
    } 
    else { 
        return new String(Character.toChars(codePoint));
    }

    或者,如果您需要建立許多個這樣的 string,則可能希望編寫一個重複使用toChars方法所使用的數列的通用版本:

/**
* 建立每一個均含有一個指定
* 程式碼點的新 String. 
* 針對 BMP 字元優化的版本。
 */ 
String[] newStrings(int[] codePoints) { 
    String[] result = new String[codePoints.length];
    char[] codeUnits = new char[2];
    for (int i = 0; i < codePoints.length; i++) {
         int count = Character.toChars(codePoints[i], codeUnits, 0);
        result[i] = new String(codeUnits, 0, count);
    } 
    return result;

    不過,最終您可能會發現,您需要的是一個完全不同的解決方案。新的構建器String(int codePoint)實際上建議作為String.valueOf(char)的一個基於程式碼點的備選方案。在很多情況下,此方法用於訊息生成的環境,例如:

System.out.println("Character " + String.valueOf(char) + " is invalid."); 

    新的格式化 API支援增補文字,提供一種更加簡單的備選方案:

System.out.printf("Character %c is invalid.%n", codePoint); 

    使用此高層 API 不僅簡捷,而它有很多特殊的優點:它可以避免串聯(串聯會使訊息很難本地化),並將需要移進資源包 (resource bundle) 的字串數量從兩個減少到一個。

 

增補字元透視:功能增強

    在支援使用增補字元的 Java 2 平臺中的大部分更改沒有反映到新的 API 內。一般預期是,處理字元序列的所有介面將以適合其功能的方式處理增補字元。本部分著重講述為達到此預期所作一些功能增強。

    Java 程式語言中的識別符號

    Java 語言規範指出所有 Unicode 字母和數字均可用於識別符號。許多增補字元是字母或數字,因此 Java 語言規範已經參照新的基於程式碼點的方法進行更新,以在識別符號內定義合法字元。為使用這些新方法,需要檢測識別符號的 javac 編譯器和其他工具都進行了修訂。

    庫內的增補字元支援

    許多 J2SE 庫已經過增強,可以通過現有介面支援增補字元。以下是一些例子:

        字串大小寫轉換功能已更新,可以處理增補字元,也可以實現 Unicode 標準中規定的特殊大小寫規則。
        java.util.regex包已更新,這樣模式字串和目標字串均可以包含增補字元並將其作為完整單元處理。
        現在,在java.text包內進行整理處理時,會將增補字元看作完整單元。
        java.text.Bidi類已更新,可以處理增補字元和 Unicode 4.0 中新增的其他字元。請注意,Cypriot Syllabary 字元子集內的增補字元具有從右至左的方向性。
        Java 2D API 內的字型渲染和列印技術已經過增強,可以正確渲染和測量包含增補字元的字串。
        Swing 文字元件實現已更新,可以處理包含增補字元的文字。

    字元轉換

    只有很少的字元編碼可以表示增補字元。如果是基於 Unicode 的編碼(如 UTF-8 和 UTF-16LE),則舊版的 J2RE 內的字元轉換器已經按照正確處理增補字元的方式實現轉換。對於 J2RE 5.0,可以表示增補字元的其他編碼的轉換器已更新:GB18030、x-EUC-TW(現在實現所有 CNS 11643 層面)和 Big5-HKSCS(現在實現 HKSCS-2001)。

    在原始檔內表示增補字元

    在 Java 程式語言原始檔中,如果使用可以直接表示增補字元的字元編碼,則使用增補字元最為方便。UTF-8 是最佳的選擇。在所使用的字元編碼無法直接表示字元的情況下,Java 程式語言提供一種 Unicode 轉義符語法。此語法沒有經過增強,無法直接表示增補字元。而是使用兩個連續的 Unicode 轉義符將其表示為 UTF-16 字元表示法中的兩個編碼單元。例如,字元 U+20000 寫作“/uD840/uDC00”。您也許不願意探究這些轉義序列的含義;最好是寫入支援所需增補字元的編碼,然後使用一種工具(如 native2ascii)將其轉換為轉義序列。

    遺憾的是,由於其編碼問題,屬性檔案仍侷限於 ISO 8859-1(除非您的應用程式使用新的 XML 格式)。這意味著您始終必須對增補字元使用轉義序列,而且可能要使用不同的編碼進行編寫,然後使用諸如 native2ascii 的工具進行轉換。

    經修訂的 UTF-8

    Java 平臺對經修訂的 UTF-8 已經很熟悉,但是,問題是應用程式開發人員在可能包含增補字元的文字和 UTF-8 之間進行轉換時需要更加留神。需要特別注意的是,某些 J2SE 介面使用的編碼與 UTF-8 相似但與其並不相容。以前,此編碼有時被稱為“Java modified UTF-8”(經 Java 修訂的 UTF-8)或(錯誤地)直接稱為“UTF-8”。對於 J2SE 5.0,其說明文件正在更新,此編碼將統稱為“modified UTF-8”(經修訂的 UTF-8)。

    經修訂的 UTF-8 和標準 UTF-8 之間之所以不相容,其原因有兩點。其一,經修訂的 UTF-8 將字元 U+0000 表示為雙位元組序列 0xC0 0x80,而標準 UTF-8 使用單位元組值 0x0.其二,經修訂的 UTF-8 通過對其 UTF-16 表示法的兩個代理程式碼單元單獨進行編碼表示增補字元。每個代理程式碼單元由三個位元組來表示,共有六個位元組。而標準 UTF-8 使用單個四位元組序列表示整個字元。

    Java 虛擬機器及其附帶的介面(如 Java 本機介面、多種工具介面或 Java 類檔案)在java.io.DataInput和DataOutput介面和類中使用經修訂的 UTF-8 實現或使用這些介面和類,並進行序列化。Java 本機介面提供與經修訂的 UTF-8 之間進行轉換的例程。而標準 UTF-8 由String類、java.io.InputStreamReader和OutputStreamWriter類、java.nio.charset設施 (facility) 以及許多其上層的 API 提供支援。

    由於經修訂的 UTF-8 與標準的 UTF-8 不相容,因此切勿同時使用這兩種版本的編碼。經修訂的 UTF-8 只能與上述的 Java 介面配合使用。在任何其他情況下,尤其對於可能來自非基於 Java 平臺的軟體的或可能通過其編譯的資料流,必須使用標準的 UTF-8.需要使用標準的 UTF-8 時,則不能使用 Java 本機介面例程與經修訂的 UTF-8 進行轉換。

    在應用程式內支援增補字元

    現在,對大多數讀者來說最為重要的問題是:必須對應用程式進行哪些更改才能支援增補字元?

    答案取決於在應用程式中進行哪種型別的文字處理和使用哪些 Java 平臺 API.對於僅以各種形式char序列([char[]、java.lang.CharSequence實現、java.text.CharacterIterator實現)處理文字和僅使用接受和退回序列(如char序列)的 Java API 的應用程式,可能根本不需要進行任何更改。Java 平臺 API 的實現應該能夠處理增補字元。

    對於本身解釋單個字元、將單個字元傳送給 Java 平臺 API 或呼叫能夠返回單個字元的方法的應用程式,則需要考慮這些字元的有效值。在很多情況下,往往不要求支援增補字元。例如,如果某應用程式搜尋char序列中的 HTML 標記,並逐一檢查每個char,它會知道這些標記僅使用 Basic Latin 字元子集中的字元。如果所搜尋的文字含有增補字元,則這些字元不會與標記字元混淆,因為 UTF-16 使用程式碼單元表示增補字元,而程式碼單元的值不會用於 BMP 字元。

    只有在某應用程式本身解釋單個字元、將單個字元傳送給 Java 平臺 API 或呼叫能夠返回單個字元的方法且這些字元可能為增補字元時,才必須更改該應用程式。在提供使用char序列的並行 API 時,最好轉而使用此類 API.在其他情況下,有必要使用新的 API 在char和基於程式碼點的表示法之間進行轉換,並呼叫基於程式碼點的 API.當然,如果您發現在 J2SE 5.0 中有更新、更方便的 API,使您能夠支援增補字元並同時簡化程式碼(如上格式化範例中所述),則沒有必要這樣做。

    您可能會猶豫,是將所有文字轉換為程式碼點表示法(即int[])然後在該表示法中處理,還是在大多數情況下仍採用char序列,僅在需要時轉換為程式碼點,兩者之間孰優孰劣很難確定。當然,總體來說,Java 平臺 API 相對於char序列肯定具有一定的優勢,而且採用 Java 平臺 API 可以節省記憶體空間。

    對於需要與 UTF-8 之間進行轉換的應用程式,還需要認真考慮是需要標準的 UTF-8 還是經修訂的 UTF-8,並針對每種 UTF-8 採用適當的 Java 平臺。“經修訂的 UTF-8”部分介紹進行正確選擇所需的資訊。

    使用增補字元測試應用程式

    經過前面部分的介紹後,無論您是否需要修訂應用程式,測試應用程式是否執行正常始終是一種正確的做法。對於不含有圖形使用者介面的應用程式,有關“在原始檔內表示增補字元” 的資訊有助於設計測試用例。以下是有關使用圖形使用者介面進行測試的補充資訊。

    對於文字輸入,Java 2 SDK提供用於接受“/Uxxxxxx”格式字串的程式碼點輸入方法,這裡大寫的“U”表示轉義序列包含六個十六進位制數字,因此允許使用增補字元。小寫的“u”表示轉義序列“/uxxxx”的原始格式。您可以在 J2SDK 目錄 demo/jfc/CodePointIM 內找到此輸入方法及其說明文件。

    對於字型渲染,您需要至少能夠渲染一些增補字元的字型。其中一種此類字型為 James Kass 的Code2001字型,它提供手寫體字形(如 Deseret 和 Old Italic)。利用 Java 2D 庫中提供新功能,您只需將該字型安裝到 J2RE 的 lib/fonts/fallback 目錄內即可,然後它可自動新增至在 2D 和 XAWT 渲染時使用的所有邏輯字型 — 無需編輯字型配置檔案。

    至此,您就可以確認,您的應用程式能夠完全支援增補字元了!

    結論

    對增補字元的支援已經引入 Java 平臺,大部分應用程式無需更改程式碼即可處理這些字元。解釋單個字元的應用程式可以在Character類和多種CharSequence子類中使用基於程式碼點的新 API.

    以下是Unicode和UTF-8之間的轉換關係表:

U-00000000 - U-0000007F: 0xxxxxxx 
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 

    Byte 陣列轉整數:

static int bytes2int(byte[] b)
    { 
        int mask=0xff;
        int temp=0;
        int res=0;
        for(int i=0;i<4;i++){
             res<<=8;
             temp=b[i]&mask;
             res|=temp;
        } 
    return res;
}

整數轉byte陣列:

static byte[] int2bytes(int num)
    { 
        byte[] b=new byte[4];
        int mask=0xff;
        for(int i=0;i<4;i++){ 
            b[i]=(byte)(num>>>(24-i*8));
        } 
    return b;
}