Java中使用long存储位序列的位编辑、初始化与操作异常问题
问题场景与复现
- 基于64位
long类型实现位序列存储与指定位编辑,初始阶段为了初始化全0位序列,写了冗余的全0二进制字面量:long singleBit = 0b0000000000000000000000000000000000000000000000000000000000000000L; - 尝试通过左移+按位或实现指定位置1操作时,出现两个核心异常:
- 执行
System.out.println(Long.toBinaryString( singleBit | 1 << 62));时,输出为1000000000000000000000000000000(共31位),缺失大量前导0,位位置完全不符合预期 - 对位序列靠后的第63位等位置执行同类操作时同样出现结果偏差
- 将移位操作的字面量1替换为自定义的long类型常量
long oneBit = 0b0000000000000000000000000000000000000000000000000000000000000001L;后,移位运算的数值本身正确,但按位或的最终结果和预期不符
- 执行
异常根本原因
1. 字面量类型与移位截断问题
Java中不带L/l后缀的整数字面量1,本质是32位int类型,不是64位long类型。
执行1 << 62的完整过程:
- Java规定对int类型做移位运算时,仅取移位值的低5位作为实际移位长度,也就是
62 & 0b11111 = 30 - 实际等价于执行
1 << 30,得到的int值为0x40000000,隐式转换为long后为31位长度的0x0000000040000000L Long.toBinaryString的输出特性为不补前导0,仅输出从最高位有效1开始的二进制串,因此输出结果刚好是1后面跟30个0的31位字符串,和实际观测的输出完全一致——本质是移位位置被截断到了30位,根本没有到达62位的位置。- 对应第63位的移位操作
1 << 63,同样会被截断为取移位值的低5位63 & 0b11111 = 31,实际执行1 << 31,结果为int类型的负数,转long后高位带扩展1,完全达不到63位的目标位置。
2. long类型常量替换后的按位或异常
替换为long类型的oneBit后移位运算本身不会再被截断,出现逻辑偏差通常来自两个认知误区:
- 位索引方向认知偏差:位运算场景下通用的索引规则为*最低位(值为20的位置)为第0位,从右向左计数*,如果误将最高位(符号位,值为263的位置)作为第0位从左向右计数,移位位置会完全颠倒,得到完全不符合预期的按位或结果
- 运算符优先级认知偏差:左移运算符
<<的优先级高于按位或|,如果没有按照运算规则加括号,会导致运算顺序错乱
正确实现方案
1. 简化long类型的初始化
不需要写满64位的全0二进制字面量,直接赋值0即可:long bitSequence = 0L;
2. 标准指定位编辑操作(统一采用最低位为第0位的计数规则)
所有移位操作必须使用带L后缀的1,确保运算在64位long空间内进行,避免截断:
- 指定位置1:
bitSequence |= 1L << bitIndex; - 指定位清0:
bitSequence &= ~(1L << bitIndex); - 指定位取反:
bitSequence ^= 1L << bitIndex; - 读取指定位状态:
boolean bitValue = (bitSequence & (1L << bitIndex)) != 0;
注意:将long作为位序列容器使用时,不需要关心其作为有符号数的正负属性,包括符号位(第63位)在内的所有64个比特位都可以作为独立的存储位自由编辑。
3. 输出完整64位二进制串
弥补Long.toBinaryString不补前导0的缺陷,输出对齐64位的二进制串:
String full64BitBinary = String.format("%64s", Long.toBinaryString(bitSequence)).replace(' ', '0');
内容的提问来源于stack exchange,提问作者user14717506
相关产品推荐
相关产品推荐

