Java 中的三重移位运算符

2025年7月28日 | 阅读 8 分钟

在 Java 中,三元右移运算符(也称为位移运算符)由“>>>”表示,它是一种位操作运算符,可以将给定值的位向右移动指定的位数,用零填充最左边的位。与双右移运算符“>>”不同,“>>”会用原始值的符号位填充最左边的位,而三元右移运算符始终用零填充最左边的位。

简单来说,>>> 运算符将数字的位向右移动,就像 >> 一样,但它总是用 0 填充最左边的位,无论数字是正数还是负数。这使得它在某些底层按位操作中特别有用,在这些操作中,数字的符号不应影响结果。

请注意,“>>>”也被称为无符号右移运算符。Java 中不存在无符号左移运算符(“<<<”)。因为“<<<”的工作方式与左移运算符“<<”相同。

语法

三元右移运算符的语法如下:

其中,number 是要移位的数值,numBits 是要移位的位数,result 是移位操作的结果。

三元右移(>>>)运算符的工作原理

使用整数数据类型

考虑数字 -8。在二进制(32 位有符号整数)中,-8 的 2 的补码是:

现在,使用以下代码片段将移位 2 的 >>> 运算符应用于 -8:

二进制操作是:

原始 11111111 11111111 11111111 11111000

移位后 00111111 11111111 11111111 11111110

前导位(原来是 1,因为它是负数)现在被替换为 0。这会将原始的负数转换为一个大的正数。因此,最终输出是:1073741822

三元右移运算符与 long 数据类型

与 int 类似,>>> 运算符也可以用于 long 值。在这种情况下,Java 将数字视为 64 位有符号值并相应地进行移位。

在这里,-1L 在二进制中表示为全为 1,使用 >>> 右移它,会在左边填充 0,使其有效地变成最大的 long 值。

三元右移运算符的必要性

三元右移运算符在我们需要对无符号整数值执行逻辑右移的情况下特别有用。在 Java 中,整数值使用二的补码表示法,这意味着最左边的位用作符号位。这使得对无符号整数值执行逻辑右移变得困难,因为符号位会被移入最右边的位,从而有效地改变整数的值。

例如,考虑以下代码:

在这种情况下,x 最初设置为 -1,其二进制表示为 11111111 11111111 11111111 11111111。当我们使用双右移运算符按 1 位进行右移(“>>”)时,我们得到 11111111 11111111 11111111 11111111,仍然是 -1。因为符号位被移入最右边的位,从而保留了整数的负值。

但是,如果我们改用三元右移运算符(“>>>”),我们将得到不同的结果:

在这种情况下,移位操作后 x 的值为 01111111 11111111 11111111 11111111,即十进制的 2147483647。因为最左边的位被填充为 0 而不是 1,从而有效地将整数的值更改为正值。

示例

编译并运行

输出

x: -1
y: 2147483647

在此程序中,我们声明一个整数变量 x 并将其初始化为 -1,其二进制表示为 11111111 11111111 11111111 11111111。然后,我们使用三元右移运算符对 x 进行逻辑右移 1 位,并将结果存储在新整数变量 y 中。

最后,我们使用 System.out.println() 方法打印 x 和 y 的值。从输出可以看出,移位操作后 x 的值仍为 -1,而 y 的值现在为 2147483647。因为三元右移运算符用 0 填充了 x 的最左边位,从而有效地将其值更改为正整数。

总的来说,这个示例程序演示了如何在 Java 中使用三元右移运算符对无符号整数值执行逻辑右移并优化某些按位操作。

关于三元右移运算符的一个重要注意事项是,它在 Java 中的优先级低于算术、逻辑和按位运算符。这意味着它在这些运算符之后进行计算,如果需要,我们需要使用括号来控制计算顺序。

例如,考虑以下代码:

示例

编译并运行

输出

y: 32
z: 2

除以 2 的幂

