1 Math类

1.1 概述

查看API文档,我们可以发现API文档中关于Math类的定义如下:

Math类所在包为java.lang包,因此在使用的时候不需要进行导包。并且Math类被final修饰了,因此该类是不能被继承的。

Math类包含执行基本数字运算的方法,我们可以使用Math类完成基本的数学运算。

在API文档中没有体现可用的构造方法,因此我们就不能直接通过new关键字去创建Math类的对象。同时我们发现Math类中的方法都是静态的,因此在使用的时候我们可以直接通过类名去调用。在Math类中定义了很多数学运算的方法,我们主要学习一些常见的方法。

1.2 常见方法

tips:重点讲解内容

常见方法介绍

我们要学习的Math的常见方法如下所示:

1
2
3
4
5
6
7
8
public static int abs(int a)					// 返回参数的绝对值
public static double ceil(double a) // 返回大于或等于参数的最小整数
public static double floor(double a) // 返回小于或等于参数的最大整数
public static int round(float a) // 按照四舍五入返回最接近参数的int类型的值
public static int max(int a,int b) // 获取两个int值中的较大值
public static int min(int a,int b) // 获取两个int值中的较小值
public static double pow (double a,double b) // 计算a的b次幂的值
public static double random() // 返回一个[0.0,1.0)的随机值

2 System类

2.1 概述

tips:了解内容

System类所在包为java.lang包,因此在使用的时候不需要进行导包。并且System类被final修饰了,因此该类是不能被继承的。

System包含了系统操作的一些常用的方法。比如获取当前时间所对应的毫秒值,再比如终止当前JVM等等。

在API文档中没有体现可用的构造方法,因此我们就不能直接通过new关键字去创建System类的对象。同时我们发现System类中的方法都是静态的,因此在使用的时候我们可以直接通过类名去调用。

2.2 常见方法

tips:重点讲解内容

常见方法介绍

我们要学习的System类中的常见方法如下所示:

1
2
3
public static long currentTimeMillis()			// 获取当前时间所对应的毫秒值(当前时间为0时区所对应的时间即就是英国格林尼治天文台旧址所在位置)
public static void exit(int status) // 终止当前正在运行的Java虚拟机,0表示正常退出,非零表示异常退出
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); // 进行数值元素copy

案例演示

接下来我们就来通过一些案例演示一下这些方法的特点。

案例1:演示currentTimeMillis方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SystemDemo01 {

public static void main(String[] args) {

// 获取当前时间所对应的毫秒值
long millis = System.currentTimeMillis();

// 输出结果
System.out.println("当前时间所对应的毫秒值为:" + millis);

}

}

运行程序进行测试,控制台的输出结果如下:

1
当前时间所对应的毫秒值为:1576050298343

获取到当前时间的毫秒值的意义:我们常常来需要统计某一段代码的执行时间。此时我们就可以在执行这段代码之前获取一次时间,在执行完毕以后再次获取一次系统时间,然后计算两个时间的差值,这个差值就是这段代码执行完毕以后所需要的时间。

案例2:演示exit方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SystemDemo01 {

public static void main(String[] args) {

// 输出
System.out.println("程序开始执行了.....");

// 终止JVM
System.exit(0);

// 输出
System.out.println("程序终止了..........");

}

}

运行程序进行测试,控制台输出结果如下:

1
程序开始执行了.....

此时可以看到在控制台只输出了”程序开始了…”,由于JVM终止了,因此输出”程序终止了…”这段代码没有被执行。

3 Runtime

3.1 概述

​ Runtime表示Java中运行时对象,可以获取到程序运行时设计到的一些信息

3.2 常见方法

常见方法介绍

我们要学习的Object类中的常见方法如下所示:

1
2
3
4
5
6
7
public static Runtime getRuntime()		//当前系统的运行环境对象
public void exit(int status) //停止虚拟机
public int availableProcessors() //获得CPU的线程数
public long maxMemory() //JVM能从系统中获取总内存大小(单位byte)
public long totalMemory() //JVM已经从系统中获取总内存大小(单位byte)
public long freeMemory() //JVM剩余内存大小(单位byte)
public Process exec(String command) //运行cmd命令

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RunTimeDemo1 {
public static void main(String[] args) throws IOException {
//运行cmd命令
//shutdown :关机
//加上参数才能执行
//-s :默认在1分钟之后关机
//-s -t 指定时间 : 指定关机时间
//-a :取消关机操作
//-r: 关机并重启
Runtime.getRuntime().exec("shutdown -s -t 3600");


}
}

