本文最后更新于 235 天前 ,文中信息可能已经过时。如有问题请在评论区留言。

前言

在了解 Java 位运算前,需要先了解如下内容:

机器数、真值

计算机和真实生活中不同,一个数在计算机中只能以二进制 (0 或 1) 的方式表示,现实生活中主要以十进制表示,在二进制的表示中,最高位是符号位,最高为如果是 0 , 则表示该数的十进制表示为正数,如果最高位是 1, 则该数为负数

以十进制 3 为例,改为 8 位的二进制,则是 [0000 0011], 如果是 -3 转换为 8 位二进制,则是 [1000 0011]。在这里 [0000 0011] 与 3 表示的意思相同,不过 [0000 0011] 是机器数,3 为真值

原码、反码、补码

一个数,取绝对值大小转换为二进制,称其为原码。正常情况下,对计算机的两个数进行加法运算,由于不知道符号,所以对其取补码后进行运算,正数的补码是其本身,负数的补码是对其正数取反加 1。

原码

正数的原码是本身。负数的原码是其正数的原码最高位改为 1

示例:1, -1 转换为 8 位的二进制原码

1 的原码 = [0000 0001]
-1 的原码 = [1000 0001]

反码

正数的反码是本身。负数的反码是其最高位不变,其他位取反

示例:1, -1 转换为 8 位的二进制反码

1 的反码 = [0000 0001]
-1 的反码 = [1111 1110]

补码

正数的补码是本身。负数的补码是最高位不变,其他位取反,最后位 + 1 (即 反码 + 1)

示例:1, -1 转换为 8 位的二进制补码

1 的补码 = [0000 0001]
-1 的补码 = [1111 1111]

已知一个数的补码求其原码,就是对补码再求补码

在 Java 中所有数据都是以补码的形式存在的。(可通过以下代码验证)

举例来说 int 类型占 4 个字节,每个字节长度是 8 位,共 32 位。取整数 1 来说 二进制的标示为 [0000 0000 0000 0000 0000 0000 0000 0001]。

java
1
2
System.out.prinln(Integer.toBinaryString(-1);
// result: 11111111111111111111111111111111

Java 位运算

位逻辑运算符

位逻辑运算符对操作数的每一位进行逻辑运算,并将结果返回。

&(按位与运算符)

将操作数的每一位进行逻辑与运算,即只有当两个操作数的对应位都为 1 时,结果才为 1。

例: 1, 2 进行与运算:

计算过程:

text
1: [0000 0001]  
2: [0000 0010]  
&: [0000 0000] = 0
java
1
2
int a = 1, b = 2;
System.out.println(a & b); // 0

|(按位或运算符)

将操作数的每一位进行逻辑或运算,即只要有一个操作数的对应位为 1,结果就为 1。

例:1, 2 进行或运算

计算过程:

text
1: [0000 0001]  
2: [0000 0010]  
|: [0000 0011] = 3
java
1
2
int a = 1, b = 2;
System.out.println(a | b); // 3

~(按位非运算符)

将操作数的每一位取反,即 0 变成 1,1 变成 0。(此时得到的是补码,最终结果是补码的原码)

例: ~0 = -1

计算过程:

text
0: [0000 0000]  
~: [1111 1111] —(转换为原码)—> [1000 0001] = -1
java
1
2
int a = 0;
System.out.println(~ a); // -1

^(按位异或运算符)

将操作数的每一位进行逻辑异或运算,即只有当两个操作数的对应位不同时,结果才为 1。

例: 3, 5 进行异或运算

计算过程:

text
3: [0000 0011]  
5: [0000 0101]  
^: [0000 0110] = 6
java
1
2
int a = 3, b = 5;
System.out.println(a ^ b); // 6

移位运算符

移位运算符将操作数的每一位向左或向右移动指定的位数。(移除的低位被舍弃)

<<(左移运算符)

将操作数的每一位向左移动指定的位数,并在低位补零。

x << y,计算结果相当于:x 乘以 2 的 y 次幂 ( \$x * 2^y\$ )

示例 1

计算:5 << 2

text
5: [0000 0101] (有效值 101) << 2
= [0001 0100] = 20

相当于:\$5 * (2^2) = 5 * 4 = 20\$

示例 2

计算:2147483647 << 1

text
2147483647 的二进制:  
0111 1111 1111 1111 1111 1111 1111 1111

2147483647 << 1:  
1111 1111 1111 1111 1111 1111 1111 1110 (第一位符号位为 1, 负数,该值为补码)

依据补码求原码:(除符号位取反 + 1):  
(除符号位取反):  
1000 0000 0000 0000 0000 0000 0000 0001  
(+1):  
最后结果的二进制:  
1000 0000 0000 0000 0000 0000 0000 0010 = -2

>>(有符号右移运算符)

>> 将操作数的每一位向右移动指定的位数,并在高位根据操作数的符号位进行扩展。

x >> y,计算结果相当于: x 除以 2 的 y 次幂 ( \$x / 2 ^ y\$ )

示例:5 >> 2

计算过程:

text
5: [0000 0101] >> 2  
= [0000 001] = 1

相当于:\$5 / (2^2) = 5 / 4 = 1\$

>>>(无符号右移运算符)

将操作数的每一位向右移动指定的位数,并在高位都补零。(移除的低位被舍弃)

正数的 >>>>> 无区别,负数则会变成正数。