最近在修改公司之前的项目,在项目中遇到了权限校验的问题,代码中出现了BigInteger的setBit()testBit()方法,之前未接触过,所以了解了下BigInteger。

在Java中,由CPU原生提供的整型最大范围是64位long型整数。使用long型整数可以直接通过CPU指令进行计算,速度非常快。

如果我们使用的整数范围超过了long型怎么办?这个时候,就只能用软件来模拟一个大整数。java.math.BigInteger就是用来表示任意大小的整数。BigInteger内部用一个int[]数组来模拟一个非常大的整数:

BigInteger bi = new BigInteger("1234567890");
System.out.println(bi.pow(5)); // 2867971860299718107233761438093672048294900000

BigInteger做运算的时候,只能使用实例方法,例如,加法运算:

BigInteger i1 = new BigInteger("1234567890");
BigInteger i2 = new BigInteger("12345678901234567890");
BigInteger sum = i1.add(i2); // 12345678902469135780

long型整数运算比,BigInteger不会有范围限制,但缺点是速度比较慢。

也可以把BigInteger转换成long型:

BigInteger i = new BigInteger("123456789000");
System.out.println(i.longValue()); // 123456789000System.out.println(i.multiply(i).longValueExact()); // java.lang.ArithmeticException: BigInteger out of long range

使用longValueExact()方法时,如果超出了long型的范围,会抛出ArithmeticException

BigIntegerIntegerLong一样,也是不可变类,并且也继承自Number类。因为Number定义了转换为基本类型的几个方法:

      • 转换为bytebyteValue()

      • 转换为shortshortValue()

      • 转换为intintValue()

      • 转换为longlongValue()

      • 转换为floatfloatValue()

      • 转换为doubledoubleValue()

因此,通过上述方法,可以把BigInteger转换成基本类型。如果BigInteger表示的范围超过了基本类型的范围,转换时将丢失高位信息,即结果不一定是准确的。如果需要准确地转换成基本类型,可以使用intValueExact()longValueExact()等方法,在转换时如果超出范围,将直接抛出ArithmeticException异常。

知道了BigInteger的概念,运算也和之前的BigDecimal很像,第一次看到BigInteger,还是学的不够多啊 这样的知识居然现在才发现。

再说说setBit()和testBit()方法

在项目中是使用BigInteger的这两个方法来进行权限效验的,利用菜单id生成对应权限效验码,把具体的权限设置为一个正整数值,如果一个用户有多个权限的话,比如1,2权限,那么我们设置值的时候就是num.setBit(1),num.setBit(2),然后把返回的num值保存在session中,要验证是否有权限的话,只要从session中取得保存的num,然后执行下num.test(权限值),如果返回true就是有权限的,否则无权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
   /**
     * 利用BigInteger对权限进行2的权的和计算
     
     * @param rights String型权限编码数组
     * @return 2的权的和
     */
    public static BigInteger sumRights(String[] rights) {
        BigInteger num = new BigInteger("0");
        for (int i = 0; i < rights.length; i++) {
            num = num.setBit(Integer.parseInt(rights[i]));
        }
        return num;
    }
 
   /**
     * 测试是否具有指定编码的权限
     
     * @param sum
     * @param targetRights
     * @return
     */
    public static boolean testRights(String sum, int targetRights) {
        if (Tools.isEmpty(sum))
            return false;
        return testRights(new BigInteger(sum), targetRights);
    }
     
     
     public static void main(String[] args) {
        System.out.println(testRights(new BigInteger(
            "8148143905337944345073782753637512644205873574663745002544561797417525199053346824733589504"),
            302));
        BigInteger num = new BigInteger("0");
        num = num.setBit(302);
        System.out.println(num);
    }

testBit方法代码

1
2
3
4
5
6
public boolean testBit(int n) {
        if (n < 0)
            throw new ArithmeticException("Negative bit address");
 
        return (getInt(n >>> 5) & (1 << (n & 31))) != 0;
    }

意思就是将1左移n位,与this做&运算,其实就是判断当前数(要写成二进制)第n+1位上的数是不是为1,是的话返回true

setBit方法代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 public BigInteger setBit(int n) {
        if (n < 0)
            throw new ArithmeticException("Negative bit address");
 
        int intNum = n >>> 5;
        int[] result = new int[Math.max(intLength(), intNum+2)];
 
        for (int i=0; i < result.length; i++)
            result[result.length-i-1] = getInt(i);
 
        result[result.length-intNum-1] |= (1 << (n & 31));
 
        return valueOf(result);
    }

意思就是将1左移n位,与this对象做|运算,这样的话,就会将this的二进制格式的第n+1位的数变为1.这样很明显就和上一个方法形成一对,

n可以作为某个功能编号,而角色可以使用setBit的方法设置编号,然后使用testBit来测试是不是含有n编号的功能。

如果每次有添加多个新的功能,那么就用这些功能编号依次给原来的角色编号执行setBit得到新的角色编号。

利用这两个方法进行权限效验解析:

假设我的数据库总共有4个菜单                                      4个账户

id         菜单名称                                     id         菜单权限值                   转换为二进制

1           菜单1                                         1           6                                 1 1 0

2           菜单2                                         2           12                               1 1 0 0

3           菜单3                                         3           26                              1 1 0 1 0

4           菜单4                                          4           30                              1 1 1 1 0

假如账户A    有菜单1  菜单2  的权限,       权限值=2^1+2^2=6

账户B    有菜单2  菜单3 的权限,       权限值=2^2+2^3=12

账户C    有菜单1  菜单3  菜单4 的权限,       权限值=2^1+2^3+2^4=26