4 Object类

4.1 概述

tips:重点讲解内容

Object类所在包是java.lang包。Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类;换句话说,该类所具备的方法,其他所有类都继承了。

查看API文档我们可以看到,在Object类中提供了一个无参构造方法,但是一般情况下我们很少去主动的创建Object类的对象,调用其对应的方法。更多的是创建Object类的某个子类对象,然后通过子类对象调用Object类中的方法。

4.2 常见方法

tips:重点讲解内容

常见方法介绍

我们要学习的Object类中的常见方法如下所示:

1
2
3
public String toString()				//返回该对象的字符串表示形式(可以看做是对象的内存地址值)
public boolean equals(Object obj) //比较两个对象地址值是否相等;true表示相同,false表示不相同
protected Object clone() //对象克隆

案例演示

案例1 toString方法

我们可以查看一下Object类中toString方法的源码,如下所示:

1
2
3
public String toString() {		// Object类中toString方法的源码定义
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

我们常常将这个返回值称之为对象的内存地址值。但是一般情况下获取对象的内存地址值没有太大的意义。获取对象的成员变量的字符串拼接形式才算有意义。

直接输出一个对象,那么会默认调用对象的toString方法,因此下面的代码是等效的:

1
2
3
4
5
// 创建学生对象
Student s1 = new Student("itheima" , "14") ;

// 调用s1的toString方法,把结果进行输出
System.out.println(s1.toString());

小结

  1. 在通过输出语句输出一个对象时,默认调用的就是toString()方法
  2. 输出地址值一般没有意义,我们可以通过重写toString方法去输出对应的成员变量信息
  3. toString方法的作用:以良好的格式,更方便的展示对象中的属性值
  4. 一般情况下Jdk所提供的类都会重写Object类中的toString方法

案例2:演示equals方法

在如下代码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ObjectDemo {

public static void main(String[] args) {

// 创建两个学生对象
Student s1 = new Student("itheima" , "14") ;
Student s2 = new Student("itheima" , "14") ;

// 比较两个对象是否相等
System.out.println(s1 == s2);

}

}

运行程序进行测试,控制台的输出结果如下所示:

1
false

因为”==”号比较的是对象的地址值,而我们通过new关键字创建了两个对象,它们的地址值是不相同的。因此比较结果就是false。

我们尝试调用Object类中的equals方法进行比较,代码如下所示:

1
2
3
4
5
// 调用equals方法比较两个对象是否相等
boolean result = s1.equals(s2);

// 输出结果
System.out.println(result);

运行程序进行测试,控制台的输出结果为:

1
false

为什么结果还是false呢?我们可以查看一下Object类中equals方法的源码,如下所示:

1
2
3
public boolean equals(Object obj) {		// Object类中的equals方法的源码
return (this == obj);
}

通过源码我们可以发现默认情况下equals方法比较的也是对象的地址值。比较内存地址值一般情况下是没有意义的,我们希望比较的是对象的属性,如果两个对象的属性相同,我们认为就是同一个对象;

那么要比较对象的属性,我们就需要在Student类中重写Object类中的equals方法。

1
2
3
4
5
6
7
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age); // 比较的是对象的name属性值和age属性值
}

重写完毕以后运行程序进行测试,控制台输出结果如下所示:

1
true

此时equals方法比较的是对象的成员变量值,而s1和s2两个对象的成员变量值都是相同的。因此比较完毕以后的结果就是true。

小结

  1. 默认情况下equals方法比较的是对象的地址值
  2. 比较对象的地址值是没有意义的,因此一般情况下我们都会重写Object类中的equals方法

案例2:对象克隆

​ 把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制

对象克隆的分类:

深克隆和浅克隆

浅克隆:

​ 不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来

​ 基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值。

​ Object类默认的是浅克隆

浅克隆

深克隆:

​ 基本数据类型拷贝过来,字符串复用,引用数据类型会重新创建新的

深克隆

5 Objects类

5.1 概述

tips:了解内容

Objects类所在包是在java.util包下,因此在使用的时候需要进行导包。并且Objects类是被final修饰的,因此该类不能被继承。

Objects类提供了一些对象常见操作的方法。比如判断对象是否相等,判断对象是否为null等等。

