首頁 >类库下载 >java类库 >Java 中 char 和 String 的細節和使用注意

Java 中 char 和 String 的細節和使用注意

阿神
阿神原創
2016-11-07 17:18:031679瀏覽

char 資料類型的使用注意

在 Java 中使用 char 資料類型來表示字符,但是 char 類型並不能表示一切字符。

Unicode 字元集

首先需要知道我們在 Java 中使用的是 Unicode 字元集。在其出現之前有已經有了很多字元集,如 ANSI、GB2312 等等。由於存在眾多標準不同的字元集,這就導致了兩個問題:

  • 對於任意給定的一個代碼值,在不同的字符集中可能對應不同的字符;

  • 採用大字符集的語言其編碼長度可能不同,例如對常用字元採用單字節編碼,而其他字元則採用多位元組編碼。

Unicode 字元集的出現就是為了統一編碼,消除以上的問題。所謂字符集就是一個由眾多不同的字符組成的集合。 Unicode 字元集對每一個字元都分配了一個唯一的 代碼點(code point) 用來識別字元本身。所謂代碼點就是一個添加了 U+ 前綴的十六進位整數,如字母 A 的代碼點就是 U+0041。

有了Unicode 字元集後,我們要考慮的就是以什麼樣的方式對這些字元進行傳輸和存儲,這就是 Unicode 編碼的實現方式,我們稱為 Unicode 轉換格式(Unicode Transformation Format,簡稱 UTF)。我們熟悉的 UTF-8、 UTF-16 等就是不同的 Unicode編碼實作方式。

在Unicode 字元集誕生之初,採用 UCS-2(2-byte Universal Character Set) 這種定長的編碼方式對Unicode 字元集進行編碼,這種方式採用 16 bit 的長度來進行字元編碼,所以最多可以對 2^16 = 65536 個字元進行編碼(編碼範圍從U+0000 ~ U+FFFF)。在當時的情況下,設計者們用了不到一半的數量就對所有字元進行了編碼,並且認為剩餘的空間足夠用於未來新增字元的編碼。

不幸的是,隨著中文、日文、韓文等表意文字不斷的加入,Unicode 字元集中的字元數量很快就超過了16 位元所能編碼的最大字元數量,於是設計者們對Unicode 字元集進行了新的設計。

新的設計將字元集中的所有字元分為 17 個 代碼平面(code plane)。其中 U+0000 ~ U+FFFF 這個代碼點範圍被劃定為 基本多語言平面(Basic MultilingualPlane,簡記為BMP),其餘的字符分別劃入16 個 輔助平面(Supplementary Plane),代碼點範圍為 U +10000 ~ U+10FFFF,這些處於輔助平面的字元我們稱作 增補字元(supplementary characters)。

在Unicode 字元集中的字元被重新分割到不同平面後,需要注意以下兩個面向:

  • BMP 範圍內的字元和 UCS-2 下的字元編碼基本上保持一致,但是 BMP 中的 UCS-2 下的字元編碼基本上保持一致,但是 BMP 中的 U+ D800 ~ U+DFFF 部分留空,不分配給任何字符,作用是用於給輔助平面內的字符進行編碼。

  • 不是每個平面內的每個位置都被分配給了指定的字符,原因是:

  • 特殊用途,如 BMP 中的 U+D800 ~ U+DFFF 

    作為保留空間
  • 沒有足夠的字符
  • UTF-16

UTF-16 為16 位。代碼單元指的是字元編碼的一個最基本單元,即任一個字元必然是由 n(n≥1) 個代碼單元組成的。

在 UTF-16 下,由於16 位元長度只能表示 65536 個字符,所以就預設映射所有在 BMP 範圍內的字符,由此 U+D800 ~ U+DFFF 這個部分就留空了,那麼輔助平面的字符也就能藉助這個留空的部分來表達。這就是 UTF-16 設計的巧妙之處,在不浪費空間的情況下解決所有字元的編碼問題。

那麼怎麼表達輔助平面的字符呢?其實就是將輔助平面字元的代碼點編碼為 一對 16 bit 長的代碼單元,稱為 代理對(surrogate pair),而代理對必然落在 BMP 中的 U+D800 ~ U+DFFF 部分。這樣就解決了用 16 位元的代碼單元編碼整個 Unicode 字元集的問題。要注意的是 U+D800 ~ U+DFFF 這個部分我們可以稱作 代理區,其中 U+D800 ~ U+DBFF 這個部分稱為 高位代理區(前導代理區),U+DC00 ~ U+DFFF 這個部分稱為 低位代理區(後尾代理區)。

