java常用API(二)

一、BigInteger类

1.定义

BigInteger可以表示非常大的数,虽然也有上限,但是这个上限可以看作是无穷大的。

2.方法 

(1)构造方法

① public BigInteger(int num, Random r)
public class Demo {
    public static void main(String[] args) {
        //1.public BigInteger(int num, Random r)  获取一个随机的大整数
        Random r = new Random();
        BigInteger bd1 = new BigInteger(4, r);//获取[0,2^4)中的随机整数
        System.out.println(bd1);
    }
}
②  public BigInteger(String val)
public class Demo {
    public static void main(String[] args) {
        //2.public BigInteger(String val)  获取一个指定的大整数
        BigInteger bd2=new BigInteger("99999999999999999999999999");
        System.out.println(bd2);//99999999999999999999999999
    }
}

注:字符串必须是整数,否则会报错

 ③ public BigInteger(String val, int radix)
public class Demo {
    public static void main(String[] args) {
        //3.public BigInteger(String val, int radix)  获取指定进制的大整数
        BigInteger bd4=new BigInteger("100",2);
        System.out.println(bd4);//4(二进制的100转换成十进制就是4)
    }
}

 注:字符串中的数字必须要和进制相吻合(比如二进制,只有0和1,不能有其他数字)

(2)静态方法

public class Demo {
    public static void main(String[] args) {
        BigInteger bd1 = BigInteger.valueOf(100);
        System.out.println(bd1);//100
    }
}

 注:

① 能表示范围较小,只能在long的取值范围之内,如果超出long的范围就不行了

 通过源码可以发现,valueof 方法的形参就是 long类型的。

这里因为已经超出了Int的最大表示范围,所以数字末尾要加上L。

long 类型的最大值输出正常,+1之后就会报错

**************************************************************************************************************

② 在内部对常用的数字(-16 ~ 16)进行了优化,提前把这些常用数字创建号BigInteger的对象,如果多次获取不会重新创建新的对象。

public class Demo {
    public static void main(String[] args) {

        BigInteger bd1 = BigInteger.valueOf(16);
        BigInteger bd2 = BigInteger.valueOf(16);
        System.out.println(bd1 == bd2);//true

        BigInteger bd3 = BigInteger.valueOf(17);
        BigInteger bd4 = BigInteger.valueOf(17);
        System.out.println(bd3 == bd4);//false
    }
}

bd1和bd2都是常用数字16,指向同一个 BigInteger对象,所以地址值相同,结果为true

而bd3和bd4是17,多次获取会创建新的BigInteger对象,所以地址值不同,结果为false

Question:为什么是16,怎么实现的呢? 

在valueof 方法内部,会先进行判断。

判断值是否为0,是小于 MAX_CONSTANT,还是大于 -MAX_CONSTANT

在 BigInteger 类的成员变量中,可以得知 MAX_CONSTANT 的值为16

此外,成员变量中也创建了两个数组 posConst 和 negConst

在BigInteger 类的静态代码块中,可知:

posConst 用于存放 1 ~ 16 的正整数 BigInteger 对象

negConst 用于存放 -1 ~ -16 的负整数 BigInteger 对象

那么0呢?

BigInteger 类还有一个静态变量叫做 ZERO ,专门存放 0 的BigInteger 对象

这时再回头看valueof 方法,就会很清晰了

传入的形参为0,就返回ZERO

为正数且<=16,就返回 posConst 数组中对应的 BigInteger 对象

为负数且>=-16,就返回 negConst 数组中对应的 BigInteger 对象

否则,再创建新的 BigInteger 对象

(3)成员方法

 ① 加减乘除、余
