Java位运算详解:原理、用法及实战场景(面试重点)

在Java编程中,位运算作为一种底层运算方式,直接操作整型数值的二进制位,其运算效率远超普通的加减乘除运算。但很多开发者在实际工作中却很少用到它,究其原因,大多是因为其语法晦涩、可读性较弱------毕竟代码是写给人看的,高效的同时兼顾易懂性,才是工业级开发的核心诉求。但这并不影响位运算成为面试中的高频考点,也是夯实Java基础的关键知识点,今天就带大家全面吃透位运算与移位运算,从原理到实战,层层拆解、通俗易懂。

一、位运算核心原理

位运算的核心是直接对组成整型数值(如byte、int、long等)的二进制位进行操作,无需经过十进制与二进制的转换中间环节,因此运算速度极快。常用的位运算有4种,分别是按位与(&)、按位或(|)、按位取反(~)、按位异或(^),以下用例均以byte类型(8位二进制)为例,结合负数的补码规则(计算机存储负数以补码形式)详细拆解。

1. 按位与运算(&)

运算规则:两个操作数的对应二进制位,同为1则结果为1,否则为0

关键补充:byte类型的范围是-128~127,负数的补码计算方式为"反码+1"(反码是对原码按位取反,符号位不变)。例如-10的原码为10001010,反码为11110101,补码为11110110(计算机中实际存储的是补码);8的原码、反码、补码均为00001000。

例题:-10 & 8

拆解过程:

-10(补码):11110110

8(补码): 00001000

按位与结果:00000000(对应十进制0)

2. 按位或运算(|)

运算规则:两个操作数的对应二进制位,同为0则结果为0,其余情况均为1(即只要有一个为1,结果就为1)。

例题:-10 | 8

拆解过程:

-10(补码):11110110

8(补码): 00001000

按位或结果:11111110(对应十进制-2)

3. 按位取反运算(~)

运算规则:对单个操作数的每一位二进制位按位取反,0变1,1变0。

注意点:按位取反是一元运算(只需一个操作数),且取反后结果会遵循补码规则,对于byte类型,取反后会涉及符号位的变化,最终结果与原数满足"~x = -x - 1"的规律。

例题:~8

拆解过程:

8(补码):00001000

按位取反:11110111(对应十进制-9,验证:~8 = -8 -1 = -9)

再如:~(-10)

-10(补码):11110110

按位取反:00001001(对应十进制9,验证:~(-10) = 10 -1 = 9)

4. 按位异或运算(^)

运算规则:两个操作数的对应二进制位,相同则结果为0,不同则结果为1(可记忆为"相同为0,不同为1")。

补充说明:异或运算有两个常用特性------① 一个数异或自身结果为0(x^x = 0);② 一个数异或0结果仍为自身(x^0 = x),这两个特性是异或实现数值交换的核心。

例题:-10 ^ 8(修正原例题结果疏漏,结合补码重新计算)

拆解过程:

-10(补码):11110110

8(补码): 00001000

按位异或结果:11111110(对应十进制-2,原例题结果正确,补充计算逻辑)

二、位运算的实战用途(高频场景+面试考点)

前文提到,位运算的运算效率远高于普通的加减乘除(直接操作二进制位,无需CPU进行复杂的算术运算),但实际工作中使用频率较低,核心原因是其语法晦涩、可读性弱------代码不仅是写给自己看的,更要让同事能快速理解,因此在对效率要求不极致的场景下,开发者更倾向于使用可读性更强的算术运算。

但在以下场景中,若对运算效率要求较高(如底层开发、高频运算场景),位运算就能发挥优势,同时这些场景也是面试中的高频考点,建议重点掌握:

1. 判断int型变量a是奇数还是偶数

核心逻辑:奇数的二进制最低位一定是1,偶数的二进制最低位一定是0;与1(二进制最低位为1,其余为0)进行按位与运算,可快速判断最低位状态。

实现代码:

a & 1 == 0 → 偶数

a & 1 == 1 → 奇数

优势:比a % 2判断更高效(无需进行除法运算)。

2. 求两个int类型变量x、y的平均值(避免溢出)

常规思路:(x + y) / 2,但当x和y均为较大的正数时,x + y的结果可能超过int的最大范围(-2³¹~2³¹-1),导致溢出,最终结果错误。

位运算实现(无溢出):(x & y) + ((x ^ y) >> 1)

