計算機中用補碼表示負數,并且有一定的計算方式;另外,用二進(jìn)制的最高位表示符號,0表示正數、1表示負數。這種說(shuō)法本身沒(méi)錯,可是要有一定的解釋?zhuān)蝗凰褪清e的,至少不能解釋?zhuān)瑸槭裁醋址?lèi)型的-1二進(jìn)制表示是“1111 1111”16進(jìn)制表示為FF,而不是1000 0001。
在計算機中,可以區分正負的類(lèi)型,稱(chēng)為有符號類(lèi)型,無(wú)正負的類(lèi)型,稱(chēng)為無(wú)符號類(lèi)型。
使用二進(jìn)制中的最高位表示正負
一個(gè)字節為8位,按0開(kāi)始記,那它的最高位就是第7位,2個(gè)字節,最高位就是15位,4個(gè)字節,最高位是31位,不同長(cháng)度的類(lèi)型,最高為業(yè)不同,但總是最左邊那位。

無(wú)符號和有符號數的范圍的區別
無(wú)符號數中,所有的位都用于直接表示該值的大??;有符號數中最高位用于表示正負,所以,正值時(shí),該數的最大值就會(huì )變?。?/span>
無(wú)符號數:1111 1111 值:255=1*2^7+1*2^6+.....=2^n-1
有符號數:0111 1111 值:127
同樣一個(gè)字節,無(wú)符號的最大值是255,有符號的最大值是127,下圖是無(wú)符號數和有符號數的范圍:

一個(gè)由符號的數據類(lèi)型的最大值的計算方法完全和無(wú)符號一樣,只不過(guò)它少了一個(gè)最高位,但是在負數范圍內,數值的計算方法不能直接使用前面的公式,在計算機種,負數除了最高位為
1以外,還采用補碼的形式,所以在計算前,需要對補碼進(jìn)行還原。以10進(jìn)制的計算經(jīng)驗,1表示正1,-1表示和1相對的負值。那么很容易想到在二進(jìn)制中,0000 0001表示正1,則高位為1后:1000 0001應該表示-1,不過(guò)實(shí)際上,計算機中的規定有些相反: 可以發(fā)現,
Java基本數據類(lèi)型 
Java符號類(lèi)型

