Contents

Java位运算符

1. 概述

Java 语言中使用运算符 对数据和变量进行操作。

在本教程中,我们将探讨位运算符以及它们在 Java 中的工作方式。

2. 位运算符

位运算符适用于二进制数字 或输入值的位。 我们可以将这些应用于整数类型——long、int、short、char和 byte

在探索不同的位运算符之前,让我们首先了解它们是如何工作的。

按位运算符处理十进制数的二进制等效项,并根据给定的运算符逐位对它们执行操作:

  • 首先,操作数被转换为它们的二进制表示
  • 接下来,将运算符应用于每个二进制数并计算结果
  • 最后,将结果转换回其十进制表示

让我们通过一个例子来理解;让我们取两个int

int value1 = 6;
int value2 = 5;

接下来,让我们对这些数字应用按位或运算符:

int result = 6 | 5;

要执行此操作,首先将计算这些数字的二进制表示:

Binary number of value1 = 0110
Binary number of value2 = 0101

然后该操作将应用于每个位。结果返回一个新的二进制数:

0110
## 0101
0111

最后,结果0111将被转换回十进制,等于7

result : 7

按位运算符进一步分为按位逻辑运算符和按位移位运算符。现在让我们来看看每种类型。

3. 位逻辑运算符

位逻辑运算符是 AND(&)、OR(|)、XOR(^) 和 NOT(~)。

3.1. 按位或(|)

OR 运算符比较两个整数的每个二进制数字,如果其中一个为 1,则返回 1。

这类似于 || 与布尔值一起使用的逻辑运算符。当比较两个布尔值时,如果其中一个为真,则结果为true。同样,当其中任何一个为 1 时,输出为 1。

我们在上一节中看到了此运算符的示例:

@Test
public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int value2 = 5;
    int result = value1 | value2;
    assertEquals(7, result);
}

让我们看看这个操作的二进制表示:

0110
## 0101
0111

在这里,我们可以看到使用 OR,0 和 0 将导致 0,而任何至少为 1 的组合将导致 1。

3.2. 按位与(&)

AND 运算符比较两个整数的每个二进制数字,如果两者都是 1,则返回 1,否则返回 0。

这类似于具有boolean值的 && 运算符。当两个boolean值为true时,&& 操作的结果为true

让我们使用与上面相同的示例,只是现在使用 & 运算符而不是 | 操作员:

@Test
public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int value2 = 5;
    int result = value1 & value2;
    assertEquals(4, result);
}

让我们也看看这个操作的二进制表示:

0110
## 0101
0100

0100是十进制的4,因此,结果是:

result : 4

3.3. 按位异或 (^)

**XOR 运算符比较两个整数的每个二进制位,如果两个比较的位不同,则返回 1。**这意味着如果两个整数的位都是 1 或 0,则结果将为 0;否则,结果将为 1:

@Test
public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int value2 = 5;
    int result = value1 ^ value2;
    assertEquals(3, result);
}

和二进制表示:

0110
## 0101
0011

0011是十进制的 3,因此,结果是:

result : 3

3.4. 按位补码 (~)

Bitwise Not 或 Complement 运算符仅表示输入值的每一位的否定。它只需要一个整数,它相当于!操作员。

此运算符更改整数的每个二进制数字,这意味着所有 0 变为 1,所有 1 变为 0。运算符对boolean值的工作方式类似:它将boolean值从true反转 为false,反之亦然。

现在让我们通过一个例子来了解如何找到十进制数的补码。

让我们做 value1 = 6 的补码:

@Test
public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int result = ~value1;
    assertEquals(-7, result);
}

二进制值是:

value1 = 0000 0110

通过应用补码运算符,结果将是:

0000 0110 -> 1111 1001

这是十进制数 6 的补码。由于二进制的第一个(最左边)位是 1,这意味着存储的数字的符号为负。 现在,由于数字存储为 2 的补码,首先我们需要找到它的 2 的补码,然后将生成的二进制数转换为十进制数:

1111 1001 -> 0000 0110 + 1 -> 0000 0111

最后,0000 0111 是十进制的 7。由于如上所述符号位为 1,因此得到的答案是:

result : -7

3.5. 位运算符表

让我们总结一下到目前为止我们在比较表中看到的运算符的结果:

A	B	A|B	A&B	A^B	~A
0	0	0	0	0	1
1	0	1	0	1	0
0	1	1	0	1	1
1	1	1	1	0	0

4. 移位运算符

二元移位运算符根据移位运算符将输入值的所有位向左或向右移动。

让我们看看这些运算符的语法:

value <operator> <number_of_times>

表达式的左侧是移位的整数,表达式的右侧表示它必须移位的次数。 按位移位运算符进一步分为按位左移和按位右移运算符。

4.1.有符号左移 [«]

左移运算符将位向左移动操作数右侧指定的次数。左移后,右边的空白处用 0 填充。

另一个需要注意的重要一点是,将一个数字移动 1 相当于将其乘以 2,或者,一般来说,将一个数字左移 n 个位置相当于乘以 2^n

让我们将值 12 作为输入值。

现在,我们将它向左移动 2 个位置( 12 <<* 2* ),看看最终结果是什么。

12的二进制等价物是00001100。左移2位后,结果是00110000,相当于十进制的48:

@Test
public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int leftShift = value << 2;
    assertEquals(48, leftShift);
}

这同样适用于负值:

@Test
public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int leftShift = value << 2;
    assertEquals(-48, leftShift);
}

4.2. 有符号右移 [»]

**右移运算符将所有位右移。**根据输入的数字填充左侧的空白区域:

  • 当输入数字为负数时,最左边的位为 1,则空白处将用 1 填充
  • 当输入数字为正数时,最左边的位为 0,则空白处将用 0 填充

让我们继续使用 12 作为输入的示例。

现在,我们将它向右移动 2 个位置(12 »2),看看最终结果是什么。

输入的数是正数,所以右移2位,结果是0011,也就是十进制的3:

@Test
public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int rightShift = value >> 2;
    assertEquals(3, rightShift);
}

此外,对于负值:

@Test
public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int rightShift = value >> 2;
    assertEquals(-3, rightShift);
}

4.3. 无符号右移 [»>]

该运算符与有符号右移运算符非常相似。**唯一的区别是左边的空格不管数字是正数还是负数都用0填充。**因此,结果将始终为正整数。

让我们右移相同的值 12:

@Test
public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int unsignedRightShift = value >>> 2;
    assertEquals(3, unsignedRightShift);
}

现在,负值:

@Test
public void givenOneNegativeInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int unsignedRightShift = value >>> 2;
    assertEquals(1073741821, unsignedRightShift);
}

5. 按位运算符和逻辑运算符的区别

我们在这里讨论的按位运算符与更常见的逻辑运算符之间存在一些差异。

首先,逻辑运算符作用于boolean表达式并返回boolean值(truefalse),而位运算符作用于整数值的二进制数字long、int、short、char和 byte)并返回一个整数。

此外,逻辑运算符总是计算第一个boolean表达式,并且根据其结果和使用的运算符,可能会或可能不会计算第二个布尔表达式。另一方面,按位运算符总是计算两个操作数

最后,逻辑运算符用于根据多个条件进行决策,而按位运算符则对位进行操作并执行逐位操作。

6. 用例

位运算符的一些潜在用例是:

  • 通信堆栈,其中附加到数据的标头中的各个位表示重要信息
  • 在嵌入式系统中,仅设置/清除/切换特定寄存器的一位而不修改其余位
  • 使用 XOR 运算符加密数据以解决安全问题
  • 在数据压缩中,通过将数据从一种表示转换为另一种表示,以减少使用的空间量