Objects类中无无参构造方法,因此我们不能使用new关键字去创建Objects的对象。同时我们可以发现Objects类中所提供的方法都是静态的。因此我们可以通过类名直接去调用这些方法。

5.2 常见方法

tips:重点讲解内容

常见方法介绍

我们要重点学习的Objects类中的常见方法如下所示:

1
2
3
4
public static String toString(Object o) 					// 获取对象的字符串表现形式
public static boolean equals(Object a, Object b) // 比较两个对象是否相等
public static boolean isNull(Object obj) // 判断对象是否为null
public static boolean nonNull(Object obj) // 判断对象是否不为null

我们要了解的Objects类中的常见方法如下所示:

1
2
3
public static <T> T requireNonNull(T obj)					// 检查对象是否不为null,如果为null直接抛出异常;如果不是null返回该对象;
public static <T> T requireNonNullElse(T obj, T defaultObj) // 检查对象是否不为null,如果不为null,返回该对象;如果为null返回defaultObj值
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier) // 检查对象是否不为null,如果不为null,返回该对象;如果 // 为null,返回由Supplier所提供的值

上述方法中的T可以理解为是Object类型。

6 BigInteger类

6.1 引入

​ 平时在存储整数的时候,Java中默认是int类型,int类型有取值范围:-2147483648 ~ 2147483647。如果数字过大,我们可以使用long类型,但是如果long类型也表示不下怎么办呢?

​ 就需要用到BigInteger,可以理解为:大的整数。

​ 有多大呢?理论上最大到42亿的21亿次方

​ 基本上在内存撑爆之前,都无法达到这个上限。

6.2 概述

BigInteger所在包是在java.math包下,因此在使用的时候就需要进行导包。我们可以使用BigInteger类进行大整数的计算

6.3 常见方法

构造方法

1
2
3
4
5
6
public BigInteger(int num, Random rnd) 		//获取随机大整数,范围:[0 ~ 2的num次方-1]
public BigInteger(String val) //获取指定的大整数
public BigInteger(String val, int radix) //获取指定进制的大整数

下面这个不是构造,而是一个静态方法获取BigInteger对象
public static BigInteger valueOf(long val) //静态方法获取BigInteger的对象,内部有优化

构造方法小结:

  • 如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取。
  • 如果BigInteger表示的超出long的范围,可以用构造方法获取。
  • 对象一旦创建,BigInteger内部记录的值不能发生改变。
  • 只要进行计算都会产生一个新的BigInteger对象

常见成员方法

BigDecimal类中使用最多的还是提供的进行四则运算的方法,如下:

1
2
3
4
5
6
7
8
9
public BigInteger add(BigInteger val)					//加法
public BigInteger subtract(BigInteger val) //减法
public BigInteger multiply(BigInteger val) //乘法
public BigInteger divide(BigInteger val) //除法
public BigInteger[] divideAndRemainder(BigInteger val) //除法,获取商和余数
public boolean equals(Object x) //比较是否相同
public BigInteger pow(int exponent) //次幂、次方
public BigInteger max/min(BigInteger val) //返回较大值/较小值
public int intValue(BigInteger val) //转为int类型整数,超出范围数据有误

代码实现:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.itheima.a06bigintegerdemo;

import java.math.BigInteger;

public class BigIntegerDemo1 {
public static void main(String[] args) {
/*
public BigInteger(int num, Random rnd) 获取随机大整数,范围:[0~ 2的num次方-11
public BigInteger(String val) 获取指定的大整数
public BigInteger(String val, int radix) 获取指定进制的大整数

public static BigInteger valueOf(long val) 静态方法获取BigInteger的对象,内部有优化

细节:
对象一旦创建里面的数据不能发生改变。
*/


//1.获取一个随机的大整数
/* Random r=new Random();
for (int i = e; i < 100; i++) {
BigInteger bd1 = new BigInteger(4,r);
System.out.println(bd1);//[@ ~ 15]}
}
*/

//2.获取一个指定的大整数,可以超出long的取值范围
//细节:字符串中必须是整数,否则会报错
/* BigInteger bd2 = new BigInteger("1.1");
System.out.println(bd2);
*/

/*
BigInteger bd3 = new BigInteger("abc");
System.out.println(bd3);
*/

//3.获取指定进制的大整数
//细节:
//1.字符串中的数字必须是整数
//2.字符串中的数字必须要跟进制吻合。
//比如二进制中,那么只能写日和1,写其他的就报错。
BigInteger bd4 = new BigInteger("123", 2);
System.out.println(bd4);

//4.静态方法获取BigInteger的对象,内部有优化
//细节:
//1.能表示范围比较小,只能在long的取值范围之内,如果超出long的范围就不行了。
//2.在内部对常用的数字: -16 ~ 16 进行了优化。
// 提前把-16~16 先创建好BigInteger的对象,如果多次获取不会重新创建新的。
BigInteger bd5 = BigInteger.valueOf(16);
BigInteger bd6 = BigInteger.valueOf(16);
System.out.println(bd5 == bd6);//true


BigInteger bd7 = BigInteger.valueOf(17);
BigInteger bd8 = BigInteger.valueOf(17);
System.out.println(bd7 == bd8);//false


//5.对象一旦创建内部的数据不能发生改变
BigInteger bd9 =BigInteger.valueOf(1);
BigInteger bd10 =BigInteger.valueOf(2);
//此时,不会修改参与计算的BigInteger对象中的值,而是产生了一个新的BigInteger对象记录
BigInteger result=bd9.add(bd10);
System.out.println(result);//3

}
}

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.itheima.a06bigintegerdemo;