账户D    有菜单1  菜单2  菜单3  菜单4   的权限,      权限值=2^1+2^2+2^3+2^4=30

大家可以观察一下这些权限值转换为二进制数后的规律(假如把这些二进制数从右往左转换成一个bolean数组,1 代表 false 2 代表true),看上图的转换后的二进制,我们来看这个数组

0                 1                     2                    3                      4

账户A              F                 T                     T                     F                     F

账户B              F                 F                     T                     T                     F

账户C              F                 T                     F                     T                     T

账户D              F                 T                     T                     T                     T

把上面的  1  2  3  4  看成是菜单ID,T 和 F看成表示是否有该菜单权限,你们应该能发现其中的奥妙!(现在应该也可以说明为什么菜单ID必须为正整数了。。)

java中大整型BigInteger及setBit和testBit方法的更多相关文章

  1. JAVA 长整型转换为IP地址的方法

    JAVA 长整型转换为IP地址的方法 代码例如以下: /** * 整型解析为IP地址 * @param num * @return */ public static String int2iP(Lon ...

  2. (转)JAVA的整型与字符串相互转换

    JAVA的整型与字符串相互转换1如何将字串 String 转换成整数 int? A. 有两个方法: 1). int i = Integer.parseInt([String]); 或         ...

  3. 深入理解Java的整型类型:如何实现2+2=5?

    先看下这段神奇的Java代码: public static void main(String[] args) throws Exception { doSomethingMagic(); System ...

  4. 为什么阿里巴巴Java开发手册中强制要求整型包装类对象值用 equals 方法比较?

    在阅读<阿里巴巴Java开发手册>时,发现有一条关于整型包装类对象之间值比较的规约,具体内容如下: 这条建议非常值得大家关注, 而且该问题在 Java 面试中十分常见. 还需要思考以下几个 ...

  5. JAVA的整型与字符串相互转换

    1如何将字串 String 转换成整数 int? A. 有两个方法: 1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([S ...

  6. Java关于整型类缓存[-128,127]之间的数字

    我们在学习Java的包装类Integer.Long的时候可能会遇到这个问题: ①Integer a = 500;// Integer a = Integer.valueOf(500); 等价于上面的 ...

  7. JAVA中整型的存储和左右移位运算

    byte,1个字节8位, -128 ~ 127之间,首位表示正负,0为正,1为负,0111,1111表示127,-127为127取反1000,0000,再加1,即1000,0001为-127,-127 ...

  8. java中整型、浮点型、char型扩展

    怎么区分是什么进制? 二进制:0b开头,eg: int i = 0b10; 八进制:0开头,eg: int k = 010; 十进制: 十六进制:0x开头,eg: int j = 0x10; 浮点数类 ...

  9. 一个封存Id与状态对应键值的神器,BigInteger的setBit和testBit用法实例

    1,首先描述一下应用场景 比如,我们要对菜单做权限,控制不同角色菜单显示与不显示,角色为经理时,我们需要菜单id为 4,7,13,24的菜单显示,别的菜单不显示. 就是说,这时候我们要把4,7,13, ...

随机推荐

  1. vuex状态管理安装方法

    1.可以启动vue ui 手动添加vuex. 或使用 cnpm install vuex 2.使用,import vuex from “vuex” vue.use(vuex) 3.安装插件, 首先键入 ...

  2. 三、进程和线程、协程在python中的使用

    三.进程和线程.协程在python中的使用 1.多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用.多进程的优势就是一个子 ...

  3. vue 列表的排序过渡 shuffle遇到的问题

    内部的实现,Vue 使用了一个叫 FLIP 简单的动画队列使用 transforms 将元素从之前的位置平滑过渡新的位置 需要注意的是使用 FLIP 过渡的元素不能设置为 display: inlin ...

  4. fenby C语言 P26

    指针 地址 类型+“*”+变量名=指针变量 int *p1;//指向整型变量的指针变量p1 float *p2;//指向浮点型变量的指针变量p2 char *p3;//指向字符型变量的指针变量p3 “ ...

  5. 你必须知道的容器监控 (3) Prometheus

    本篇已加入<.NET Core on K8S学习实践系列文章索引>,可以点击查看更多容器化技术相关系列文章.上一篇介绍了Google开发的容器监控工具cAdvisor,但是其提供的操作界面 ...

  6. 开源.Net Standard版华为物联网北向接口SDK

    最近用到了华为的物联网平台API,但是官方没有.Net版的SDK,所以就自己封装了一个,开源出来给有需要的朋友,同时也算是为.Net Core的发展做点小贡献~ 源码地址:https://github ...

  7. 第二十六章 system v消息队列(二)

    msgsnd int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 作用: 把一条消息添加到消息队列中 参数: msqi ...

  8. Java 干货之深入理解String

    可以证明,字符串操作是计算机程序设计中最常见的行为,尤其是在Java大展拳脚的Web系统中更是如此. ---<Thinking in Java> 提到Java中的String,总是有说不完 ...

  9. [py2neo]Ubuntu14 安装py2neo失败问题解决

    环境 1.操作系统Ubuntu14 2.py2neo版本4.1 3.python版本python3.4 问题 pip install py2neo==4.1  安装失败,提示: Cannot unin ...

  10. NOI导刊总结

    NOI导刊总结 前两天去郑州,参加了什么NOI导刊的培训,然后就发现大佬是真的多,还十分意外的发现了一个事,清华北大是不是发笔记本和耳机,为啥三个老师的都一模一样... 这几天主要以讲.NOIP知识点 ...