下面透過將 U+64321 這個處於輔助平面的字元進行 UTF-16 編碼的實例來講解輔助平面字元的編碼方式。

先將這個字元的代碼點減去 0x10000,得到長度為 20 bit 的一個值,這個值的範圍必然在 0x0000 ~ 0xFFFF之內。

Java 中 char 和 String 的細節和使用注意

將 Vx 的高位 10 bit 的值作為高位代理的運算基數 Vh,將低位 10 bit 的值作為低位代理的運算基數 Vl。這兩個 10 bit 的值的值範圍都必然在 0x0000 ~ 0x3FF 之間。

Java 中 char 和 String 的細節和使用注意

將 Vh 和 Vl 分別與高位代理區和低位代理區起始位置的代碼點進行 按位或 運算,得到的結果就是這個處於輔助平面的字符 U+64321 的 運算,得到的結果就是這個處於輔助平面的字符 U+64321 的 UTF-16 

Java 中 char 和 String 的細節和使用注意

所以最終 U+64321 這個字符就被編碼成了由高位代理和低位代理組成的一個代理對,我們需要同時用 0xD950 和 0xDF21 來表示這個字符。

透過上面的例子我們可以看到,任何輔助平面內的字元在 UTF-16 下都會被編碼為由兩個長度為16 位元的代理編碼組成的代理對,在程式中表示這個字元時,需要佔用的就不再是16 位元的空間,而是32 位元。

不建議在 Java 程式中使用 char 資料型別

經過上面對 Unicode 字元集和 UTF-16 的講解,我們現在來討論為什麼不建議在 Java 程式中使用 char 資料類型。

由於Java 採用的是16 位元的Unicode 字元集,即 UTF-16,所以在Java 中 char 資料類型是定長的,其長度永遠只有16 位,char 資料類型永遠只能表示代碼點在 U+ 0000 ~ U+FFFF 之間的字符,也就是 BMP 內的字符。如果代碼點超過了這個範圍,即使用了增補字符,那麼 char 資料類型將無法支持,因為增補字符需要 32 位的長度來存儲,我們只能轉而使用 String 來存儲這個字符。

Java 中 char 和 String 的細節和使用注意

如上編寫的程式碼,使用 char 資料類型來保存輔助平面的字符,編譯器將會報錯 Invalid character constant。

隨著互聯網用戶的不斷增多以及互聯網語言的不斷豐富,用戶越來越高頻率的在互聯網上使用一些特殊字符來表達豐富的語義,而這些字符很有可能是屬於輔助平面裡的增補字符,所以如果我們使用 char 類型來處理,就很有可能會減低我們程序的健全性。

String 的細節

取得字串長度

String 是我們在程式設計時使用的非常多的資料類型,它用來表示一個字串。查看 String 的原始碼,我們可以看到其底層實際上是使用一個 char 類型陣列在儲存我們的字元。

Java 中 char 和 String 的細節和使用注意

我們也知道呼叫其 length() 方法可以得到字串的長度,也就是字串中字元的數量。其實作是直接傳回底層 value 陣列的長度,程式碼如下:

Java 中 char 和 String 的細節和使用注意

結合我們上面對於字符編碼的知識,我們知道Java 中 char 的長度永遠是16 位,如果我們在字符串中使用了增補字符,那就意味著需要2 個 char 類型的長度才能存儲,對於 String 底層儲存字元的陣列 value 來說,就需要2 個陣列元素的位置。所以下面的這個程式我們將得到一個意料之外的結果:

Java 中 char 和 String 的細節和使用注意

按照我們的想法,字符串 tt 中應該只有 8 個字符,然而實際輸出卻是 9 個。上面我們已經講過 Java 採用的是 16 位元的 Unicode 字元集,所以在 Java 中一個程式碼單元的長度也是 16 位元。一個增補字元需要兩個代碼單元來表示,所以 tt 字串中的字元 

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
上一篇:jvm內存區域下一篇:jvm內存區域