import java.math.BigInteger;

public class BigIntegerDemo2 {
public static void main(String[] args) {
/*
public BigInteger add(BigInteger val) 加法
public BigInteger subtract(BigInteger val) 减法
public BigInteger multiply(BigInteger val) 乘法
public BigInteger divide(BigInteger val) 除法,获取商
public BigInteger[] divideAndRemainder(BigInteger val) 除法,获取商和余数
public boolean equals(Object x) 比较是否相同
public BigInteger pow(int exponent) 次幂
public BigInteger max/min(BigInteger val) 返回较大值/较小值
public int intValue(BigInteger val) 转为int类型整数,超出范围数据有误
*/

//1.创建两个BigInteger对象
BigInteger bd1 = BigInteger.valueOf(10);
BigInteger bd2 = BigInteger.valueOf(5);

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

//3.除法,获取商和余数
BigInteger[] arr = bd1.divideAndRemainder(bd2);
System.out.println(arr[0]);
System.out.println(arr[1]);

//4.比较是否相同
boolean result = bd1.equals(bd2);
System.out.println(result);

//5.次幂
BigInteger bd4 = bd1.pow(2);
System.out.println(bd4);

//6.max
BigInteger bd5 = bd1.max(bd2);


//7.转为int类型整数,超出范围数据有误
/* BigInteger bd6 = BigInteger.valueOf(2147483647L);
int i = bd6.intValue();
System.out.println(i);
*/

BigInteger bd6 = BigInteger.valueOf(200);
double v = bd6.doubleValue();
System.out.println(v);//200.0
}
}

6.4 底层存储方式:

对于计算机而言,其实是没有数据类型的概念的,都是0101010101,数据类型是编程语言自己规定的,所以在实际存储的时候,先把具体的数字变成二进制,每32个bit为一组,存储在数组中。

数组中最多能存储元素个数:21亿多

数组中每一位能表示的数字:42亿多

理论上,BigInteger能表示的最大数字为:42亿的21亿次方。

但是还没到这个数字,电脑的内存就会撑爆,所以一般认为BigInteger是无限的。

存储方式如图所示:

bigInteger的底层原理

7 BigDecimal类

7.1 引入

首先我们来分析一下如下程序的执行结果:

1
2
3
4
5
6
7
public class BigDecimalDemo01 {

public static void main(String[] args) {
System.out.println(0.09 + 0.01);
}

}

这段代码比较简单,就是计算0.09和0.01之和,并且将其结果在控制台进行输出。那么按照我们的想法在控制台输出的结果应该为0.1。那么实际的运行结果是什么呢?我们来运行一下程序,控制台的输出

结果如下所示:

1
0.09999999999999999

这样的结果其实就是一个丢失精度的结果。为什么会产生精度丢失呢?

在使用float或者double类型的数据在进行数学运算的时候,很有可能会产生精度丢失问题。我们都知道计算机底层在进行运算的时候,使用的都是二进制数据; 当我们在程序中写了一个十进制数据 ,在进行运算的时候,计算机会将这个十进制数据转换成二进制数据,然后再进行运算,计算完毕以后计算机会把运算的结果再转换成十进制数据给我们展示; 如果我们使用的是整数类型的数据进行计算,那

