源本科技 | 码上会

Java 位运算符

2025/12/25
14
0

在 Java 中,位运算符(Bitwise Operators) 允许我们直接对整数的二进制位进行操作。它们是低级编程、性能优化和特定算法(如加密、压缩、图形处理)的核心工具。虽然日常开发中不常使用,但掌握位运算能让你写出更高效、更紧凑的代码。


学习目标

  • 理解 Java 中 7 种位运算符的作用与原理

  • 掌握按位与、或、异或、取反及三种移位操作的使用方法

  • 能够将十进制数转换为二进制并手动计算位运算结果

  • 了解位运算在实际场景中的应用价值

  • 避免常见误区(如补码表示、符号位处理等)


什么是位运算符?

位运算符作用于 byteshortintlong 等整数类型的每一位,直接在二进制层面进行逻辑或算术操作。Java 提供以下 7 种位运算符:

💡 所有位运算都在 32 位(int)或 64 位(long) 的二进制补码表示下进行。


1. 按位与(&) “全 1 才 1”

只有当两个对应位都为 1 时,结果位才为 1

int a = 5; // 二进制: 0101
int b = 7; // 二进制: 0111

// 按位与:
//   0101
// & 0111
// ------
//   0101 → 十进制 5

System.out.println(a & b); // 输出: 5

应用场景

  • 掩码(Masking):提取特定位

    int flags = 0b10110010;
    boolean isFeatureEnabled = (flags & 0b00000100) != 0; // 检查第 3 位
  • 判断奇偶(n & 1) == 0 表示偶数


2. 按位或(|)“有 1 就 1”

只要有一个对应位为 1,结果位就为 1

int a = 5; // 0101
int b = 7; // 0111

// 按位或:
//   0101
// | 0111
// ------
//   0111 → 十进制 7

System.out.println(a | b); // 输出: 7

应用场景

  • 设置标志位

    int permissions = READ;
    permissions |= WRITE; // 添加写权限

3. 按位异或(^) “不同为 1”

对应位不同时结果为 1,相同则为 0

int a = 5; // 0101
int b = 7; // 0111

// 按位异或:
//   0101
// ^ 0111
// ------
//   0010 → 十进制 2

System.out.println(a ^ b); // 输出: 2

特性与应用

  • 自反性a ^ a = 0

  • 交换律 / 结合律:可用于不借助临时变量交换两个数

    a = a ^ b;
    b = a ^ b; // b = (a^b)^b = a
    a = a ^ b; // a = (a^b)^a = b
  • 数据校验:奇偶校验、简单加密


4. 按位取反(~)“0 变 1,1 变 0”

对所有位进行翻转。注意:Java 使用二进制补码表示负数!

int a = 5; // 32 位: 00000000 00000000 00000000 00000101

// 取反后:
//        11111111 11111111 11111111 11111010

// 这是 -6 的补码表示!
System.out.println(~a); // 输出: -6

数学关系

~N = -(N + 1)

验证:

  • ~5 = -6

  • ~(-3) = 2

⚠️ 不要误以为 ~5 = 246(那是无符号解释),Java 中所有整数都是有符号的。


5. 位移运算符 快速乘除 2 的幂

左移(<<

  • 所有位向左移动,低位补 0

  • 相当于 乘以 2ⁿ(可能溢出)

int x = 4; // 0100
System.out.println(x << 2); // 010000 → 16 (4 * 2²)

有符号右移(>>

  • 高位补符号位(正数补 0,负数补 1)

  • 相当于 除以 2ⁿ(向下取整)

int x = -8; // ...11111000
System.out.println(x >> 2); // ...11111110 → -2

无符号右移(>>>

  • 高位始终补 0,忽略符号

  • 用于将负数视为无符号值处理

int x = -1; // 32 个 1
System.out.println(x >>> 30); // 000000...11 → 3

🔍 对比:

System.out.println(-8 >> 1);  // -4
System.out.println(-8 >>> 1); // 2147483644(大正数!)

完整示例

public class BitwiseDemo {
    public static void main(String[] args) {
        int a = 5;  // 0101
        int b = 7;  // 0111

        System.out.println("a & b = " + (a & b));   // 5
        System.out.println("a | b = " + (a | b));   // 7
        System.out.println("a ^ b = " + (a ^ b));   // 2
        System.out.println("~a    = " + ~a);        // -6
        System.out.println("a << 2= " + (a << 2));  // 20
        System.out.println("a >> 1= " + (a >> 1));  // 2
        System.out.println("a >>>1= " + (a >>> 1)); // 2(正数时与 >> 相同)
    }
}
a & b = 5
a | b = 7
a ^ b = 2
~a    = -6
a << 2= 20
a >> 1= 2
a >>>1= 2

实际应用场景

领域

应用示例

权限系统

用单个整数存储多个布尔权限(如 READ=1, WRITE=2, EXEC=4

加密算法

AES、SHA 等大量使用 XOR 和位移

图形处理

像素颜色通道分离(RGB → R, G, B)

网络协议

IP 地址与子网掩码计算

性能优化

x << 1x * 2 更快(现代 JIT 可能优化掉,但仍有意义)

数据压缩

Huffman 编码、位打包


优势与注意事项

优势

  • 执行速度快:直接操作 CPU 寄存器

  • 内存高效:多个标志可压缩到一个整数

  • 算法简洁:某些问题(如找唯一数)用 XOR 极简解决

  • 底层控制力强:精确操控每一位

注意事项

  • 仅适用于整数类型

  • 负数使用补码表示,取反和右移需特别小心

  • 可读性差:过度使用会降低代码可维护性

  • 优先级较低:通常需加括号

    // 错误:== 优先级高于 &
    if (a & b == 0) { } 
    
    // 正确
    if ((a & b) == 0) { }

重点总结

  • &|^逐位逻辑运算

  • ~按位取反,结果遵循 ~N = -(N+1)

  • << 左移 = 乘 2ⁿ,>> 有符号右移 = 除 2ⁿ(保留符号)

  • >>> 无符号右移 = 高位补 0,用于处理“无符号”场景

  • 位运算高效但晦涩,应在必要时使用,并添加注释


思考题

  1. 如何用位运算判断一个整数是否是 2 的幂?

  2. 为什么 ~5 的结果是 -6 而不是 246?请从补码角度解释。

  3. 给定一个数组,其中只有一个数字出现一次,其余都出现两次,如何用位运算找出这个数字?


位运算符是程序员工具箱中的“精密手术刀”——不常用,但在关键时刻能解决棘手问题。理解其原理,善用其威力,你将能编写出更底层、更高效的 Java 程序。