在講解之前,先了解機器數、真值、原碼、反碼、補碼的概念
1、機器數
一個數在計算機中的二進制表示形式, 叫做這個數的機器數。機器數是帶符號的,在計算機用一個數的最高位存放符號, 正數為0, 負數為1。
比如,十進制中的數 +3 ,計算機字長為8位,轉換成二進制就是00000011。如果是 -3 ,就是 10000011 。
那么,這里的 00000011 和 10000011 就是機器數。
2、真值
因為第一位是符號位,所以機器數的形式值就不等於真正的數值。例如上面的有符號數 10000011,其最高位1代表負,其真正數值是 -3 而不是形式值131(10000011轉換成十進制等於131)。所以,為區別起見,將帶符號位的機器數對應的真正數值稱為機器數的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
3、原碼
原碼就是符號位加上真值的絕對值, 即用第一位表示符號, 其余位表示值. 比如如果是8位二進制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符號位. 因為第一位是符號位, 所以8位二進制數的取值范圍就是:[1111 1111 , 0111 1111],即[-127 , 127]
原碼是人腦最容易理解和計算的表示方式.
4、反碼
反碼的表示方法是:正數的反碼是其本身,負數的反碼是在其原碼的基礎上, 符號位不變,其余各個位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
可見如果一個反碼表示的是負數, 人腦無法直觀的看出來它的數值. 通常要將其轉換成原碼再計算.
5、補碼
補碼的表示方法是:正數的補碼就是其本身。負數的補碼是在其原碼的基礎上, 符號位不變, 其余各位取反, 最后+1. (即在反碼的基礎上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]補
[-1] = [10000001]原 = [11111110]反 = [11111111]補
對於負數, 補碼表示方式也是人腦無法直觀看出其數值的. 通常也需要轉換成原碼在計算其數值.
6、中文轉byte[]時變成了負數?
原因:漢字以補碼存儲
代碼演示:
public class FOSWrite1 { public static void main(String[] args) throws IOException { // 使用文件名稱創建流對象 FileOutputStream fos = new FileOutputStream("fos.txt"); // 字符串轉換為字節數組 byte[] b = "黑".getBytes(); for (int i = 0; i < b.length; i++) { System.out.println(b[i]); } // 寫出字節數組數據 fos.write(b); // 關閉資源 fos.close(); } }
結果如下:
-23 -69 -111
為什么中文的中
轉換成byte
類型后 就變成了[-23, -69, -111]?
原理如下
在utf-8編碼中, 一個中文字符占用3個字節.
在GBK編碼中, 一個中文字符占用2個字節.
我們在這個網站查詢“黑”字的utf-8
編碼16進制是 E9BB91 對應的二進制是11101001 10111011 10010001
二進制是帶符號位的, 我們需要給它們轉換成十進制, 步驟如下:
第一步: 由補碼得到反碼 (也就是二進制值減1)
注意:最高位由於都是1,故都是負數
進制 | 字節1 | 字節2 | 字節3 |
---|---|---|---|
十六進制 | E9 | BB | 91 |
補碼(二進制,負數) | 1110 1001 | 1011 1011 | 1001 0001 |
(由補碼得到)反碼 | 1110 1000 | 1011 1010 | 1001 0000 |
E9轉換成二進制
第二步: 由反碼得到原碼(1變0, 0變1)
進制 | 字節1 | 字節2 | 字節3 |
---|---|---|---|
十六進制 | E9 | BB | 91 |
補碼(二進制,負數) | 1110 1001 | 1011 1011 | 1001 0001 |
(由補碼得到)反碼 | 1110 1000 | 1011 1010 | 1001 0000 |
(將反碼變為)原碼 | 1001 0111 | 1100 0101 | 1110 1111 |
第三步: 將原碼轉換真值
進制 | 字節1 | 字節2 | 字節3 |
---|---|---|---|
十六進制 | E9 | BB | 91 |
補碼(二進制,負數) | 1110 1001 | 1011 1011 | 1001 0001 |
(由補碼得到)反碼 | 1110 1000 | 1011 1010 | 1001 0000 |
(將反碼變為)原碼 | 1001 0111 | 1100 0101 | 1110 1111 |
真值 | -23 | -69 | -111 |
總結:(1)、漢字以補碼存儲。(2)、一個漢字轉成字節數組會變成三個負數,每個負數表示一個字節,即一個漢字用三個字節表示。