逻辑拆解:x & y 获取x和y二进制位中同为1的部分(即两者的公共部分,相当于平均值的整数基础);x ^ y 获取x和y二进制位中不同的部分(相当于两者的差值部分),右移1位等价于除以2,最终将两部分相加得到平均值。

3. 判断一个大于0的整数x是不是2的幂次方

核心逻辑:2的幂次方的二进制特点是"只有一位为1,其余全为0"(如2→10、4→100、8→1000);x & (x - 1) 会将x二进制中最低位的1变为0,若x是2的幂次方,执行后结果为0。

实现代码:((x & (x - 1)) == 0) && (x != 0)

注意:x != 0 是因为0满足(x & (x - 1)) == 0,但0不是2的幂次方。

4. 交换两个int类型变量x、y(无需临时变量,高效)

常规思路:需要定义临时变量temp,temp = x; x = y; y = temp;,会占用额外的内存空间。

位运算实现(无临时变量,性能最优):

x ^= y; // x = x ^ y(此时x存储了x和y的差值特征)

y ^= x; // y = y ^ (x ^ y) = x(利用异或特性x^x=0、0^x=x,将x的值赋给y)

x ^= y; // x = (x ^ y) ^ x = y(将y原来的值赋给x)

注意:该方法仅适用于数值类型,且x和y不能指向同一个内存地址(否则会导致两者都变为0)。

5. 求int类型变量x的绝对值

核心逻辑:int类型是32位,最高位为符号位(0表示正数,1表示负数);x >> 31 会将符号位扩展到整个32位(正数右移31位为0,负数右移31位为-1,即二进制全1)。

实现代码:

int abs(int x) {

int y = x >> 31; // 获取符号位(正数y=0,负数y=-1)

return (x ^ y) - y; // 等价于(x + y) ^ y,负数取反+1,正数不变

}

逻辑验证:x为正数时,y=0,(x^0)-0 = x;x为负数时,y=-1(二进制全1),x^y 等价于按位取反,再减y(减-1即加1),最终得到绝对值。

6. 取模运算(a % (2ⁿ) 等价于 a & (2ⁿ - 1))

核心逻辑:2ⁿ 的二进制是"1后面跟n个0"(如2³=8→1000),2ⁿ - 1 的二进制是"n个1"(如2³-1=7→0111);a & (2ⁿ -1) 会保留a二进制的低n位,等价于a除以2ⁿ的余数。

示例:a=10(1010),n=2(2²=4),10 % 4 = 2;10 & (4-1) = 10 & 3 = 2(1010 & 0011 = 0010)。

注意:该方法仅适用于除数是2的幂次方的场景,且效率远高于常规取模运算。

7. 乘法运算(a * (2ⁿ) 等价于 a << n)

核心逻辑:左移n位,等价于将二进制数整体向左移动n位,右边补0,数值扩大为原来的2ⁿ倍,即a乘以2ⁿ。

示例:a=3(0011),n=2,3 * 4 = 12;3 << 2 = 12(0011 << 2 → 1100)。

8. 除法运算(a / (2ⁿ) 等价于 a >> n)

核心逻辑:右移n位,等价于将二进制数整体向右移动n位,左边补符号位(正数补0,负数补1),数值缩小为原来的1/2ⁿ,即a除以2ⁿ(向下取整)。

示例:a=12(1100),n=2,12 / 4 = 3;12 >> 2 = 3(1100 >> 2 → 0011)。

9. 求一个整数x的相反数(~x + 1)

核心逻辑:根据补码规则,负数的补码是原码取反加1,而一个数的相反数的补码,就是该数补码的取反加1,因此~x + 1 等价于 -x。

示例:x=10(00001010),~10 + 1 = 11110101 + 1 = 11110110(对应十进制-10)。

10. 补充:a % 2 等价于 a & 1

本质是第6点取模运算的特例(n=1,2¹=2,2¹-1=1),也是判断奇偶性的底层逻辑,效率高于常规取模。

三、移位运算(与位运算相辅相成)

移位运算也是操作二进制位的重要运算,分为左移(<<)、有符号右移(>>)、无符号右移(>>>)三种,主要用于实现数值的快速乘除(如前文位运算的7、8点),以下仍以byte类型为例讲解。

1. 左移运算(<<)

运算规则:将操作数的二进制位整体向左移动n位,右边空出来的位用0填补,高位左移后溢出的部分直接舍弃。

核心结论:左移n位,等价于该数乘以2的n次幂(a << n = a * 2ⁿ),效率远高于乘法运算。

示例:3 << 2 → 3 * 2² = 12(0011 << 2 → 1100);8 << 1 → 16(00001000 << 1 → 00010000)。

2. 有符号右移运算(>>)

运算规则:将操作数的二进制位整体向右移动n位,左边空出来的位根据符号位填补(正数符号位为0,补0;负数符号位为1,补1),低位右移后溢出的部分舍弃。

核心结论:有符号右移n位,等价于该数除以2的n次幂(a >> n = a / 2ⁿ),向下取整,效率高于除法运算。

示例:12 >> 2 → 12 / 4 = 3(1100 >> 2 → 0011);-10 >> 1 → -5(11110110 >> 1 → 11111011,对应十进制-5)。

3. 无符号右移运算(>>>)

运算规则:将操作数的二进制位整体向右移动n位,无论原操作数的符号位是0还是1,左边空出来的位一律用0填补,低位右移后溢出的部分舍弃。

关键注意点:

① Java中整型默认是int类型(32位),因此进行无符号右移时,会将操作数先转换为32位二进制数再进行移位操作;

② 无符号右移会将负数的符号位(1)也当作普通位处理,填补0后,负数会变成正数(因为最高位变为0)。

示例:-1(int类型,32位全1)>>> 1 → 2147483647(31位全1,最高位为0,对应int类型的最大值)。

四、面试重点实战题(必掌握)

题目:用最快的速度计算出2 * 16的值(高频面试题,考察移位运算的应用)

核心思路:16是2的4次幂(2⁴=16),根据左移运算的特性,2 * 16 = 2 * 2⁴ = 2 << 4,移位运算效率远高于直接乘法。

验证代码(测试运行时间,单位:纳秒):

public static void main(String[] args) {

方法1:直接乘法运算

long startTime = System.nanoTime(); // 获取开始时间

System.out.println(2 * 16); // 输出结果:32

long endTime = System.nanoTime(); // 获取结束时间

System.out.println("乘法运算运行时间: " + (endTime - startTime) + "ns");

方法2:左移运算(最快)

long startTime1 = System.nanoTime(); // 获取开始时间

System.out.println(2 << 4); // 输出结果:32,2左移4位等价于2*16

long endTime1 = System.nanoTime(); // 获取结束时间

System.out.println("左移运算运行时间: " + (endTime1 - startTime1) + "ns");

}

运行结果说明:多次测试后会发现,左移运算的运行时间远短于乘法运算,这就是移位运算(位运算延伸)的效率优势,也是面试中考察的核心点。

五、总结

位运算与移位运算的核心优势是运算效率极高,本质是直接操作二进制位,跳过了十进制与二进制的转换环节,适合底层开发、高频运算等对效率要求极致的场景。但由于其语法晦涩、可读性较弱,实际业务开发中使用频率较低------毕竟代码的可维护性、可读性,往往比极致的效率更重要(除非有明确的性能瓶颈)。

但对于Java学习者和面试者而言,位运算及其应用场景是必须掌握的知识点:一方面,它能帮助我们更深入理解计算机存储数据的底层逻辑(二进制、补码);另一方面,面试中频繁考察的奇偶判断、数值交换、求平均值(无溢出)等场景,都需要用到位运算的核心思路。

相关推荐
callJJ1 小时前
深入浅出 MVCC —— 从零理解 MySQL 并发控制
数据库·mysql·面试·并发·mvcc
游乐码1 小时前
c#万物之父装箱拆箱
开发语言·c#
Scott.W1 小时前
跟我学Easyi3C Tower Adapter Console(9)
人工智能·python·嵌入式硬件·i3c
多恩Stone1 小时前
【3D-AICG 系列-14】Trellis 2 的 Texturing Pipeline 保留单层薄壳,而 Textured GLB 会变成双层
人工智能·python·算法·3d·aigc
CDwenhuohuo1 小时前
var面试题
开发语言·javascript·ecmascript
PD我是你的真爱粉1 小时前
深入理解 Event Loop:JavaScript 的“心脏起搏器”
开发语言·javascript·ecmascript
GIS程序猿2 小时前
批量出图工具,如何使用C#实现动态文本
开发语言·arcgis·c#·arcgis插件·gis二次开发
小杜的生信筆記2 小时前
生信技能技巧小知识,Linux多线程压缩/解压工具
linux·数据库·redis
刘恒1234567892 小时前
Windows 电脑文件夹手动分类指南
java·windows·python·电脑·php