位运算为什么快?因为程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。下面先介绍一下位运算各种符号的意义并附上简单的解释

按位与(&)

按位与的意思是把两个数字的二进制每位进行与运算(两对应位都为1则为1,否则为0)
例子如下:
3的二进制为0011
5的二进制为0101
3 & 5 的二进制为 0001
3 & 5 = 1

按位或(|)

按位或的意思是把两个数字的二进制每位进行或运算(两对应位其中有1则为1,否则0)
例子如下:
3的二进制为0011
5的二进制为0101
3 | 5 的二进制为0111
3 | 5 = 7

按位异或(^)

按位异或的意思是两个数字的二进制每位进行异或运算(两对应位相同则为1,不同为0)
例子如下:
3的二进制为0011
5的二进制为0101
3 ^ 5 的二进制为0110
3 ^ 5 = 6

左移(<<)

左移的意思是将二进制数按指定左移几位,移掉的省略,右边缺失的位,用0补齐
例子如下:
5的二进制为0101
5<<2的二进制为010100
5<<2 = 20

带符号右移(>>)

带符号右移的意思是将二进制数按指定右移几位,移掉的省略,左边缺失的位,(该数是正数)用0补齐,(该数是负数)用1补齐
例子如下:

5的二进制为 00000000000000000000000000000101
5>>2的二进制为00000000000000000000000000000001
5>>2 = 1

-5的二进制为 11111111111111111111111111111011
-5>>2的二进制为11111111111111111111111111111110
-5>>2 = -2

无符号右移(>>>)

无符号右移的意思是将二进制数按指定右移几位,移掉的省略,左边缺失的位,用0补齐
无符号右移,按二进制形式把所有的数字向右移动对应巍峨位数,低位移出(舍弃),高位的空位补零。对于正数来说和带符号右移相同,对于负数来说不同。其他结构和>>相似
例子如下:
5的二进制为 00000000000000000000000000000101
5>>>2的二进制为00000000000000000000000000000001
5>>>2 = 1

-5的二进制为 11111111111111111111111111111011
-5>>>2的二进制为00111111111111111111111111111110
-5>>>2 = 1073741822

按位取反(~)

按位取反的意思是把一个数字的二进制每位进行取反操作(1变0,0变1)
例子如下:
5的二进制为 00000000000000000000000000000101
~5的二进制为11111111111111111111111111111010
~5 = -6

下面附上一个简单的实际运用场景

假设我们需要仅使用一个字段来实现多个权限的控制,这个时候我们可以定义一个Integer值,把它二进制表示中32位的每一位当做一个权限.权限类代码如下

public class Power {

    public static final int SWITCH_ZERO = 0;
    public static final int SWITCH_ONE = 1;
    public static final int SWITCH_TWO = 2;
    public static final int SWITCH_THREE = 3;
    public static final int SWITCH_FOUR = 4;

    
    private Integer switchValue = 0;

    /**
     * 打开开关
     */
    public void openSwitch(int num) {
        int v = 1 << num;
        switchValue = switchValue | v;
    }

    /**
     * 关闭开关
     */
    public void closeSwitch(int num) {
        int v = 1 << num;
        v = ~v;
        switchValue = switchValue & v;
    }

    /**
     * 判断该开关是否开启
     */
    public boolean isSwitchOpen(int num) {
        int v = 1 << num;
        int ret = switchValue & v;
        return ret != 0;
    }

    public Integer getSwitchValue() {
        return switchValue;
    }

    public void setSwitchValue(Integer switchValue) {
        this.switchValue = switchValue;
    }
}

示例代码如下:

public static void main(String[] args) {
        Power item = new Power();
        item.setSwitchValue(0);
        item.openSwitch(Power.SWITCH_FOUR);
        System.out.println(getBinaryString(item.getSwitchValue()));
        System.out.println(item.isSwitchOpen(Power.SWITCH_FOUR));

        item.closeSwitch(Power.SWITCH_FOUR);
        System.out.println(getBinaryString(item.getSwitchValue()));
        System.out.println(item.isSwitchOpen(Power.SWITCH_FOUR));
    }

打印结果如下:

00000000000000000000000000010000
true
00000000000000000000000000000000
false

我们先来看打开开关这个方法

public void openSwitch(int num) {
        int v = 1 << num;
        switchValue = switchValue | v;
    }

由于默认0是关,1是开,所以我们先把要开的那一位左移,如果num为5那么得到v的二进制就为
00000000000000000000000000100000,然后把开关的值和v进行按位或运算,得到开关的值为
00000000000000000000000000100000
因为左移得到的二进制的值除了要开的那一位全是0,那么进行按位或运算时不会影响到其他位的值

下面是关闭开关的这个方法

/**
     * 关闭开关
     */
    public void closeSwitch(int num) {
        int v = 1 << num;
        v = ~v;
        switchValue = switchValue & v;
    }

由于默认0是关,1是开,所以我们先把要开的那一位左移,然后进行按位取反,如果num为5那么得到v的二进制就为
11111111111111111111111111011111,然后把开关的值和v进行按位与运算,那么只有两个数每位都为1得到的结果才是1,所以得到开关的值为
00000000000000000000000000000000
因为左移得到的二进制的值按位取反之后除了要关的那一位全是1,那么进行按位与运算时不会影响到其他位的值

接下来是判断该开关是否开启这个方法

/**
     * 判断该开关是否开启
     */
    public boolean isSwitchOpen(int num) {
        int v = 1 << num;
        int ret = switchValue & v;
        return ret != 0;
    }

和之前两个方法一样,先进行左移得到v,然后把开关的值switchValue和v进行按位与,由于v除了那一位是1,其他都是0,那么按位与运算后得到的值就是switchValue中第num为的值.