public class Demo {
    public static void main(String[] args) {
        //创建两个BigInteger对象
        BigInteger  bd1=BigInteger.valueOf(10);
        BigInteger bd2=BigInteger.valueOf(5);

        //1.add  加法
        BigInteger bd3=bd1.add(bd2);
        System.out.println(bd3);//15

        //2.subtract  减法
        BigInteger bd4=bd1.subtract(bd2);
        System.out.println(bd4);//5

        //3.multiply  乘法
        BigInteger bd5=bd1.multiply(bd2);
        System.out.println(bd5);//50

        //4.divide  除法
        BigInteger bd6=bd1.divide(bd2);
        System.out.println(bd6);//2

        //5.获取商和余数
        BigInteger[] arr=bd1.divideAndRemainder(bd2);
        System.out.println(arr[0]);//商:2
        System.out.println(arr[1]);//余数:0

        //验证bd1和bd2的值是否发生改变
        System.out.println(bd1);//10
        System.out.println(bd2);//5
    }
}
② equals方法

BigInteger类也重写了Object类的equals方法,可以比较对象内部的数据是否相同

public class Demo {
    public static void main(String[] args) {
        //创建两个BigInteger对象
        BigInteger bd1=BigInteger.valueOf(10);
        BigInteger bd2=BigInteger.valueOf(5);
        BigInteger bd3=BigInteger.valueOf(10);

        //equals  比较是否相等
        boolean result1=bd1.equals(bd2);
        boolean result2=bd1.equals(bd3);
        System.out.println(result1);//false
        System.out.println(result2);//true
    }
}
③ pow方法
public class Demo4 {
    public static void main(String[] args) {
        //创建两个BigInteger对象
        BigInteger bd1 = BigInteger.valueOf(10);
        BigInteger bd2 = BigInteger.valueOf(5);

        //pow  次幂
        //注意次幂只能传int数据,不能传BigInteger对象
        BigInteger bd3 = bd1.pow(2);
        System.out.println(bd3);//100

        //验证bd1和bd2的值是否发生改变
        System.out.println(bd1);//10
        System.out.println(bd2);//5
    }
}

细节:pow()方法中的实参只能传int数据,不能传BigInteger对象,否则报错

************************************************************************************************************** 

结论:

BigInteger对象一旦创建,内部记录的只将不能在发生改变。

只要进行计算都会产生一个新的BigInteger对象。

④ max/min方法
public class Demo{
    public static void main(String[] args) {
        //创建两个BigInteger对象
        BigInteger bd1 = BigInteger.valueOf(10);
        BigInteger bd2 = BigInteger.valueOf(5);
        BigInteger bd3 = BigInteger.valueOf(10)

        //max/min  返回较大值/较小值 --> 没有创建新对象,而是将结果对象返回
        BigInteger bd5 = bd1.max(bd2);
        System.out.println(bd1 == bd5);//true
        System.out.println(bd1 == bd2);//false

    }
}

细节:这个方法比较特殊,没有创建新对象,而是将结果对象返回。

⑤ intValue方法
public class Demo4 {
    public static void main(String[] args) {
        //intValue  转为int类型数据,超出范围数据有误
        BigInteger bd1=BigInteger.valueOf(2147483647L);
        BigInteger bd2=BigInteger.valueOf(2147483648L);

        int x1=bd1.intValue();
        int x2=bd2.intValue();

        System.out.println(x1);//2147483647
        System.out.println(x2);//-2147483648
    }
}

可以发现,当BigInteger对象中的数据超出 int 类型的最大表示范围时,intValueg方法的结果就会产生错误。

如果想要正确返回数据,可以使用对应的转换方法,比如 longValue、doubleValue

3.底层存储方式

 对于任意一个数,比如 27670116110564327424

① 首先将它转换成二进制,然后从右往左,每32位为一组

② 每组再各自转换成对应的十进制

③ 再按照顺序存储到一个数组中

对于 27670116110564327424 和 -27670116110564327424

 可以看出,正负号由一个专门的变量signum存储,数据存放的数组为mag,所以两个mag数组都是 [1, -2147483648, 0]

**************************************************************************************************************

question:为什么说 BigInteger 可以表示非常大的数据,甚至接近于无穷大?

