从 NIO SelectionKey 看按位运算符在权限设计中的运用

一、为什么要这样设计权限

 /**
     * Operation-set bit for read operations.
     *
     * <p> Suppose that a selection key's interest set contains
     * <tt>OP_READ</tt> at the start of a <a
     * href="Selector.html#selop">selection operation</a>.  If the selector
     * detects that the corresponding channel is ready for reading, has reached
     * end-of-stream, has been remotely shut down for further reading, or has
     * an error pending, then it will add <tt>OP_READ</tt> to the key's
     * ready-operation set and add the key to its selected-key&nbsp;set.  </p>
     */
    public static final int OP_READ = 1 << 0;

在刚接触到SelectionKey时实际上是有点晕的,好好的一个1,为什么要用1<<0来表示?读权限是1,为什么写权限又是4?(1<<2)。

为什么不用1来代表写权限,2来代表读权限,3代表只写,4代表只读,5代表可读可写呢?实际上即使是这样,对计算机来说,也不是什么负担,只要文档写清楚,对人来说也不是很难看懂。

上面那种表示方式当然可以,但如果有10个权限,那么我们的文档会变得十分复杂,比如说如果我想要增、删、改、查、移权限,又该怎么表示?如果我不想要读权限了,只要增、改、查,又该怎么表示?

二、用二进制来表示权限

在查阅资料后发现这是一种巧妙的权限设计方式:

1<<0 代表 二进制的1向左移零位,也就是将 0000 0001 变成了 0000 0001,还是1
1<<2 代表 二进制的1向左移两位,也就是将 0000 0001 变成了 0000 0100,也就是4

从二进制的角度来判断有没有权限,比如说第一位是1,代表有读取权限,第三位是1,代表有写入权限。
那么 1<<0 就代表 只读(没有写),1<<2 就代表只写(不可读)

那么现在就很清晰明了 如果说 00000 分别代表增、删、改、查、移,那么 10010 ,意思就是拥有新增和查询的权限。

三、位运算的神奇之处

事情不是这样就完了!用二进制来表达权限后,再辅以位运算,我们的权限系统会如有神助。

最简单的一个需求,我想要给我自己新增一个修改的权限。

        if (permission.has(update)){
            return;
        } else {
            permission.add(update);
        }

搞定!10秒钟都不用。新需求:现在还需要添加一个读权限!

        if (permission.has(select)){
            return;
        } else {
            permission.add(select);
        }

轻松加愉快。

如果这样写代码的话,就算是糟蹋了当初设计这个权限系统的人的一番心血。

1、按位或 实现有则权限不变,无则添加权限

还说说增、删、改、查、移这五个权限,比如说现在我只有删除权限 0000 1000。
我想加一个查询权限,只需要 0000 1000 | 0000 0010

按位或,如其名,同位只要有一个是1,就取1。

0000 1000 |
0000 0010

得到结果 0000 1010,代表我同时拥有 2号 和 4号权限,即删、查权限。
这时候来了一个新需求,我要添加增、删、改、移这四个权限。也就是添加 0001 1101 这四个权限。

0000 1010 |
0001 1101

得到结果 0001 1111,我同时拥有了所有权限,按位或完美实现了 “有则不变,无则新增” 这个功能。

2、按位与 和 按位取反 实现有则删除,无则不变

比如说现在我的权限是 0001 1101,我想删除增加的权限(0001 0000)。

可以这样写:0001 1101 &~ 0001 0000

运算的意思是: 先将 0001 0000 取反,得到 1110 1111,
再进行与运算,按位与,也如其名,同位要都是1,才能是1。

0001 1101 &
1110 1111

得到结果 0000 1101,我的增加权限没了!只剩下删、改、移

如果我们要删除增、删、改( 0001 1100 ) 这三个权限呢?

一样,将 0001 1100 取反,得到 1110 0011。

1110 0011&
0000 1101

得到结果 0000 0001 ,也就是说,我只剩下移动的权限了。使用 &~ 按位与、按位取反,很容易的就实现了 “有则删除,无则不变” 的功能。

通过 与、或、取反,我们实现了增、删功能,那查询功能呢?

3、按位与,实现查询功能

比如说我们现在有权限:0001 1101,我想知道我是否有移动的权限,那么我可以按位与移动权限,即 0000 0001

按位与后得到 0000 0001,代表我有移动的权限,得到 0000 0000 则代表我没有移动的权限。

所以我们可以得知,判断是否拥有某个权限,或者拥有列举的所有权限,或者只需要以下代码:

boolean hasPer = (myPermission & comparePermission) == comparePermission 即可。

那如何判断有用列举的其中一个权限即可呢?

很简单….反过来想,如果没有所有的权限,那么肯定得到 0000 0000。

所以:boolean hasOneOfPer = (myPermission & comparePermission) != 0

四、二进制判断权限的实际应用

如果有接触过linux系统的小伙伴肯定听说过权限 777 、123、000、等。

实际上这个数字 0 - 7 就是二进制权限的一种设计。

7转换成二进制,就是 111,代表 rwx,分别是 读、写、执行。

777 的意思是,文件所有者、同组用户、其他组用户都拥有 读、写执行的权限。

776 呢? 6 转换为二进制 即是 110,代表文件所有者、同组用户、拥有 读、写执行的权限,而其他组用户只有 读、写权限,没有执行权限。

猜你喜欢

转载自blog.csdn.net/anurnomeru/article/details/81039300
NIO