先了解什么是二进制原码:
二进制中一个字节(byte)是存储信息的最小单位,大小为8bits,即8个位,每个0或1就是一个位(bit),规定数据的最高位是符号位。符号位是1表示负数,是0表示正数。正数存储二进制原码,负数存储的是二进制的补码,补码是负数的绝对值反码加1。
8bits = 1byte
byte (1个字节)
char (2个字节)
short (2个字节)
int (4个字节)
long (8个字节)
float (4个字节)
double (8个字节)
看代码:
public static String bytesHexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String str = Integer.toHexString(b[i] & 0xFF);
if (str.length() == 1) {
str = '0' + str;
}
ret += str.toUpperCase();
}
return ret;
}
函数将byte[] b转化成十六进制字符串的过程中,for循环中的byte和0xFF进行与运算后使用Integer.toHexString转化出十六进制字符串。b[i] & 0xFF运算后得出的仍然是int,为什么要进行这一步?直接用Integer.toHexString(b[i])将byte强转为int行不行?
看例子:
0000 0001代表的是数字1
1000 0000代表的是-1
所以一个byte
正数最大是0111 1111,也就是数字127
负数最大是1111 1111,也就是数字-128
什么是反码:
反码就是把它的原码除符号位都取反(0变1,1变0)
一个数如果是正数,则它的反码与原码相同;
一个数如果是负数,则符号位为1,其余各位是对原码取反
什么是补码:
补码是在反码的末位上加1,正数的原反补码是相同的
在计算机中,如果我们用1个字节表示一个数,一个字节有8位,超过8位就进1,
在内存中情况为(100000000),进位1被丢弃。
- 一个数为正,则它的原码、反码、补码相同
- 一个数为负,符号位为1,其余各位是对原码取反,然后整个数加1
例如:
0的原码为 00000000
0的反码为 11111111(正零和负零的反码相同)
0的补码为 100000000(舍掉开头的1,正零和负零的补码相同)
1的原码为 10000001
1的反码为 11111110
1的补码为 11111111
那么-1的三码如下:
首先正1的原码:
0000 0001
然后按位取反(每个位上的0变成1,1变成0):
1111 1110
最后加1:
1111 1111
java采用的是补码的形式,利用溢出将减法变成加法对于十进制数,从5得到1
用减法:
5-4=1
因为4+6满10,我们可以将6作为4的补数
用加法:
5+6=11(去掉高位1,也就是减10)得到1.
对于十六进制数,从f到5
用减法:
f-a=5
因为a+6满16,将6作为a的补数
用加法:
f+6=15(去掉高位1,也就是减16)得到5.
补充了一些知识点,回到最初的疑问:
当系统检测到byte可能会转化成int或者说byte与int类型进行运算的时候,会将byte的内存空间高位补1(也就是按符号位补位)扩充到32位,再参与运算,其二进制补码其实已经改变了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。
最后一个梨:
-12的二进制表示:
1111 0100
高位补齐1后变成(按符号位补位)
1111 1111 1111 1111 1111 1111 1111 0100
十六进制0xFF的二进制表示是:
1111 1111。
高24位补0(按符号位补位)
0000 0000 0000 0000 0000 0000 1111 1111
-12补码与0xFF进行与&操作,就是
1111 1111 1111 1111 1111 1111 1111 0100 &
0000 0000 0000 0000 0000 0000 1111 1111
---------------------------------------
0000 0000 0000 0000 0000 0000 1111 0100
最后:byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性。