不建議在 Java 程序中使用 char 數據類型

好了,看了標題我知道你有疑問,這裡我得承認算並半個標題黨吧。

事情是這樣的:

這裡有段程序,你跑一下,結果可能跟你想的不一樣

<code>public static void main(String[] args) {
String str = "";
System.out.println(str.length());
}/<code>

你可能認為字符串長度應該是1吧,為什麼會是2呢?這裡其實就是所謂的『坑』,說到這個坑,話就有些長了,我們先看一些關於字符的概念。

以下的基礎知識我相信大多數開發的同學都知道,如果你明白直接跳過就好。


Unicode 字符集的出現就是為了統一編碼。所謂字符集就是一個由眾多不同的字符組成的集合。

Unicode 字符集對每一個字符都分配了一個唯一的 代碼點(code point) 用來標識字符本身。

所謂代碼點就是一個添加了 U+ 前綴的十六進制整數,如字母 A 的代碼點就是 U+0041。

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

碼點如何轉換成UTF的幾種形式呢?

不建議在 Java 程序中使用 char 數據類型

如上圖所示

  • UTF-32採用的定長四字節則是32位
  • UTF-8是變長的編碼方案,可以有1,2,3,4四種字節組合
  • UTF-16是一種變長的2或4字節編碼模式

在 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)。

不建議在 Java 程序中使用 char 數據類型

在 Unicode 字符集中的字符被重新劃分到不同平面後,需要注意:

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

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

特殊用途,如 BMP 中的 U+D800 ~ U+DFFF 部分;

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

回答程序輸出長度為2而不是1的問題

我們使用的字符其實不是普通字符,而是增補字符,我們知道 Java 中 char 的長度永遠是 16 位,如果我們在字符串中使用了增補字符,那就意味著需要 2 個 char 類型的長度才能存儲,對於 String 底層存儲字符的數組 value 來說,就需要 2 個數組元素的位置。我們再看一下String 類length方法的源碼:

<code>/** 

* Returns the length of this string.
* The length is equal to the number of in the string.
*
* @return the length of the sequence of characters represented by this
* object.
*/
public int length() {
return value.length;
}/<code>

一切就明白了。java 的 String 內部用的 UTF-16 編碼,String.length() 直接返回 code unit 的個數,也就是 Java 的 2 字節 char 的個數。

當然這裡不是說絕對不要用char,只是坑多(上面只是其中一個,JDK9還有別的),建議少用而已。


分享到:


相關文章: