一、引言        

        你是不是在看 Java 代码时,总对& ^ <<这些符号感到陌生?明明知道它们是位运算和位移运算,却搞不懂背后的逻辑,更不知道怎么用在实际开发里?其实这些运算一点都不 “抽象”—— 它们直接对应 CPU 底层的半导体电路设计,比如按位与就是 “两个高电压才输出高电压”,左移就是二进制数的快速 “放大”。今天这篇笔记,就从底层原理讲到实战应用,把位运算、位移运算的规则、例子和用法全拆透,让你看完不仅能看懂代码,还能用上这些 “高效运算技巧”!

二、位运算(针对二进制的逐位操作)

        位运算的核心是对两个数的二进制位逐位进行计算,共 4 种基础运算,每种运算都有明确的底层原理和实用场景(CPU底层都是一些半导体电路,与半导体电路的设计有关)。

1. 按位与(&):“双 1 才为 1”

  • 底层原理:对应半导体电路中 “两个输入都是高电压,才输出高电压”;二进制位中,只有两个数的对应位同时为 1,结果位才是 1,其他情况(0&0、0&1、1&0)结果均为 0。

  • 实例计算00111 & 01011 = 00011(逐位对比:第 1 位 0&0=0,第 2 位 0&1=0,第 3 位 1&0=0,第 4 位 1&1=1,第 5 位 1&1=1,最终结果 00011)。

  • 核心应用:精准判断某二进制位的值(“查位”)。
    例:已知未知数二进制为0101x001(x 是第 4 位,从右数),若该数与00001000(仅第 4 位为 1)做 & 运算:

    • 结果为00001000 → x=1;

    • 结果为00000000 → x=0。

2. 按位或(|):“双 0 才为 0”

  • 底层原理:对应半导体电路中 “只要有一个输入是高电压,就输出高电压”;二进制位中,只有两个数的对应位同时为 0,结果位才是 0,其他情况(0|1、1|0、1|1)结果均为 1。

  • 实例计算00111 | 01011 = 01111(逐位对比:第 1 位 0|0=0,第 2 位 0|1=1,第 3 位 1|0=1,第 4 位 1|1=1,第 5 位 1|1=1,最终结果 01111)。

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

  • 底层原理:对应半导体电路中 “两个输入电压不同,才输出高电压”;二进制位中,两个数的对应位不同(一个 0、一个 1) 时,结果位为 1,相同(0&0、1&1)时结果为 0。

  • 实例计算00111 ^ 01011 = 01100(逐位对比:第 1 位 0^0=0,第 2 位 0^1=1,第 3 位 1^0=1,第 4 位 1^1=0,第 5 位 1^1=0,最终结果 01100)。

  • 核心应用
    (1)三数交换(无需临时变量):利用 “异或两次同一个数,结果不变” 的特性。
    例:交换int a = 5(二进制0000 0101)和int b = 3(二进制0000 0011):
    ① a = a ^ b → a = 0000 0101 ^ 0000 0011 = 0000 0110
    ② b = a ^ b → b = 0000 0110 ^ 0000 0011 = 0000 0101(此时 b=5,即原 a 的值);
    ③ a = a ^ b → a = 0000 0110 ^ 0000 0101 = 0000 0011(此时 a=3,即原 b 的值)。
    (2)记录两个数的不同位:异或结果中 “1” 的位置,就是两个数对应位不同的地方。
    例:A=001、B=100A^B=101,结果中第 1 位和第 3 位为 1,说明 A 和 B 的这两位不同;若已知 A 和结果 C,可通过B = A ^ C反推 B(反之同理)。

4. 按位取反(~):“逐位翻转”

  • 底层原理:仅针对单个数字,将其二进制的每一位0 变 1、1 变 0(注意:Java 中整数是有符号的,取反后需结合补码理解,此处先看基础逻辑)。

  • 实例计算:对二进制0011111000取反 → 结果为1100000111

三、位移运算(二进制数的整体移动)

        位移运算本质是让二进制数整体左移或右移,核心是 “快速缩放”,底层基于补码(负数的补码 = 原码取反 + 1)进行计算,分为左移、有符号右移、无符号右移 3 种。

1. 左移(<<):“数据扩大,补 0 填充”

  • 核心逻辑:二进制数整体向左移动 n 位,右边空缺的位用0补全;本质是 “数据扩大”,扩大倍数为 “2 的 n 次方”(类比十进制左移 n 位扩大 10 的 n 次方倍)。

  • 关键规则:左移 n 位 = 原数 × 2ⁿ(前提是不超出数据类型的范围)。

  • 实例计算
    (1)单个数字左移:0000 0001(十进制 1)<< 3 → 0000 1000(十进制 8),即 1×2³=8;
    (2)替代乘法运算(更高效):

    • 4×8 = 4×2³ → 4 的二进制0000 0100左移 3 位 → 0010 0000(十进制 32);

    • 4×9 = 4×(8+1) = 4×2³ + 4×2⁰ → 4 左移 3 位(0010 0000) + 4 左移 0 位(0000 0100) → 结果0010 0100(十进制 36);

    • 4×218 = 4×(128+64+16+8+2) = 4×(2⁷+2⁶+2⁴+2³+2¹) → 4 左移 7 位 + 左移 6 位 + 左移 4 位 + 左移 3 位 + 左移 1 位,最终结果为4×218=872

2. 有符号右移(>>):“数据变小,符号位补位”

  • 核心逻辑:二进制数整体向右移动 n 位,左边空缺的位用原数的符号位补全(正数符号位为 0,负数为 1);本质是 “数据缩小”,缩小倍数为 “2 的 n 次方”(正数直接缩小,负数需结合补码)。

  • 实例计算(以 int 类型 8 位简化表示,实际为 32 位)
    (1)正数右移:00001000(十进制 8)>> 2 → 整体右移 2 位,左边补 0 → 00000010(十进制 2),即 8÷2²=2;
    (2)负数右移:11001000(假设为某负数的补码)>> 2 → 整体右移 2 位,左边补 1(符号位为 1) → 11110010
    (3)实际代码验证:

    • System.out.println(-8); → 输出 - 8(原码10000000 00000000 00000000 00001000);

    • System.out.println(-8 >> 2); → 先算 - 8 的补码11111111 11111111 11111111 11111000,右移 2 位补 1 → 补码11111111 11111111 11111111 11111110,转原码后为 - 2,故输出 - 2。

3. 无符号右移(>>>):“数据变小,全 0 补位”

  • 核心逻辑:二进制数整体向右移动 n 位,左边空缺的位全部用 0 补全(不考虑符号位,即把负数当作正数的二进制来处理),结果始终为非负数。

  • 实例计算(以 int 类型 32 位完整表示)
    (1)System.out.println(-8); → 输出 - 8(原码10000000 00000000 00000000 00001000);
    (2)System.out.println(-8 >>> 2); → 先算 - 8 的补码11111111 11111111 11111111 11111000,无符号右移 2 位,左边补 0 → 结果00111111 11111111 11111111 11111110,转十进制为 1073741822,故输出 1073741822。

四、核心总结:位运算与位移运算的关键区别与应用场景

运算类型 核心操作 关键特性 典型应用
位运算 (&、^、~) 二进制逐位计算 操作单个二进制位,可精准控制位值

查位、

三数交换、

记录不同位

左移(<<) 整体左移,补 0 快速扩大(×2ⁿ),效率高于乘法

替代整数乘法

(如 x×8=x<<3)

有符号右移(>>) 整体右移,补符号位 快速缩小(÷2ⁿ),保留符号

正数 / 负数的整数除法

(如 8>>2=2)

无符号右移(>>>) 整体右移,补 0 快速缩小,结果非负 仅处理非负数或需忽略符号的场景

五、结语

        从按位与的 “查位”、异或的 “三数交换”,到左移替代乘法、无符号右移的特殊处理,我们终于把 Java 里的位运算和位移运算讲明白了。这些运算看似 “小众”,却是理解底层逻辑、写出高效代码的关键 —— 比如用x<<3替代x*8,执行速度会快很多;用异或交换变量,还能省掉临时变量。把这篇笔记收起来,下次遇到位运算相关的代码,你就能一眼看穿逻辑;需要优化运算效率时,这些技巧也能帮上大忙。关注我,后续还会拆解更多 Java 底层知识,让复杂的原理变得 “一看就懂”~

更多推荐