由上可知,mag数组的每一个元素都可以表示32位的数据,范围为:-2147483648 ~ 2147483647

而数组的最大长度是 int 的最大值:2147483647

所以说,BigInteger能表示的最大数字:42亿的21亿次方之多

但不可能有一台电脑的内存能支撑的起这么大的数据,所以说 BigInteger表示的数据可以看作是无穷大!

二、BigDecimal类

1.引言

对于以下代码,结果如何呢?

运行结果:

可以发现,运算结果均发生错误,这是为什么呢?

★★★★★★★★★★★★★★★★★    小数在计算机中的存储方式    ★★★★★★★★★★★★★★★★★

计算机中不论是存储还是运算,都是将数据转换成二进制来进行操作的

对于任意一个浮点数,转成二进制会有整数和小数两部分

 整数部分的次幂从0开始,小数部分的次幂从-1开始

那么对于0.9和0.226,二进制如何表示呢?

然而,对于 float 类型和 double 类型,所能够表示的小数部分的 bit 位数分别是23和52

因为0.226已经超出了 double 的小数部分最大表示范围,在这种情况下进行运算,必然会造成精度丢失。

然而,在现实中很多涉及金钱的地方,都需要小数的精确计算,BigDecimal类 能够解决这个问题。

2.定义

细节:

① BigDecimal 类和BigInteger 类一样,一旦创建对象就不可再改变。

② 不论进行何种运算,对象都不会在改变了,而是创建一个新的对象。

 ③ BigDecimal 类可以表示任意精度的有符号十进制数

3.方法

(1)构造方法
① public BigDecimal(double val)

 该方法接受一个double类型的值作为参数,但是由于double类型使用二进制来表示小数。

有些小数在二进制中是无限循环小数,不能精确表示(例如 0.1).

因此,当将double类型的值转换为BigDecimal时,可能会出现精度损失,导致值不精确。

public class Demo1 {
    public static void main(String[] args) {
        //通过传递double类型的小数来创建对象
        BigDecimal bd1 = new BigDecimal(0.1);
        BigDecimal bd2 = new BigDecimal(0.9);
        System.out.println(bd1);
        System.out.println(bd2);
    }
}

运行结果:

结论:这种方式有可能是不精确的,所以不建议使用 

② public BigDecimal(String val)

该方法接受一个字符串作为参数,可以确保数值的精确性,因为字符串本身保存了数值的精确表示。

在创建对象时,参数中的字符串会被解析并转换为一个准确的数值表示,不会出现精度损失。

public class Demo1 {
    public static void main(String[] args) {
        //通过传递字符串表示的小数来创建对象
        BigDecimal bd1 = new BigDecimal("0.1");
        BigDecimal bd2 = new BigDecimal("0.9");
        BigDecimal bd3 = bd1.add(bd2);

        System.out.println(bd1);
        System.out.println(bd2);
        System.out.println(bd3);
    }
}

运行结果:

结论:首选方法,因为它不会遇到 BigDecimal(double val) 构造方法的不可预知问题。

(2)静态方法

public class Demo2 {
    public static void main(String[] args) {
        BigDecimal bd1 = BigDecimal.valueOf(10);
        BigDecimal bd2 = BigDecimal.valueOf(0.1);
        
        System.out.println(bd1);//10
        System.out.println(bd2);//0.1
    }
}

question:为什么这里 0.1输出成功了呢?

通过源码可以看出,这里首先将double类型的 0.1 转换成了字符串,再创建BigDecimal 对象 ,等同于调用了public BigDecimal(String val)构造方法,所以结果是精确的。

结论:

注意这里静态方法的形参是double类型,所以

① 如果要表示的数组不大,没有超出double的取值范围,建议使用静态方法

② 如果要表示的数组比较大,超出了double的取值范围,建议使用构造方法

question:BigDecimal 的valueOf 方法是否像BigInteger 的valueOf 方法一样,也进行了内部优化呢?

