背景
前几天读某个框架的代码,一路下来读着挺顺畅的,唯有读到某处的时候卡顿了,这个地方主要判断是否属于某种状态,但是有点不直观,主要是它使用了位运算中的或运算。虽然不是很直观,但是觉得特别的,因为平常就是四则运算比较多,涉及位运算确实少。于是记了下来,后来回忆其实JDK里面也有不少涉及位运算来管理判断状态的,比如JDK的线程池就有使用。
位运算
在 Java 语言中,位运算有如下这些:
- 左移(<<)
- 右移(>>)
- 无符号右移(>>>)
- 与(&)
- 或(|)
- 非(~)
- 异或(^)
比较常用的有:
- &(与运算):只有当两方都为 true 时,结果才是 true,否则为 false。
- |(或运算):只要当一方为 true 时,结果就是 true,否则为 false。
- ^(异或运算):只要两方不同,结果就是 true,否则为 false。
一个位运算的例子
操作数 5和3
在 Java 中,int 占了 4 个字节(Byte),一个字节呢又等于 8 个 Bit 位。
5 和3的二进制形式
5
0000 0101
3
0000 0011
与运算
5 & 3
0000 0101
0000 0011
-------------
0000 0001
运算结果:5 & 3 = 1
或运算
5 | 3
0000 0101
0000 0011
-------------
0000 0111
运算结果:5 | 3 = 7
异或运算
5 ^ 3
0000 0101
0000 0011
-------------
0000 0110
异或运算结果:5 ^ 3 = 6
应用
定义一组2的n次幂的数,用来标志不同的状态位。
例如:
public static final int ROLE_NORMAL = 0;
public static final int ROLE_USER = 1;
public static final int ROLE_ADMIN = 2;
public static final int ROLE_SUPER_ADMIN = 4;
public static final int ROLE_SUPER_SUPER_ADMIN = 8;
| 位数值 | |||||
|---|---|---|---|---|---|
| 含义 | 8 | 4 | 2 | 1 | 0 |
0000 0000 0000 0000
0000 0000 0000 0001
0000 0000 0000 0010
0000 0000 0000 0100
0000 0000 0000 1000
定义一个变量,用于存储状态(默认值是 0):
private static int roleStatus = ROLE_NORMAL;
当我们要保存状态时,直接用 | 运算即可:
roleStatus = roleStatus | ROLE_USER;
保存的运算过程如下:
0000 0000
0000 0001
--------------
0000 0001
运算结果:roleStatus=1,相当于就把这个 1 存储到 0 的二进制当中了。
** 那么如果要判断 roleStatus 中是否有某个状态呢?使用 & 运算:
roleStatus & ROLE_USER) != 0 // true,代表有;false代表无
计算过程:
roleStatus此时为:roleStatus= 1
0000 0001
0000 0001
-------------
0000 0001
即:roleStatus & ROLE_USER) = 1,因此 roleStatus & ROLE_USER) != 0 ,持有该状态(具有ROLE_USER权限)。
再来判断一个不存在的状态 roleStatus & ROLE_SUPER_ADMIN:
此时:roleStatus= 1
System.out.println((roleStatus & ROLE_SUPER_ADMIN) != 0);// false,代表没有它
计算过程:
0000 0001
0000 0100
------------
0000 0000
计算结果:roleStatus & ROLE_SUPER_ADMIN = 0,说明尙不是ROLE_SUPER_ADMIN。
删除某种状态
roleStatus已经存储了ROLE_USER状态,那如何删除呢?可以使用异或运算
roleStatus ^= ROLE_USER
计算过程:
0000 0001
0000 0001
---------------
0000 0000
roleStatus ^= ROLE_USER的结果为0,又回到了初始状态,即删除了该状态。
又一个例子:
具有ROLE_USER、ROLE_ADMIN、ROLE_SUPER_ADMIN三种状态的例子:
roleStatus = ROLE_USER | ROLE_ADMIN | ROLE_SUPER_ADMIN ;
计算过程:
0000 0000
0000 0001
0000 0010
0000 0100
-------------
0000 0111
计算结果为:7。(看上去是不是很像Linux的用户权限状态码?Linux的权限也是一样的设计。)
去除ROLE_SUPER_ADMIN状态,计算过程:
roleStatus 此时为roleStatus=7(0000 0111)
roleStatus ^= roleStatus | ROLE_SUPER_ADMIN;
0000 0111
0000 0100
-------------
0000 0011
计算结果:roleStatus = 3,已经去除ROLE_SUPER_ADMIN状态。
最后
通过 |、^、& 运算,可以很方便快捷的对状态值进行操作。在某些场景下,如果使用位操作来管理,能简化很多操作,还很节省存储。
(完)