char 在 Java 中是個(gè)很底層的東西了,比如 String 郑现、StringBuilder 的底層就是它,但是在我們平時(shí)的工作中呢荧降,很少使用他接箫,不過(guò)既然是底層,今天我們大家就來(lái)一起研究下這 “哥們”朵诫。
char 用來(lái)表示 ==1 個(gè)字符== 辛友。也就是說(shuō),如果你寫 2個(gè)字符剪返,那么這里是會(huì)報(bào)錯(cuò)的瞎领。如下:
// 這種編譯報(bào)錯(cuò) Too many characters in character literal
char c = '中國(guó)';
當(dāng)然 char 也可以用來(lái)表示中文字符。在賦值時(shí)随夸,我們經(jīng)常這樣表示:
char c = '中';
char c1 = 'H';
但是這里就會(huì)遇到一些面試上的坑了九默,比如使用字符類型進(jìn)行算術(shù)運(yùn)算和比較運(yùn)算? 這是個(gè)什么鬼 A + B == 宾毒?驼修??
其實(shí)在 Java 內(nèi)部進(jìn)行字符處理時(shí)诈铛,采用的是 Unicode乙各,(這里插一句 對(duì) Unicode 和 UTF-X 的理解,Unicode 是一個(gè)包含世界各國(guó)字母的字符列表的編碼幢竹。 Universal Multiple-Octet Coded Character Set”耳峦,簡(jiǎn)稱 UCS, 俗稱 “unicode “,就是每一個(gè)字母對(duì)應(yīng)一個(gè)編碼 ID焕毫,是一種映射關(guān)系蹲坷,我們可以理解為 Unicode 是 一個(gè)標(biāo)準(zhǔn)驶乾,一個(gè)規(guī)則,而 UTF-X 則是一種具體的對(duì) Unicode 的實(shí)現(xiàn)循签,UTF-X 是一種 針對(duì) Unicode 的可變長(zhǎng)度字符編碼级乐,也是一種前綴碼,是一種編碼格式县匠。它可以用來(lái)表 示Unicode標(biāo)準(zhǔn)中的任何字符风科,而且 UTF-8 是兼容 ASCII 的。UTF-8 是 Unicode 的 實(shí)現(xiàn)方式之一乞旦。)
char 本質(zhì)上是一個(gè)固定占用 2 個(gè)字節(jié)的無(wú)符號(hào)正整數(shù)贼穆,對(duì)應(yīng) Unicode, 也就是說(shuō) 上面 的 李兰粉, H 都分別對(duì)應(yīng)一個(gè) 正整數(shù)扮惦,char 只能表示 Unicode 編號(hào)在 65 536 以內(nèi)的字符。因?yàn)橐粋€(gè)字節(jié) 只能表示 256 個(gè)符號(hào)亲桦,2 個(gè)字節(jié)就是 256 x 256 = 65536 個(gè)符號(hào)崖蜜。那么如果超出范圍該咋表示呢,用 2 個(gè)char?颓汀Tチ臁!
既然了解完了 Unicode舔琅,那么每個(gè)字符都可以用一個(gè) 對(duì)應(yīng)的 編碼ID 表示等恐,也就是 一個(gè)正整數(shù)。既然是數(shù)字了备蚓,自然可以進(jìn)行算術(shù)運(yùn)算和比較運(yùn)算课蔬。
char 的 二進(jìn)制轉(zhuǎn)換
下面我們就以 Integer 的轉(zhuǎn)換二進(jìn)制函數(shù) toBinaryString 為例,說(shuō)明一下 char 字節(jié)在 Java 中如何轉(zhuǎn)換二進(jìn)制的郊尝。
public static void main(String[] args) {
char c = '中';
System.out.println(Integer.toBinaryString(c));
// 二進(jìn)制為: 100111000101101
}
我們給 char 賦值一個(gè) 中文 字節(jié) 中二跋,然后求出它的 二進(jìn)制。
Integer 的 toBinaryString 方法:
/**
* 返回 輸入?yún)?shù) i 的 二進(jìn)制字符串
*/
public static String toBinaryString(int i) {
return toUnsignedString0(i, 1);
}
這里調(diào)用了 toUnsignedString0流昏, 如下
/**
* Convert the integer to an unsigned number.
* 轉(zhuǎn)換一個(gè) 整型到一個(gè)無(wú)符號(hào)二進(jìn)制數(shù)字
*/
private static String toUnsignedString0(int val, int shift) {
// 這里斷言忽略
// assert shift > 0 && shift <=5 : "Illegal shift value";
// Integer.numberOfLeadingZeros 返回?zé)o符號(hào)整型的最高非零位前面的0的個(gè)數(shù)扎即,包括符號(hào)位在內(nèi)
// 比如 Integer.numberOfLeadingZeros(10) 結(jié)果是 28
// Integer.SIZE 為 32
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
// 這里是整個(gè)方法的核心,整數(shù)轉(zhuǎn)換二進(jìn)制况凉,然后更新 char 的緩沖
formatUnsignedInt(val, shift, buf, 0, chars);
// Use special constructor which takes over "buf".
return new String(buf, true);
}
核心轉(zhuǎn)換函數(shù) formatUnsignedInt 如下:
/**
* 格式化到字符緩沖區(qū)
* @param val 被格式化的數(shù)
* @param shift 格式化的類型 (4 代表16進(jìn)制, 3 代表8進(jìn)制, 1 代表二進(jìn)制)
* @param buf 待寫入的字符緩沖區(qū)
* @param offset 字符開(kāi)始的位置
* @param len 要寫的字符數(shù)
* @return the lowest character location used
*/
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
int radix = 1 << shift;
int mask = radix - 1;
do {
// 二進(jìn)制的 & 運(yùn)算谚鄙,求出下標(biāo),然后獲取 digits 中對(duì)應(yīng)的值刁绒,寫入 buf 緩沖區(qū)
// final static char[] digits = {
// '0' , '1' , '2' , '3' , '4' , '5' ,
// '6' , '7' , '8' , '9' , 'a' , 'b' ,
// 'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
// 'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
// 'o' , 'p' , 'q' , 'r' , 's' , 't' ,
// 'u' , 'v' , 'w' , 'x' , 'y' , 'z'
}
buf[offset + --charPos] = Integer.digits[val & mask];
// 無(wú)符號(hào)右移
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
通過(guò)上面的代碼可以看出闷营,Integer 內(nèi)部維護(hù)了一個(gè) char 數(shù)組,我們的普通字符轉(zhuǎn)換 二進(jìn)制的時(shí)候知市,都是求出 digits 某個(gè)下標(biāo)的值傻盟,然后寫到緩沖區(qū)速蕊,以 String 的形式返回給用戶的。