么在把十进制数据转换成二进制数据的时候不会存在精度问题; 如果我们的数据是一个浮点类型的数据,有的时候计算机并不会将这个数据完全转换成一个二进制数据,而是将这个将其转换成一个无限的趋近于这个十进数的二进制数据; 这样使用一个不太准确的数据进行运算的时候, 最终就会造成精度丢失;为了提高精度,Java就给我们提供了BigDecimal供我们进行数据运算。

7.2 概述

BigDecimal所在包是在java.math包下,因此在使用的时候就需要进行导包。我们可以使用BigDecimal类进行更加精准的数据计算。

7.3 常见方法

构造方法

要用BigDecimal类,那么就需要首先学习一下如何去创建BigDecimal的对象。通过查看API文档,我们可以发现Jdk中针对BigDecimal类提供了很多的构造方法,但是最常用的构造方法是:

1576134383441

了解完常见的构造方法以后,我们接下来就重点介绍一下常见的成员方法。

常见成员方法

BigDecimal类中使用最多的还是提供的进行四则运算的方法,如下:

1
2
3
4
public BigDecimal add(BigDecimal value)				// 加法运算
public BigDecimal subtract(BigDecimal value) // 减法运算
public BigDecimal multiply(BigDecimal value) // 乘法运算
public BigDecimal divide(BigDecimal value) // 触发运算

接下来我们就来通过一些案例演示一下这些成员方法的使用。

案例1:演示基本的四则运算

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BigDecimalDemo01 {

public static void main(String[] args) {

// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("0.3") ;
BigDecimal b2 = new BigDecimal("4") ;

// 调用方法进行b1和b2的四则运算,并将其运算结果在控制台进行输出
System.out.println(b1.add(b2)); // 进行加法运算
System.out.println(b1.subtract(b2)); // 进行减法运算
System.out.println(b1.multiply(b2)); // 进行乘法运算
System.out.println(b1.divide(b2)); // 进行除法运算

}

}

运行程序进行测试,控制台输出结果如下:

1
2
3
4
4.3
-3.7
1.2
0.075

此时我们可以看到使用BigDecimal类来完成浮点数的计算不会存在损失精度的问题。

案例2:演示除法的特殊情况

如果使用BigDecimal类型的数据进行除法运算的时候,得到的结果是一个无限循环小数,那么就会报错:ArithmeticException。 如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BigDecimalDemo02 {

public static void main(String[] args) {

// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("1") ;
BigDecimal b2 = new BigDecimal("3") ;

// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2));

}

}

运行程序进行测试,控制台输出结果如下所示:

1
2
3
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1716)
at com.itheima.api.bigdecimal.demo02.BigDecimalDemo02.main(BigDecimalDemo02.java:14)

针对这个问题怎么解决,此时我们就需要使用到BigDecimal类中另外一个divide方法,如下所示:

1
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

上述divide方法参数说明:

1
2
3
4
5
divisor:			除数对应的BigDecimal对象;
scale: 精确的位数;
roundingMode: 取舍模式;
取舍模式被封装到了RoundingMode这个枚举类中(关于枚举我们后期再做重点讲解),在这个枚举类中定义了很多种取舍方式。最常见的取舍方式有如下几个:
UP(直接进1) , FLOOR(直接删除) , HALF_UP(4舍五入),我们可以通过如下格式直接访问这些取舍模式:枚举类名.变量名

接下来我们就来演示一下这些取舍模式,代码如下所示:

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
37
38
39
40
41
42
43
44
45
46
public class BigDecimalDemo02 {

public static void main(String[] args) {

// 调用方法
method_03() ;

}

// 演示取舍模式HALF_UP
public static void method_03() {

// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("0.3") ;
BigDecimal b2 = new BigDecimal("4") ;

// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2 , 2 , RoundingMode.HALF_UP));

}

// 演示取舍模式FLOOR
public static void method_02() {

// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("1") ;
BigDecimal b2 = new BigDecimal("3") ;

// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2 , 2 , RoundingMode.FLOOR));

}

// 演示取舍模式UP
public static void method_01() {

// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("1") ;
BigDecimal b2 = new BigDecimal("3") ;

// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2 , 2 , RoundingMode.UP));

}

}

小结:后期在进行两个数的除法运算的时候,我们常常使用的是可以设置取舍模式的divide方法。

7.4 底层存储方式:

把数据看成字符串,遍历得到里面的每一个字符,把这些字符在ASCII码表上的值,都存储到数组中。

bigdecimal存储原理