最近在修改公司之前的项目,在项目中遇到了权限校验的问题,代码中出现了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. 品优购(IDEA版)-第一天

    # 品优购(IDEA版)-第一天 品优购IDEA版应该是2019年的新项目.目前只有视频.资料其他都还是旧的. ## 1.学习目标 1:了解电商行业特点以及理解电商的模式 2:了解整体品优购的架构特点 ...

  2. @ConditionalOnProperty注解

    一 源码解析 查看ConditionalOnProperty的源码 package org.springframework.boot.autoconfigure.condition; import j ...

  3. Java8系列 (五) Optional类

    概述 在Java8之前, 如果需要对一个变量做一次 null 检查, 通常会像下面这样写 T t = service1.query(); if (t != null) { K k = service2 ...

  4. 写出float x 与“零值”比较的if语句——一道面试题分析

    写出float  x 与“零值”比较的if语句 请写出 float  x 与“零值”比较的 if 语句: const float EPSINON = 0.00001; if ((x >= - E ...

  5. ElasticSearch中文分词器-IK分词器的使用

    IK分词器的使用 首先我们通过Postman发送GET请求查询分词效果 GET http://localhost:9200/_analyze { "text":"农业银行 ...

  6. 在VMware下进行的Windows2008操作系统虚拟机的安装

    一.VMware虚拟机的安装 首先你需要拥有一款软件VMware,这是一款虚拟机安装软件.Vmware比起Vbox收费较贵,占用资源大,但是拥有大量的资源以及拥有克隆技术,适合新手学习使用,较为专业. ...

  7. 学习笔记62_python文件操作

    #使用 str = raw_input("输入密码:") 来获取键盘的输入 #创建或打开一个文件 fp = open("hellow.txt","w& ...

  8. 大公司喜欢问的Java集合类面试题

    Collection Collection是基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些Collection允许相同的元素而另一 ...

  9. Data Compression Category

    Data Compression is an approach to compress the origin dataset and save spaces. According to the Eco ...

  10. day 2上午 elect 选举 背包

    #include<iostream> using namespace std; int n; ; ]; long long p[maxn]; long long dp[maxn][maxn ...