Java的原始類(lèi)型里沒(méi)有無(wú)符號類(lèi)型,如果需要某個(gè)寬度的無(wú)符號類(lèi)型,可以用>>>,這個(gè)是java的無(wú)符號右移操作符,或者使用下一個(gè)寬度的帶符號類(lèi)型來(lái)模擬,例如,需無(wú)符號的short,就用int來(lái)模擬:
1 int toUnsigned(short s) {
2 return s & 0x0FFFF;
3 }
十進(jìn)制的字面常理只有一個(gè)特性,就是所有的十進(jìn)制字面常量都是正數,如果想寫(xiě)一個(gè)負的十進(jìn)制,則需要在正的十進(jìn)制字面常量前面加上“-”就好了。
十六進(jìn)制或者八進(jìn)制的字面常量就不一定是正數或者負數,如果最高位是1,那么就是負數:
1 System.out.println(0x80);//128
2 //0x81看作是int型,最高位(第32位)為0,所以是正數
3 System.out.println(0x81);//129
4 System.out.println(0x8001);//32769
5 System.out.println(0x70000001);//1879048193
6 //字面量0x80000001為int型,最高位(第32位)為1,所以是負數
7 System.out.println(0x80000001);//-2147483647
8 //字面量0x80000001L強制轉為long型,最高位(第64位)為0,所以是正數
9 System.out.println(0x80000001L);//2147483649
10 //最小int型
11 System.out.println(0x80000000);//-2147483648
12 //只要超過(guò)32位,就需要在字面常量后加L強轉long,否則編譯時(shí)出錯
13 System.out.println(0x8000000000000000L);//-922337203685477
System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));// cafebabe
結果為什么不是0x1cafebabe?該程序執行的加法是一個(gè)混合類(lèi)型的計算:左操作數是long型,而右操作數是int類(lèi)型。為了執行該計算,Java將int類(lèi)型的數值用拓寬原生類(lèi)型轉換提升為long類(lèi)型,然后對兩個(gè)long類(lèi)型數值相加。因為int是有符號的整數類(lèi)型,所以這個(gè)轉換執行的是符號擴展。
這個(gè)加法的右操作數0xcafebabe為32位,將被提升為long類(lèi)型的數值0xffffffffcafebabeL,之后這個(gè)數值加上了左操作0x100000000L。當視為int類(lèi)型時(shí),經(jīng)過(guò)符號擴展之后的右操作數的高32位是-1,而左操作數的第32位是1,兩個(gè)數
值相加得到了0:
0x ffffffffcafebabeL
+0x 0000000100000000L
-----------------------------
0x 00000000cafebabeL
如果要得到正確的結果0x1cafebabe,則需在第二個(gè)操作數組后加上“L”明確看作是正的long型即可,此時(shí)相加時(shí)拓
展符號位就為0:
Java代碼
1 System.out.println(Long.toHexString(0x100000000L + 0xcafebabeL));// 1cafebabe
System.out.println((int)(char)(byte)-1);// 65535
結果為什么是65535而不是-1?
窄的整型轉換成較寬的整型時(shí)符號擴展規則:如果最初的數值類(lèi)型是有符號的,那么就執行符號擴展(即如果符號位為1,則擴展為1,如果為零,則擴展為0);如果它是char,那么不管它將要被提升成什么類(lèi)型,都執行零擴展。
了解上面的規則后,我們再來(lái)看看迷題:因為byte是有符號的類(lèi)型,所以在將byte數值-1(二進(jìn)制為:11111111)提升到char時(shí),會(huì )發(fā)生符號位擴展,又符號位為1,所以就補8個(gè)1,最后為16個(gè)1;然后從char到int的提升時(shí),由于是char型提升到其他類(lèi)型,所以采用零擴展而不是符號擴展,結果int數值就成了65535。
如果將一個(gè)char數值c轉型為一個(gè)寬度更寬的類(lèi)型時(shí),只是以零來(lái)擴展,但如果清晰表達以零擴展的意圖,則可以考慮
使用一個(gè)位掩碼:
Java代碼
1 int i = c & 0xffff;//實(shí)質(zhì)上等同于:int i = c ;
說(shuō)明:
至于0xff,這屬于java的字面常量,他已經(jīng)是int了,ff表示為11111111,java對這種字面常量,不把他前面的1看做符號位,雖然也是有符號擴展,但是,擴展成的是00...ff.
“數字字面常量”的類(lèi)型都是int型,而不管他們是幾進(jìn)制,所以“2147483648”、“0x180000000(十六進(jìn)制,共33位,所以超過(guò)了整數的取值范圍)”字面常量是錯誤的,編譯時(shí)會(huì )報超過(guò)int的取值范圍了,所以要確定以long來(lái)表示“2147483648L”、“0x180000000L”。
System.out.println(0x80000001);//-2147483647 ,已經(jīng)是32位,最高位是符號位
System.out.println(0xcafebabe);//-889275714
System.out.println(0xffff); //65535 int是32位的,最高位已經(jīng)是0,相當于0X0000ffff
System.out.println(0xff); //255
如果將一個(gè)char數值c轉型為一個(gè)寬度更寬的整型,并且希望有符號擴展,那么就先將char轉型為一個(gè)short,它與char上個(gè)具有同樣的寬度,但是它是有符號的:
2 int i = (short)c;
如果將一個(gè)byte數值b轉型為一個(gè)char,并且不希望有符號擴展,那么必須使用一個(gè)位掩碼來(lái)限制它:
3 char c = (char)(b & 0xff);// char c = (char) b;為有符號擴展
((byte)0x90 == 0x90)?
答案是不等的,盡管外表看起來(lái)是成立的,但是它卻等于false。為了比較byte數值(byte)0x90和int數值0x90,Java
通過(guò)拓寬原生類(lèi)型將byte提升為int,然后比較這兩個(gè)int數值。因為byte是一個(gè)有符號類(lèi)型,所以這個(gè)轉換執行的是符號擴展,將負的byte數值提升為了在數字上相等的int值(10010000?111111111111111111111111 10010000)。在本例中,該轉換將(byte)0x90提升為int數值-112,它不等于int數值的0x90,即+144。
解決辦法:使用一個(gè)屏蔽碼來(lái)消除符號擴展的影響,從而將byte轉型為int。
Java代碼
1 ((byte)0x90 & 0xff)== 0x90
java中byte轉換int時(shí)為何與0xff進(jìn)行與運算
在剖析該問(wèn)題前請看如下代碼
public static String bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex.toUpperCase();
}
return ret;
}
上面是將byte[]轉化十六進(jìn)制的字符串,注意這里b[i] & 0xFF將一個(gè)byte和 0xFF進(jìn)行了與運算,然后使用Integer.toHexString取得了十六進(jìn)制字符串,可以看出
b[i] & 0xFF運算后得出的仍然是個(gè)int,那么為何要和 0xFF進(jìn)行與運算呢?直接 Integer.toHexString(b[i]);,將byte強轉為int不行嗎?答案是不行的.
其原因在于:
1.byte的大小為8bits而int的大小為32bits
2.java的二進(jìn)制采用的是補碼形式
在這里先溫習下計算機基礎理論
byte是一個(gè)字節保存的,有8個(gè)位,即8個(gè)0、1。
8位的第一個(gè)位是符號位,
也就是說(shuō)0000 0001代表的是數字1
1000 0000代表的就是-1
所以正數最大位0111 1111,也就是數字127
負數最大為1111 1111,也就是數字-128
上面說(shuō)的是二進(jìn)制原碼,但是在java中采用的是補碼的形式,下面介紹下什么是補碼
Integer.toHexString的參數是int,如果不進(jìn)行&0xff,那么當一個(gè)byte會(huì )轉換成int時(shí),由于int是32位,而byte只有8位這時(shí)會(huì )進(jìn)行補位,例如補碼11111111的十進(jìn)制數為-1轉換為int時(shí)變?yōu)?/span>11111111111111111111111111111111好多1啊,呵呵!即0xffffffff但是這個(gè)數是不對的,這種補位就會(huì )造成誤差。和0xff相與后,高24比特就會(huì )被清0了,結果就對了。
補碼是有符號數,所以從8位變?yōu)?/span>int需要有符號擴展,變?yōu)?/span>11111111111111111111111111111111(最終的值為-1)。
至于0xff,這屬于java的字面常量,他已經(jīng)是int了,ff表示為11111111,java對這種字面常量,不把他前面的1看做符號位,雖然也是有符號擴展,但是,擴展成的是00...ff.
一般在有些編譯器重,寫(xiě)ff,會(huì )把第一位1認為是符號位,所以可以這么寫(xiě):0x0ff
DataInputStream in = new DataInputStream(
new BufferedInputStream(fileInputStream));
聯(lián)系客服