Java 提供了一些运算符,用于操作 byte、short、int、long 或 char 的各个位。
使用 << 将位向左移动,使用 >> 将位向右移动。
// Shift two places to the left
0b0000_1011 << 2;
// # => 0b0010_1100
// Shift two places to the right
0b0000_1011 >> 2;
// # => 0b0000_0010
<< 运算符总是在右侧补 0。而 >> 补的则是与最左边那一位相同的位(数字为负数时补 1,为正数时补 0)。
// Shift 2 places to the right preserves the sign
// This is a negative value, whose binary representation is
// 1000_0000_0000_0000_0000_0000_0010_0110
int value = -0x7FFFFFDA;
// Shift two places to the right, preserving the sign bit
value >> 2;
// # => 1110_0000_0000_0000_0000_0000_0000_1001
如果右移时希望补 0,则应改用 >>>。
// Shift two places to the right, inserting 0s on the left
value >>> 2;
// # => 0010_0000_0000_0000_0000_0000_0000_1001
按位与运算符(&)接收两个值,并对每一位执行与运算。它把第一个值的每一位与第二个值中相同位置的位进行比较。如果两者都是 1,结果的该位就是 1;否则,结果的该位是 0。
0b0110_0101 & 0b0011_1100;
// # => 0b0010_0100
按位或运算符(|)接收两个值,并对每一位执行或运算。它把第一个值的每一位与第二个值中相同位置的位进行比较。只要有一个是 1,结果的该位就是 1;否则就是 0。
0b0110_0101 | 0b0011_1100;
// # => 0b0111_1101
按位异或运算符(^)对两个值执行按位异或运算。和按位与、按位或运算符一样,它把第一个值的每一位与第二个值中相同位置的位进行比较。如果其中只有一个为 1,结果的该位就是 1;否则就是 0。
0b0110_0101 ^ 0b0011_1100;
// # => 0b0101_1001
~)最后,按位取反运算符(~)会把每一位翻转。与前面几个运算符不同,它是一元运算符,只作用于一个值。
~0b0110_0101;
// # => 0b1001_1010
你的朋友刚刚给你发来一条带着重要秘密的消息。为了不让别人轻易读懂,这条消息经过了一系列位操作的加密。你需要编写方法来帮助解密这条消息。
解密消息的第一步,是逆转加密过程中的移位,把这些位向右移回。解密过程中后续的步骤会假定左侧补入的是 0。
实现Secrets.shiftBack方法,它接收一个值和要移位的位数,并执行移位。
Secrets.shiftBack(0b1001, 2);
# => 0b0010
接下来,有一些位需要被设为 1。
实现Secrets.setBits方法,它接收一个值和掩码,返回将值中对应的位设为 1 后的结果。当掩码中的某一位为 1 时,值中对应的位也应设为 1。其余所有位保持不变。
Secrets.setBits(0b0110, 0b0101);
# => 0b0111
加密过程中有一些位被翻转了。要解密消息,就需要把它们翻转回来。
实现Secrets.flipBits方法,它接收一个值和掩码。掩码指明值中哪些位需要翻转。如果掩码中的某一位为 1,值中对应的位就翻转。其余所有位保持不变。
Secrets.flipBits(0b1100, 0b0101);
# => 0b1001
最后,还有一些位解密后始终为 0。
实现Secrets.clearBits方法,它接收一个值和掩码。当掩码中的某一位为 1 时,value中对应的位应设为 0。其余所有位保持不变。
Secrets.clearBits(0b0110, 0b0101);
# => 0b0010