Java 通过位运算操作状态

首先通过定义一组静态常量来定义多个状态:

1
2
3
4
public static final int OP_READ = 1<<0;
public static final int OP_WRITE = 1<<2;
public static final int OP_CONNECT = 1<<3;
public static final int OP_ACCEPT = 1<<4;

如果把它们转换为二进制:

1
2
3
4
1<<0: 0000 0001
1<<2: 0000 0100
1<<3: 0000 1000
1<<4: 0001 0000

可以发现,一个数如果是 2 的幂,则它的二进制数都只有一个位为 1,其余位数都是 0.

存储状态

下面再来定义一个变量,用来存储状态(默认值是 0):

1
private static int STATUS = 0;

当需要保存状态时,直接用按位或运算 | :

1
2
//假如需要保存状态 OP_READ
STATUS = STATUS | OP_READ;

保存的运算过程如下:

1
2
3
4
5
0000 0000
执行 | 运算
0000 0001
---------
0000 0001

相当于把 1 这个状态值存储到 0 的二进制当中。

可以在 STATUS 中存储多个状态值,STATUS = OP_READ | OP_WRITE | OP_CONNECT

判断状态

那么如果要判断变量 STATUS 中是否有某个状态呢?这时候需要使用 & 运算:

1
( STATUS & OP_READ ) != 0 //true,代表含有 OP_READ

计算过程如下:

1
2
3
4
5
0000 0001
执行 & 运算
0000 0001
---------
0000 0001

再来判断一个不存在的状态 STATUS & OP_WRITE

1
( STATUS & OP_WRITE ) != 0; //false,代表不含有 OP_WRITE

计算过程如下:

1
2
3
4
5
0000 0001
执行 & 运算
0000 0010
----------
0000 0000 = 0

因为 OP_WRITE 这个状态的二进制位,1 的位置处,STATUS 的二进制并没有对应的 1,而又因为其他位都是 0,导致全部归 0,计算出来的结果自然也就是 0 了。

这也就是为什么定义状态的数字中,都是 2 的次幂,因为它们的特点就是二进制只有一个为 1 的位,其他位都是 0,并同其他数位 1 的位不冲突。

移除已存储状态

现在知道了如何存储和判断状态,那么如何移除已经存储的状态呢?这时候就要用到非运算 ^ 。
假设 STATUS 中已经存储了 OP_CONNECT 这个状态,要把它从 STATUS 中移除,可以写:

1
STATUS = STATUS ^ OP_CONNECT

计算过程:

1
2
3
4
5
0000 1000
执行 ^ 运算
0000 1000
---------
0000 0000

可以见到 STATUS 中已经没有了 OP_CONNECT 的状态值了。