通过源码可以发现,当形参是 long 类型时,也先进行了if 判断。

判断这个 val 是否小于ZERO_THROUGH_TEN 的长度,那这个ZERO_THROUGH_TEN 是什么呢?

在该类的静态变量里,发现 ZERO_THROUGH_TEN 是一个事先定义好的 BigDecimal 对象数组,从0到10,共11个,所以 ZERO_THROUGH_TEN.length等于11

再回来看 valueOf 方法,会发现:

首先判断 val 值是否在 0 ~ 10之间,是则返回对应的已经创建好的对象

如果是其他情况,则重新创建对象

public class Demo2 {
    public static void main(String[] args) {
        BigDecimal bd1 = BigDecimal.valueOf(10);
        BigDecimal bd2 = BigDecimal.valueOf(10);

        BigDecimal bd3 = BigDecimal.valueOf(10.0);
        BigDecimal bd4 = BigDecimal.valueOf(10.0);

        System.out.println(bd1 == bd2);//true
        System.out.println(bd3 == bd4);//false
    }
}

可以发现,由于 10 的BigDecimal对象已经创建,所以bd1和bd2指向同一个对象,地址值是相等的

而由上可知,实参时10.0,也就是形参是double类型时,是创建了一个新的对象,所以bd3和bd4是不相等的

结论:如果传递的是[0,10]之间的整数,那么方法会返回已经创建好的对象,不会重新new

(3)成员方法

 ① 加减乘除
public class Demo3 {
    public static void main(String[] args) {
        BigDecimal bd1 = BigDecimal.valueOf(10.0);
        BigDecimal bd2 = BigDecimal.valueOf(2.0);

        //1.add  加法
        BigDecimal bd3 = bd1.add(bd2);
        System.out.println(bd3);//12.0

        //2.subtract  减法
        BigDecimal bd4 = bd1.subtract(bd2);
        System.out.println(bd4);//8.0

        //3.multiply  乘法
        BigDecimal bd5 = bd1.multiply(bd2);
        System.out.println(bd5);//20.00

        //4.divide  除法
        BigDecimal bd6 = bd1.divide(bd2);
        System.out.println(bd6);//5
    }
}
② 除法

一般情况下,除法可以正常运算,但如果是除不尽的情况下,就会报错

public class Demo4 {
    public static void main(String[] args) {
        BigDecimal bd1 = BigDecimal.valueOf(10.0);
        BigDecimal bd2 = BigDecimal.valueOf(3.0);

        BigDecimal bd3= bd1.divide(bd2);
        System.out.println(bd3);
    }
}

运行结果:

在这中情况下,我们通常采用 divide 的重载方法 

 其中scale 表示要保留几位小数,RoundingMode表示舍入的方式,如下:

常用的舍入方式就是 HALF_UP,就是我们常说的四舍五入

public class Demo4 {
    public static void main(String[] args) {
        BigDecimal bd1 = BigDecimal.valueOf(10.0);
        BigDecimal bd2 = BigDecimal.valueOf(3.0);

        BigDecimal bd3 = bd1.divide(bd2, 2, RoundingMode.HALF_UP);
        System.out.println(bd3);//0.33
    }
}

运行结果:

4.底层存储方式

BigDecimal 的底层存储不同于 BigInteger 

对于 0.266 一个这么短的数,转换成二进制之后就高达55位。

那如果小数是成百上千位,在转换成二进制之后,就会非常长。

如果和BigInteger 一样,按每32位为一段进行划分,效率会非常低。

对于0.226,实际的存储方式是先将每一位拆分,再转换成其ASCII码,存储到数组中 

再看3个例子:

 通过Debug调试:

由于也是用数组存储,而数组的最大长度是 int 的最大值:2147483647

也不可能有任何一台电脑能容纳的下这么大的内存,所以 BigDecimal 能表示的数据也同样是能看作是无穷大的!

  • 46
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值