1.API介绍
API (Application Programming Interface)
: 应用程序编程接口
简单理解:API
就是别人已经写好的东西, 我们直接使用即可。
例如我们前面说过的Random:
public static void main(String[] args) {
Random r = new Random();
int number = r.nextInt(100);
}
Java API
: 指的就是JDK中提供的各种功能的Java类
这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。
2. Math类
Math 是一个帮助我们进行计算的工具类
私有化构造方法,并且其中的方法都是静态的,方便我们使用类名直接调用
2.1 Math中的方法
方法名 | 说明 |
---|---|
public static int abs(int a) | 获取参数绝对值 |
public static double ceil(double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round (float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int值中的较大值 |
public static double pow(double a, double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double的随机值, 范围[0.0,1.0) |
示例:
abs()
System.out.println(Math.abs(-12.3));
System.out.println(Math.abs(-12));
System.out.println(Math.abs(-2147483648));
这里我们需要注意的是:
- abs的参数类型有
int,double ,long , float
- 此外int类型的数据范围是:-2147483648~2147483647,所以
System.out.println(Math.abs(-2147483648));
的返回值是它自己;因为正数中没有可以对应的数字
ceil()
System.out.println(Math.ceil(12.3));
System.out.println(Math.ceil(13.0));
输出的结果都是13.0
向上取整
floor()
System.out.println(Math.floor(12.3));
System.out.println(Math.floor(12));
输出的结果都是12.0
向下取整
round()
System.out.println(Math.round(12.3));
System.out.println(Math.round(12.5));
输出的结果是12、13
它的返回结果是int
类型
max()、min()
System.out.println(Math.max(12, 13));
System.out.println(Math.max(13.4, 15.9));
System.out.println(Math.min(12, 13));
System.out.println(Math.min(13.4, 15.9));
pow()
System.out.println(Math.pow(2, 3));
//开平方
System.out.println(Math.sqrt(4.0));
//开立方
System.out.println(Math.cbrt(8.0));
值得注意的是:Java提供了专门提供开平方和立方的方法sqrt
和cbrt
random()
for (int i = 0; i < 10; i++) {
System.out.println(Math.floor(Math.random() * 100) +1);
//Math.random() [0.0 1.0)
//* 100 [0.0 100.0)
//floor 去掉了后面的小数
//+1 [0.q 100.0)|
}
需要我们注意的是random
返回的值的范围是:[0.0 - 1.0)
2.2案例练习
a. 判断质数
判断一个范围内的数是否为一个质数
这是以前的代码:
public static boolean isPrime(int number){
for (int i = 2; i < number; i++) {
if(number % i == 0){
return false;
}
}
return true;
}
这个代码当范围较大的时候时间效率很低:例如:997,去掉1和997,要循环995次
我们优化代码:
- 一个质数的所有因子个数是偶数,并且小的和大的数两两成对
- 所有小的因子都在原数的平方根左边(比平方根小)
优化:
public static boolean isPrime(int number){
int count = 0;
for (int i = 2; i < Math.sqrt(number); i++) {
count++;
if(number % i == 0){
return false;
}
}
System.out.println(count);
return true;
}
b.字幂数(包含水仙花数)
自幂数:一个n位自然数 等于自身各个数位上数字的 n次幂 之和
举例1:三位数
举例2:四位数
1^3 +5^3 + 3^3 = 153
1^4+6^4 + 3^4 + 4^3 = 1634
介绍 | 介绍 |
---|---|
如果自幂数是一位数, 也叫做:独身数 | 三位自幂数:水仙花数 |
四位自幂数: 四叶玫瑰数 | 五位自幂数:五角星数 |
六位自幂数: 六合数 | 七位自幂数:北斗七星数 |
八位自幂数: 八仙数 | 九位自幂数:九九重阳数 |
十位自幂数:十全十美数 |
int count = 0;
//得到每一个三位数
for (int i = 100; i <= 999; i++) {
//个位 十位 百位
int ge = i % 10;
int shi = i / 10 % 10;
int bai = i / 100 % 10;
//判断:
//每一位的三次方之和 跟本身 进行比较。
double sum = Math.pow(ge,3) + Math.pow(shi,3) + Math.pow(bai,3);
if(sum == i){
count++;
System.out.println(i);
}
System.out.println(count);
}
3.System
System
是一个工具类,提供了一些和系统相关的方法
我们先来了解一下计算机中的时间原点:1970.01.01-00:00:00
因为时差的原因在我们这里要延后8个小时:1970.01.01-08:00:00
同时我们要知道在计算机中时间一般按毫秒计算
- 1秒 = 1000 毫秒
- 1毫秒 = 1000 微秒
- 1微秒 = 1000 纳秒
3.1 方法
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的 Java 虚拟机 |
public static long currentTimeMillis() | 返回当前系统的时间毫秒值形式 |
public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数) | 数组拷贝 |
3.2 方法介绍
exit()
有两个形参:- 0 : 表示当前虚拟机正常停止
- 非0 :表示当前虚拟机异常停止
System.exit(0);
System.out.println("不会执行打印语句");
currentTimeMillis()
long t = System.currentTimeMillis();
System.out.println(t);
可以获取当前代码运行时距离时间原点的时间,当然我们也可以使用这个函数来获取一段程序运行的时间
long statr = System.currentTimeMillis();
int i;
for (i = 0; i < 1000; i++) {
i += i;
}
System.out.println(i);
long end = System.currentTimeMillis();
double time = end - statr;
System.out.println(time);
arraycopy()
各个参数的含义:
- 参数一:数据源,要拷贝的数据从哪个数组而来
- 参数二:从数据源数组中的第几个索引开始拷贝
- 参数三:目的地,我要把数据拷贝到哪个数组中
- 参数四:拷贝到目的地数组的起始索引。
- 参数五:拷贝的个数
//拷贝数组
int[] arr1 = {1,2,3,4,5,6,7,8,9,10};
int[] arr2 = new int[10];
//把arr1数组中的数据考贝到arr2中
System.arraycopy(arr1, srcPos: 0,arr2, destPos: 0, length: 5);
//验证
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] +" ");
}
输出的结果为:1,2,3,4,5,0,0,0,0,0
一些注意事项:
- 1.如果数据源数组和目的地数组都是基本数据类型,那么两者的类型必须保持一致,否则会报错
- 2.在拷贝的时候需要考虑数组的长度,如果超出范围也会报错
- 3.如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型
对于第3的例子:
Student s1 = new Student ( name: "zhangsan", age: 23);
Student s2 = new Student( name: "lisi", age: 24);
Student s3 = new Student( name: "wangwu", age: 25);
Student[] arr1 = {s1, s2, s3};
Person[] arr2 = new Person[3];
//把arr1中对象的地址值赋值给arr2中
System.arraycopy(arr1, srcPos: 0,arr2, destPos: 0, length:3);
//遍历数组arr2
for (int i = 0; i < arr2.length; i++) {
Student stu = (Student) arr2[i];
System.out.println(stu.getName() + ","+ stu.getAge());
}
当然子类类型的数组赋值给子类一定是可以的
4.Runtime
Runtime
有些特殊,我们无法直接创建它的对象,需要使用一个方法来返回:
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命令 |
代码示例:
public static void main(String[] args) {
//创建对象并查看线程
Runtime r = Runtime.getRuntime();
System.out.println(r.availableProcessors());
//可获取的总空间
System.out.println(r.maxMemory() / 1024 / 1024);
//可用空间
System.out.println(r.totalMemory() /1024 / 1024);
//可用空间剩余
System.out.println(r.freeMemory() / 1024 / 1024);
//执行cmd命令
try {
r.exec("shutdown");
} catch (IOException e) {
e.printStackTrace();
}
//停止虚拟机
Runtime.getRuntime().exit(0);
System.out.println("这个代码不会执行");
}
关于为什么
Runtime
类不允许外界创建多个对象,因为Runtime反应虚拟机的运行环境,但是运行环境时唯一的,只需要一个对象就够了
实际上在Runtime中创建的对象是用
final
修饰的,表示对象的地址值不能再被修改,所以不管在哪一个类中创建这个对象,返回的都是同一个对象
5. Object
Object
是Java中的顶级父类,所有的类都间接或者直接的继承于Object
类
特性:Object
类中没有成员变量,没有有参构造
被所有子类继承的父类不可能有共性的成员变量,所以就不设置
a.方法
方法名 | 说明 |
---|---|
public String toString() | 返回对象的字符串表示形式 |
public boolean equals(Object obj) | 比较两个对象是否相等 |
protected Object clone(int a) | 对象克隆 |
b.用法示例
在此之前我先写一个Student
类用作示例辅助:
public Student{
private String name;
private int age;
public Student(){}
pbulic Student(String name, int age) {
this.name = name;
this.age = age;
}
//getter和setter方法
}
1.toString()
//1.toString 返回对象的字符串表示形式
Object obj = new Object();
String str1 = obj.toString();
System.out.println(str1);//java.lang.Object@119d7047
Student stu = new Student();
String str2 = stu.toString();
System.out.println(str2);//com.itheima.a04objectdemo.Student@4eec7777
这里讲一下System.out.println()
System
:类名out
:静态变量System.out
:获取打印的对象println()
:方法- 参数:表示打印的内容
- 核心逻辑:当我们打印一个对象的时候,底层会调用对象的
toString
方法,把对象变成字符串。然后再打印在控制台上,打印完毕换行处理。
System.out.println(stu);//com.itheima.a04objectdemo.Student@4eec7777
//思考:默认情况下,因为Objeqt类中的toString方法返回的是地址值
//所以,默认情况下,打印一个对象打印的就是地址值
//但是地址值对于我们是没什么意义的?
我们可以在要打印的类中重写toString
方法
因为在打印的时候调用的是被打印的对象的
toString
方法(继承于Object类)
@Override
public String toString() {
return name + ", " + age;
}//name 和 age 是类中的成员变量
2.equals()
Student s1 = new Student( name: "zhangsan", age: 23);
Student s2 = new Student ( name: "zhangsan", age: 23);
boolean result1 = s1.equals(s2);
System.out.println(result1);//true
结论:
- 如果没有重写
equals
方法,那么默认使用object
中的方法进行比较,比较的是地址值是否相等 - 一般来讲地址值对于我们意义不大, 所以我们会重写, 重写之后比较的就是对象内部的属性值了
重写:我们可以使用idea自带的重写程序
之后一直点击下一步即可,得到的重写equals()
方法就可以比较成员变量了
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
一个大厂面试题:
String s = "abc";
StringBuilder sb = new StringBuilder("abc");
System.out.println(s.equals(sb));// false
//因为equals方法是被s调用的,而s是字符串
//所以equals要看String类中的
//字符串中的equals方法,先判断参数是否为字符串
//如果是字符串,再比较内部的属性
//但是如果参数不是字符串,直接返回false
System.out.println(sb.equals(s));// false
//因为equals方法是被sb调用的,而sb是StringBuilder
//所以这里的equals方法要看StringBuilder中的equals方法
//那么在StringBuilder当中,没有重写equals方法
//使用的是Object中的
//在Object当中默认是使用 == 号比较两个对象的地址值
//而这里的s和sb记录的地址值是不一样的,所以结果返回false
3.clone()
这个方法是protected
修饰的,我们创建的对象要使用这个方法要在类中重写(或者说是super
调用)
例如前面的Student
类:
class Student implements Cloneable{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
值得注意的是:我们需要实现
Cloneable
接口,但是这个接口中没有抽象方法。像这样的接口称为标记性接口。
例如:当类实现了Cloneable
接口就表示当前类可以被克隆,没有实现的话当前类就不可以被克隆
实现克隆:
Student s = new Student("lsh", 18);
Student s1 = (Student) s.clone();
System.out.println(s1.getAge());
System.out.println(s1.getName());
同时
clone()
返回值是Object
类型,所以需要强制转换
克隆分类
1.浅克隆
- 不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
2.深克隆
- 基本数据类型拷贝过来
- 字符串复用
- 引用数据类型会重新创建新
我们给出一个重写为深克隆的例子:(假设类中有一维数组)
@Override
protected Object clone() throws CloneNotSupportedException {
//调用父类中的clone方法
//相当于让Java帮我们克隆一个对象,并把克隆之后的对象返回出去。
//先把被克隆对象中的数组获取出来
int[] data = this.data;
//创建新的数组
int[] newData = new int[data.length];
//拷贝数组中的数据
for (int i = 0; i < data.length; i++) {
newData[i] = data[i];
//调用父类中的方法克隆对象
User u = (User) super.clone();
//因为父类中的克隆方法是浅克隆,替换克隆出来对象中的数组地址值
u.data = newData;
return u;
}
6.Objects
Objects
是一个对象工具类,有以下的方法:
方法名 | 说明 |
---|---|
public static boolean equals(Object a, Object b) | 先做非空判断,比较两个对象 |
public static boolean isNull(Object obj) | 判断对象是否为null,为null返回true,反之 |
public static boolean nonNull(Object obj) | 判断对象是否为null,跟isNull的结果相反 |
a.方法说明
boolean result = Objects.equals(s1, s2);
System.out.println(result);
注意:
- 方法的底层会判断
s1
是否为nu11
,如果为nu11
,直接返回false
- 如果
s1
不为null,
那么就利用s1
再次调用equals
方法 - 此时
s1
是Student
类型,所以最终还是会调用Student
中的equals
方法。
在这个类中如果没有重写, 比较地址值, 如果重写了,就比较属性值。