Character與Unicode
Character 基本數據類型char 的包裝類
Character 類型的對象包含一個 char 類型的字段
該類提供了幾種方法來確定字符的類別(小寫字母、數字等),並將字符從大寫轉換為小寫,反之亦然
Character在 jdk8中, 基於版本Unicode6.0.2 標准
Character 類的方法和數據是通過 UnicodeData 文件中的信息定義的,
該文件是 Unicode Consortium 維護的 Unicode Character Database 的一部分
此文件指定了各種屬性,其中包括每個已定義 Unicode 代碼點或字符范圍的名稱和常規類別
此文件及其描述可從 Unicode Consortium 獲得,網址如下:
http://www.unicode.org
在Java中,char 數據類型(和 Character 對象封裝的值)基於原始的 Unicode 規范
將字符定義為固定寬度的 16 位實體
也就是說 char表示UTF-16編碼的代碼單元
對於0號平面來說,一個碼點使用一個代碼單元
對於輔助平面,那么一個碼點將會是兩個代碼單元
在Unicode簡介中,我們有說到,一個字符在Unicode字符集中的二進制值稱為代碼點
在UTF-16編碼中,0號平面內,一個碼點16位表示,稱之為一個代碼單元
總結下就是:
Unicode字符集中,一個字符對應一個代碼點
UTF-16中,16位數表示的是一個代碼單元
那么在0 號平面內,一個代碼單元就能夠表示一個代碼點
但是在輔助平面,一個代碼點需要兩個代碼單元
java中的char就是UTF-16中的代碼單元
所以說,一個char表示一個代碼單元,可能並不是一個字符
實在理解不了的話,就可以記住,有些字符需要兩個char表示,一個char可能僅僅是某個字符的一半
Unicode字符數據庫(Unicode Character Database UCD )
是由許多列出Unicode字符屬性和相關數據的數據文件組成
可以查看http://www.unicode.org/ucd/ 了解UCD的相關信息
查看
https://www.unicode.org/reports/tr44/#Property_Values
中的Property Value Lists章節
General Category Values以及Bidirectional Class Values章節了解
Character中定義了大量的常量,其實就是對於這兩個章節信息的描述
可以簡單地理解為是字符的屬性
比如一個字符可能是大寫字母,可能是小寫字母這樣子
比如getType方法就是專門用來返回屬性的,根據這個屬性進而可以推斷出更多的信息就像下面的的例子這樣
Unicode想要深入研究,也是一門學問,此處不再繼續深入,精力有限
對於Character我們只需要記住
| Character 類的方法和數據是通過 UnicodeData 文件中的信息定義的
char 數據類型(和 Character 對象封裝的值)基於原始的 Unicode 規范
提供的方法和數據也是基於Unicode規范來的
|
|
他將字符定義為固定寬度的 16 位實體,也就是只能表示一個代碼單元
而Unicode也可能是有兩個代碼單元組成
也就是一個代碼單元可能完整的表示了一個代碼點,也可能是一個代碼點的一部分
|
|
除非你真的有必要對UTF-16中的代碼單元進行操作,
否則最好不要在程序中使用char類型的原因
原因很簡單,一個char並不一定能夠代表一個字符,可能只是一半字符
|
接下來對Character的基礎方法進行介紹,過於深入Unicode的方法不再說明
有興趣的可以研究下
常用屬性
去掉上面說到的通用類別常量信息,還有以下屬性
| 無符號二進制形式表示 char 值的位數 | public static final int SIZE = 16; |
| 無符號二進制形式表示 char 值的字節數 | public static final int BYTES = SIZE / Byte.SIZE; |
| 表示基本類型 char 的 Class 實例 | public static final Class<Character> TYPE = (Class<Character>) Class.getPrimitiveClass("char"); |
| 常量值是 char 類型的最小值,即 '\u0000' | public static final char MIN_VALUE = '\u0000'; |
| 常量值是 char 類型的最大值,即 '\uFFFF' | public static final char MAX_VALUE = '\uFFFF' |
| Unicode代碼點的最小值 | public static final int MIN_CODE_POINT = 0x000000; |
| Unicode代碼點的最大值 | public static final int MAX_CODE_POINT = 0X10FFFF; |
| UTF-16 編碼中的 Unicode 高代理項代碼單元的最小值 | public static final char MIN_HIGH_SURROGATE = '\uD800'; |
| UTF-16 編碼中的 Unicode 高代理項代碼單元的最大值 | public static final char MAX_HIGH_SURROGATE = '\uDBFF'; |
| UTF-16 編碼中的 Unicode 低代理項代碼單元的最小值 | public static final char MIN_LOW_SURROGATE = '\uDC00' |
| UTF-16 編碼中的 Unicode 低代理項代碼單元的最大值 | public static final char MAX_LOW_SURROGATE = '\uDFFF' |
| 代理項的最小值 也就是高代理項的最小值 |
public static final char MIN_SURROGATE = MIN_HIGH_SURROGATE; |
| 代理項的最大值 也就是低代理項的最大值 |
public static final char MAX_SURROGATE = MAX_LOW_SURROGATE; |
| 增補代碼點的最小值 也就是除了0號平面的第一個值 |
public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000 |
| 可用於與字符串相互轉換的最大基數 | public static final int MAX_RADIX = 36; |
| 可用於與字符串相互轉換的最小基數 | public static final int MIN_RADIX = 2 |
可以看得出來,即使去掉了那些通用類別的常量,剩下的信息仍舊是對Unicode編碼的描述
只要看過了前文的Unicode的簡介,很好理解
構造方法
| Character(char) | 只有一種形式的構造方法 直接設置value的值 ![]() |
常用方法
比較
| compare(char, char) | ![]() 看起來可能會有人覺得奇怪,怎么還能直接減法? 其實char不就是一個UTF-16的代碼單元么,他不就是一個十六進制數么 如下圖所示,0x0058 - 0x002B 得到的值的十進制就是45 ![]() 比較的也就是前后順序了 |
| compareTo(Character) | 實例方法 借助於靜態方法 ![]() |
valueOf
系列 包裝 基本類型 為 對象
XXXValue
獲取 基本類型值
toString
借助於String.valueOf方法包裝轉換
equals
重寫了equals方法,比較的是實際的對象中的value值
hashcode
直接返回value的int值
reverseBytes
public static char reverseBytes(char ch)
character也有翻轉字節轉換的方法
再剩下就是Unicode更加強相關的方法了,簡單介紹下
getType
獲取字符在Unicode中的屬性類別信息
再次強調,一個char只是一個代碼單元,本質是16位 所以參數類型為char時,顯然不能支持輔助平面內的字符
顯然,int的范圍完全足夠,可以支持BMP和輔助平面
獲取到的屬性信息在不少針對於Unicode的處理中,將會很有用
比如
toChars
將指定的代碼點,保存到char數組
一個是保存到指定數組,一個是創建一個新的數組
| public static int toChars(int codePoint, char[] dst, int dstIndex) |
保存到指定的數組的指定位置 如果0號平面內 dst[dstIndex] 中存儲相同的值,並返回 1 如果輔助平面 dst[dstIndex]高代理 dst[dstIndex+1]低代理返回2 |
| public static char[] toChars(int codePoint) |
返回一個char數組保存指定代碼點 |
codePointCount
返回代碼點的數量,兩種形式
codePointCount(CharSequence, int, int)
codePointCount(char[], int, int)
時刻記住,char是代碼單元,一個代碼點可能一個或者兩個代碼單元
offsetByCodePoints
計算偏移指定個個數的代碼點后的索引
還是那句話,char是代碼單元,一個代碼點可能一個或者兩個代碼單元
如果一個代碼點一個代碼單元,偏移多少個,那么下標索引就是往后移動多少個了
可惜,一個代碼點可能一個也可能兩個代碼單元
所以才有了這個方法
以第一個方法為例
就是說,給你一個數組, 然后又給了你start 和count,在這個子數組的范圍內
從index這個下標開始(顯然index應該位於start和start+count之間的,否則越界異常)
往后數codePointOffset 個代碼點,然后看看到底下標序號是多少,返回給調用者
返回的值可能就是index+codePointOffset 也可能大於index+codePointOffset
|
public static int offsetByCodePoints(char[] a,
int start,
int count,
int index,
int codePointOffset)
|
|
public static int offsetByCodePoints(CharSequence seq,
int index,
int codePointOffset)
|
codePointAt
獲取指定位置的代碼點
基本邏輯,如果第一個代碼單元的值在高代理區,下一個索引還小於數組的長度,並且下一個索引的地方是低代理區,那么返回這個代碼點
否則,就是單純的返回這個代碼單元
說白了就是我能力范圍內就給你,否則就給你我有的那一半 第三個limit是范圍,其他的范圍默認就是數組內
|
public static int codePointAt(char[] a, int index)
|
|
public static int codePointAt(CharSequence seq, int index)
|
|
public static int codePointAt(char[] a, int index, int limit )
|
看下下面這個例子,0x1f310上面也看到了,使用兩個代碼單元
chars保存了他的兩個代碼單元
chars1 只有一個元素,那就是0x1f310 的高代理位
codePointBefore
codePointBefore 和 codePointAt幾乎是一回事情,只不過是指定位置的前面
如果指定位置前面一個 index-1 處是一個低代理,而且更前面的一個index-2 是一個數組內的有效數據
那么,就返回代碼點
否則就返回一個單元
start相當於限定了數組的范圍,本來index-2>=0 就好了 現在index-2需要 >=start了
|
public static int codePointBefore(CharSequence seq,int index)
|
|
public static int codePointBefore(char[] a, int index)
|
|
public static int codePointBefore(char[] a, int index, int start )
|
charCount
public static int charCount(int codePoint)
確定表示指定字符(Unicode 代碼點)所需的 char 值的數量。如果指定字符等於或大於 0x10000,則該方法返回的值為 2。否則,該方法返回的值為 1
從代碼可以看得出來,他就是直接查看代碼點的值簡單的計算
並沒有確認是否是有效字符
toLowerCase / toUpperCase /toTitleCase
都是使用取自 UnicodeData 文件的大小寫映射信息將字符(Unicode 代碼點)進行參數轉換
lowCase就是轉換為小寫
UpperCase就是轉換為大寫
TitleCase就是首字母大寫
又是兩個版本的參數,一個char 一個int 還是老樣子,char不支持輔助平面
|
toLowerCase(char)
toLowerCase(int)
|
|
toUpperCase(char)
toUpperCase(int)
|
|
toTitleCase(char)
toTitleCase(int)
|
代碼點獲取
| public static char highSurrogate(int codePoint) | 返回代碼點的高代理 如果不是輔助平面的字符,返回未知char |
| public static char lowSurrogate(int codePoint) | 返回代碼點的低代理 如果不是輔助平面的字符,返回未知char |
代理位信息判斷
| public static boolean isSurrogate(char ch) | 是否代理部分 |
| public static boolean isSurrogatePair(char high, char low) | 是否是代理對 |
| public static boolean isHighSurrogate(char ch) | 是否是高代理 |
| public static boolean isLowSurrogate(char ch) | 是否是低代理 |
代碼點信息的校驗
| public static boolean isValidCodePoint(int codePoint) | 是否是合法的代碼點 確定指定的代碼點是否為從 0x0000 到 0x10FFFF 范圍之內的有效 Unicode 代碼點值 |
| public static boolean isBmpCodePoint(int codePoint) | 是否位於0號平面,是的話就可以使用一個char表示了 |
| public static boolean isSupplementaryCodePoint(int codePoint) | 是否位於輔助平面 輔助平面必然需要使用兩個char |
toCodePoint(char, char)
|
public static int toCodePoint(char high,char low)
將指定的代理項對轉換為其增補代碼點值。該方法沒有驗證指定的代理項對
如有必要,調用者必須使用 isSurrogatePair 驗證它
就是高代理 低代理的合並
|
isXXX 系列
isXXX系列就是校驗XXX的信息是否為真
大多兩個版本,一個是char 一個是int
還是老樣子,char僅僅支持0號平面
|
小寫?
isLowerCase(char)
isLowerCase(int)
|
大寫?
isUpperCase(char)
isUpperCase(int)
|
首字母大寫?
isTitleCase(char)
isTitleCase(int)
|
|
數字?
isDigit(char)
isDigit(int)
|
被定義為 Unicode 中的字符?
isDefined(char)
isDefined(int)
|
字母?
isLetter(char)
isLetter(int)
|
|
字母或數字?
isLetterOrDigit(char)
isLetterOrDigit(int)
|
是否能夠作為 Java 標識符中的首字符?
isJavaIdentifierStart(char)
isJavaIdentifierStart(int)
|
是否能夠作為 Java 標識符中的首字符以外的字符?
isJavaIdentifierPart(char)
isJavaIdentifierPart(int)
|
|
是否允許作為 Unicode 標識符中的首字符?
isUnicodeIdentifierStart(char)
isUnicodeIdentifierStart(int)
|
是否允許作為 Unicode 標識符中的首字符以外的字符?
isUnicodeIdentifierPart(char)
isUnicodeIdentifierPart(int)
|
是否是 Java 標識符或 Unicode 標識符中可忽略的一個字符?
isIdentifierIgnorable(char)
isIdentifierIgnorable(int)
|
|
空白字符?
isSpaceChar(char)
isSpaceChar(int)
|
Java 標准是否為空白字符?
isWhitespace(char)
isWhitespace(int)
|
ISO 控制字符?
isISOControl(char)
isISOControl(int)
|
| 字母? isAlphabetic(int) |
中日越韓文字? isIdeographic(int) |
依據 Unicode 規范是否對稱?
isMirrored(char)
isMirrored(int)
|
isAlphabetic 和 isLetter 類似, 但是類型范圍有差別
isAlphabetic范圍更大
|
UPPERCASE_LETTER LOWERCASE_LETTER TITLECASE_LETTER MODIFIER_LETTER OTHER_LETTER LETTER_NUMBER
|
|
UPPERCASE_LETTER LOWERCASE_LETTER TITLECASE_LETTER MODIFIER_LETTER OTHER_LETTER
|
| 返回使用指定基數的 字符 ch/Unicode 代碼點 的數值 |
public static int digit(char ch, int radix)
public static int digit(int codePoint, int radix)
|
| 確定使用指定基數的特定數字的字符表示形式 | public static char forDigit(int digit, int radix) |
| 返回給定字符的 Unicode 方向屬性 |
public static byte getDirectionality(char ch)
public static byte getDirectionality(int codePoint)
|
| 返回指定的 Unicode 字符/Unicode 代碼點 表示的int值 |
public static int getNumericValue(char ch)
public static int getNumericValue(int codePoint)
|
| 返回指定字符codePoint的Unicode名稱,如果代碼點未被分配,則返回null | public static String getName(int codePoint) |
感受下getName
總結
Java中的char 也就是Character中包裝的數據
他們是UTF-16中的代碼單元
一個Unicode字符集的數值叫做一個代碼點
所以一個字符可能是一個char也可能是兩個char
所以,除非真的有必要,希望對UTF16的代碼單元進行處理,不要使用char類型,使用其他的高級類型比如String
char就是Unicode UTF-16在程序中的應用
想要理解char就必須理解Unicode和UTF16
Character中的定義的變量以及方法,大多都是和他們息息相關的




















