位运算符是直接操作整数二进制位的运算符,它们主要用于底层编程、性能优化和特定算法。
目录
位运算符分类
1.基本位运算符
运算符 | 名称 | 描述 | 示例 |
---|---|---|---|
~ | 按位取反 | 翻转所有位(0变1,1变0) | ~0b1010 → 0b0101 |
& | 按位与 | 两位都为1时结果为1 | 0b1100 & 0b1010 → 0b1000 |
| | 按位或 | 任一位为1时结果为1 | 0b1100 | 0b1010→0b1110 |
^ | 按位异或 | 两位不同时结果为1 | 0b1100 ^ 0b1010 → 0b0110 |
2.移位运算符
运算符 | 名称 | 描述 | 示例 |
---|---|---|---|
<< | 左移 | 左移指定位数,低位补0 | 0b1100 << 2 → 0b110000 |
>> | 算术右移 | 右移指定位数,高位补符号位 | -8 >> 1 → -4 |
>>> | 逻辑右移 | 右移指定位数,高位补0 | -8 >>> 1 → 2147483644 |
移位具体处理
移位运算的操作数始终以32位(int)或64位(long)的二进制补码形式处理
为什么是32位?
Java语言规范规定
- int类型:所有比int小的整数类型(byte、short、char)在运算时会被自动提升为int(32位)。
- long类型:如果操作数是long,则按64位处理。
- 设计初衷:保证跨平台一致性(无论底层硬件是32位还是64位)。
二进制补码存储
- 负数以补码形式存储,例如 -8 的32位表示:
11111111 11111111 11111111 11111000
- 正数直接存储原码 (正数的补码和原码相同),例如 8 的32位表示:
00000000 00000000 00000000 00001000
(1)移位操作数处理
操作数类型 | 实际运算位数 | 示例 |
---|---|---|
byte /short /char | 提升为32位 | byte b = 8; b << 2 → 按int 处理 |
int | 32位 | 8 << 2 → 操作32位 |
long | 64位 | 8L << 2 → 操作64位 |
(2)移位数处理
- int移位:int i ; i<<n ; 若 n>32 , 则只需移位( n % 32)
8 << 34 // 实际是 8 << (34 % 32) = 8 << 2 = 32
-
long移位:long l ; l<<n ; 若 n>64 , 则只需移位(n % 64)
8L << 66 // 实际是 8L << (66 % 64) = 8L << 2 = 32L
位运算符的特点
1.只适用于整数类型:
- 有效类型:byte, short, int, long, char
- 无效类型:float, double, boolean(布尔值的 & | ^ 是逻辑运算,注意按位取反运算符~ 不能直接用于 boolean 类型,如果需要布尔值的反向逻辑,应使用 逻辑非运算符 !)
为什么不能用于float/double?
设计原理:
- 位运算符是设计用于整数二进制表示的操作
- float和double采用IEEE 754浮点标准存储,其二进制结构与整数完全不同
- 浮点数的位模式包含符号位、指数位和尾数位,直接进行位运算没有数学意义
float f = 1.5f;
// int result = f & 0x0F; // 编译错误:位运算符不能应用于float
2.运算前自动类型提升:
byte a = 0b0011;
byte b = 0b0101;
int result = a & b; // byte会先提升为int
3.应用:
- 判断奇偶:(num & 1) == 0
- 交换两数:a ^= b; b ^= a; a ^= b;
- 取绝对值:(n ^ (n >> 31)) - (n >> 31)
- 位移位操作(<<、>>、>>>)优化计算
快速乘除法(处理负数需要特别注意,因为简单的位移可能导致不符合预期的结果)
int x = 10;
x = x << 1; // 相当于 x * 2 = 20
x = x >> 2; // 相当于 x / 4 = 5
2的幂次方计算
int pow2 = 1 << n; // 计算2的n次方
注意事项
1.运算符优先级问题
- 移位运算符优先级低于算术运算符
int a = 5 + 3 << 2; // 等价于(5+3)<<2=32,不是5+(3<<2)=17
2.复合赋值运算符的隐式转换
byte b = 10;
b |= 0b1000; // 合法,相当于b = (byte)(b | 0b1000)
// b = b | 0b1000; // 编译错误,需要强制转换
3.数值问题
- 对负数进行右移操作时结果可能不符合直觉
//计算前将原码转为补码,按补码运算,运算完成转回原码
int negative = -8; // 0xFFFFFFF8
int shifted = negative >> 1; // 0xFFFFFFFC (-4)
int unsigned = negative >>> 1; // 0x7FFFFFFC (2147483644)
4.不可用于BigInteger
- 虽然BigInteger存储大整数,但需要使用专门的方法而非位运算符
BigInteger bi = new BigInteger("10");
// int wrong = bi & 1; // 编译错误
int right = bi.and(BigInteger.ONE).intValue();
为什么位运算符不能直接用于 BigInteger/BigDecimal?
(1)设计目的不同
- BigInteger/BigDecimal 是高精度数值类,用于处理任意大小的整数或精确小数。
- 位运算符是为固定位数的原始类型(如 int、long)设计的,与高精度运算的定位冲突。
(2)内部存储方式
- BigInteger 内部使用 可变长度的 int[] 存储数据,而非固定的二进制补码形式。
- BigDecimal 内部基于 BigInteger + 小数位数(scale),更无位操作的意义。
(3)运算符重载限制
- Java 不支持运算符重载,无法为自定义类(如 BigInteger)重定义 &、| 等操作符。