世界上只有10种人,一种是认识二进制的,一种是不认识的。认识程序要从二进制开始,了解Java中的二进制计算规则也必不可少。
位
自打计算机问世开始,高低电平就一直伴随至今,低电平表示0,高电平表示1,这便构成了计算机中数据存储的最小单位:位。位,记为b,也叫比特(bit),每个0或者1就是一个位。
数
自然生活中接触最多的是十进制数,但是计算机只认识0和1,只能用二进制来表示一个数,所以计算机在计算和存储时,会将所有的数据都转换成二进制,也叫做机器数。为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
计算机中的数有三种表示方法:原码、反码和补码,以下都以一个字节(byte)的有符号数为例。
原码
原码是十进制数到二进制的直接转换,是人最容易接受和理解的一种表示法。最高位是符号位,表示正负,其余位是二进制表示的数,因为第一位表示符号位,所以byte的取值范围是[-127,127]。
反码
反码是在原码的基础上的一种变化,反码的应用比较少:
- 对于正数,反码和原码一致;
- 对于负数,在原码的基础上,保持符号位不变,其余位取反。
补码
补码是在补码上的有一种改进,计算机中的数值都是以补码的形式来计算和存储的:
- 对于正数,补码和原码一致;
- 对于负数,在原码的基础上,保持符号位不变,其余位取反,然后+1,即反码+1,规定-128的补码是[10000000]。
示例
| 真值 | +5 | -5 |
|---|---|---|
| 原码 | 00000101 | 10000101 |
| 反码 | 00000101 | 11111010 |
| 补码 | 00000101 | 11111011 |
计算
原码,这是最符合直觉的:
如果用原码表示,让符号位也参与计算,显然对于减法来说, 结果是不正确的,这也是为何计算机内部不使用原码表示一个数的原因。
反码:
用反码进行计算,虽然真值部分计算正确,但是存在+0和-0两个0的表示总是有点让人困惑的。
补码:
正负数的加减法可以用补码正确计算,且使用[10000000]来表示-128,也解决了0的表示问题,所以最后计算机采用补码来对数值进行计算和存储。
同余:补码的另一种理解方式,8位只能表示256个数,如果想用其中的一部分表示负数怎么办,可以使用与该负数同余的正数来表示,比如,-1=255,-4=252,转换成二进制就是负数的补码表示形式。
位运算
计算机对二进制是友好的,二进制运算有时候可以极大的提升运算的速度。Java中数值都是以补码的形式参与位运算的,并且除~外,只能用于整型。当用于byte、short和char时,会发生类型提升,会先转换成int再进行运算。
按位与
按位与&是二目运算符,将两个数对应的二进制位进行与操作,同1为1,其余为0。
| 1 | 0 | |
|---|---|---|
| 1 | 1 | 0 |
| 0 | 0 | 0 |
按位或
按位或|是二目运算符,将两个数对应的二进制位进行或操作,同0为0,其余为1。
| 1 | 0 | |
|---|---|---|
| 1 | 1 | 1 |
| 0 | 1 | 0 |
按位异或
按位异或^是二目运算符,将两个数对应的二进制位进行异或操作,不同为1,相同为0。
| 1 | 0 | |
|---|---|---|
| 1 | 0 | 1 |
| 0 | 1 | 0 |
按位非
按位非~是一目运算符,将数值的二进制所有位进行取反操作,1变为0,0变为1。
| 取反前 | 取反后 |
|---|---|
| 1 | 0 |
| 0 | 1 |
左移
左移<<是二目运算符,符号位不变,将数值的其他二进制位整体向左移动指定的位数,空白位填充0。
右移
右移>>是二目运算符,符号位不变,将数值的其他二进制位整体向右移动指定的位数,空白位填充0。
无符号右移
无符号右移>>>是二目运算符,将数值的所有二进制位(包括符号位)整体向右移动指定的位数,空白位填充0。
代码示例
1 | public class Test { |