除了对无符号整数值执行逻辑右移外,三元右移运算符还可以用于优化某些按位操作,例如将有符号整数除以 2 的幂。

Java 中的三元右移运算符是一个有用的按位操作运算符,它允许我们对无符号整数值执行逻辑右移并优化某些按位操作。但是,应谨慎使用它,因为不当使用也可能导致意外行为。

考虑以下程序:

示例

编译并运行

输出

The value of x is: 4
The value of y is: 2

因此,我们可以得出结论,如果我们对整数应用三元右移运算符,那么我们总是会得到一个正值。

对于 16 这样的正数,>> 和 >>> 产生相同的结果。

对于 -16 这样的负数,>>> 得到一个大的正数,因为它将该数字视为无符号二进制序列。

为什么三元右移运算符有用?

该运算符在以下情况下特别有用:

  1. 位掩码和二进制操作:当我们处理按位算法时,例如压缩、加密、哈希或数据序列化,我们通常关心的是位本身,而不是符号。使用 >>> 可以确保结果不会因符号扩展(使用 >> 时会发生)而失真。
  2. 模拟无符号整数:Java 没有本地的无符号 int 类型(Java 8+ 除外,可以通过辅助方法)。因此,如果我们想模拟无符号右移(如 C/C++ 中),>>> 提供了该功能。
  3. 将负数转换为大的正数(二进制上下文):在二进制数据处理(如文件系统或网络)中,负数可能会干扰偏移量和标识符。>>> 可以干净地移位而不保留符号位,使其成为将负值转换为有效的无符号等效值的理想选择。

结论

无符号右移运算符(在包括 Java 和 JavaScript 在内的不同语言中为 >>>,而在 C/C++ 中为 >>)将位向右移位,但与有符号右移(>>)不同,它总是用零填充空出的最左边位。无符号右移运算符将数字视为一个不是负数且无符号的整数,无论其原始符号如何。

三元右移运算符选择题

1. 预测以下代码片段的输出。

  1. -4
  2. 2147483644
  3. 1073741820
  4. 4
 

答案: b)

解释:负值以二的补码形式表示。对于 -8,二的补码形式将是:

-8(二的补码形式):11111111 11111111 11111111 11111000

向左移动一位后,将是:01111111 11111111 11111111 1111100,相当于 2147483644。


2. 当我们在处理文件或网络流等二进制数据时,为什么我们可能选择使用 >>> 运算符?

  1. 因为它保留了数字的符号
  2. 因为它会对负数抛出异常
  3. 因为它有助于将数字视为无符号
  4. 因为它将二进制转换为十进制
 

答案:c)

解释:三元右移运算符确保在移位时,最左边的位始终用 0 填充,无论输入数字的符号如何。


3. 以下程序的输出是什么?

  1. -1
  2. 0
  3. Long.MAX_VALUE
  4. 1
 

答案:c)

解释:在这里,-1L 在二进制中表示为全为 1,使用 >>> 右移它,会在左边填充 0,使其有效地变成最大的 long 值。


4. 关于 Java 中的 >>> 运算符,以下哪个陈述是正确的?

  1. 它仅与正数一起使用
  2. 它不能与 long 值一起使用
  3. 它在移位时保持符号位
  4. 它被称为无符号右移运算符
 

答案: d)

解释:三元右移运算符将给定数字的位向右移动指定的位数,并用零填充空出的最左边位,无论数字的原始符号如何。


5. 当我们在 Java 中对正数使用 >>> 运算符时会发生什么?

  1. 它将位向左移位并给出输出 4。
  2. 它将位向右移位并给出输出 8。
  3. 它使数字变为负数。
  4. 它会引发错误,因为 >>> 不能用于正数。
 

答案: b)

解释:对于正数,三元右移运算符的工作方式类似于右移运算符。它将给定的数字除以 2 的幂。因此,22(我们右移两位)是 4,32 / 4 = 8。