3.7.3 位运算符

3.7.3 位运算符

Java支持的位运算符有如下7个

运算符 描述
& 按位与。当两位同时为1时才返回1。
` `
~ 按位非,或者叫按位取反。单目运算符,将操作数的每个位(包括符号位)全部取反
^ 按位异或。当两位相同时返回0,不同时返回1。
<< 左移运算符
>> 右移运算符
>>> 无符号右移运算符

一般来说,位运算符只能操作整数类型的变量或值

按位与 按位或 按位异或 的运算法则如表3.3所示。

第一个操作数 第二个操作数 按位与 按位或 按位异或
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

按位非只需要一个操作数,这个运算符将把操作数在计算机底层的二进制码按位(包括符号位)取反。

程序示例 安慰与 按位或

如下代码测试了按位与按位或运算的运行结果。

1
2
System.out.println(5 & 9); // 将输出1
System.out.println(5 | 9); // 将输出13

5的二进制码是000_0101(省略了前面的24个0),而9的二进制码是0000_1001(省略了前面的24个0)。运算过程如图3.12所示
这里有一张图片

程序示例 按位异或 按位取反

下面是按位异或和按位取反的执行代码

1
2
System.out.println(~-5); // 将输出4
System.out.println(5 ^ 9); // 将输出12

下面通过图3.13来介绍运算原理:
这里有一张图片

而5^9的运算过程如图3.14所示:
这里有一张图片

程序示例 左移运算

左移运算符是**将操作数的二进制码整体左移指定位数,左移后右边空出来的位以0填充**。例如如下代码

1
2
System.out.println(5 << 2); // 输出20
System.out.println(-5 << 2); // 输出-20

下面以-5为例来介绍左移运算的运算过程,如图3.15所示。
这里有一张图片

在图3.15中,上面的32位数是5的补码,左移两位后得到一个二进制补码,这个二进制补码的最高位是1,表明是一个负数,换算成十进制数就是20。

右移运算符 无符号右移运算符

Java的右移运算符有两个:>>和>>>。

右移运算符 以符号位填充

对于右移运算符>>而言,把第一个操作数的二进制码右移指定位数后,左边空出来的位以原来的符号位填充,即
如果第一个操作数原来是正数,则左边补0;
如果第个操作数是负数,则左边补1。

无符号右移运算符 以0填充

>>>是无符号右移运算符,它把第一个操作数的二进制码右移指定位数后,左边空出来的位总是以0填充

1
2
System.out.println(-5 >> 2); // 输出-2
System.out.println(-5 >>> 2); // 输出1073741822

下面用示意图来说明>>>>>运算符的运算过程

这里有一张图片

右移不改变正负符号

从图3.16来看,-5右移2位后左边空出2位,空出来的2位以符号位补充。从图中可以看出,右移运算后得到的结果的正负与第一个操作数的正负相同。右移后的结果依然是一个负数,这是一个二进制补码,换算成十进制数就是-2。
这里有一张图片

无符号右移总是得到正数

从图3.17来看,-5无符号右移2位后左边空出2位,空出来的2位以0补充。从图中可以看出,无符号右移运算后的结果总是得到一个正数。图3.17中下面的正数是1073741822(2^30-2)。
进行移位运算时还要遵循如下规则。

  • 对于低于int类型(如byteshortchar)的操作数总是先自动类型转换为int类型后再移位
  • 对于int类型的整数移位a>>b,因为int类型是32位,所以当b>32时,所以系统先用b对32求余,得到的结果才是真正移位的位数。例如,a>>33a>>1的结果完全一样,而a>>32的结果和a本身相同。
  • 对于long类型的整数移位a>>b,因为long类型是64位,所以当b>64时,总是先用b对64求余,得到的结果才是真正移位的位数。

左移相当于乘以2的n次方 右移相当于除以2的n次方

当进行移位运算时,只要被移位的二进制码没有发生有效位的数字丢失,不难发现左移n位就相当于乘以2的n次方,右移n位则是除以2的n次方。不仅如此,进行移位运算不会改变操作数本身,只是得到了一个新的运算结果,而原来的操作数本身是不会改变的