【JAVA入门】Day18 - 常见API(Math、System、Runtime、Object、Objects)
文章目录
API 是 Java 帮我们写好的有各种功能的 Java 类,它们内部有很多使用的方法。
一、Math 类
Math 类在 java.lang 中,直接继承 Object 类。其包含了基本数学运算的方法。
Math 类是一个最终类,不能被继承:
public final class Math extends Object
Math 中的方法全是静态方法(static),且私有化构造方法,不可创建对象,所以调用时直接用类名 Math 调用即可。
常见的方法如下。
public class MathDemo1 {
public static void main(String[] args) {
//abs 获取参数绝对值
System.out.println(Math.abs(88)); //88
System.out.println(Math.abs(-88)); //88
//ceil 获取向上取整
System.out.println(Math.ceil(12.34)); //13.0
System.out.println(Math.ceil(12.54)); //13.0
System.out.println(Math.ceil(-12.34)); //-12.0
System.out.println(Math.ceil(-12.54)); //-12.0
//floor 获取向下取整
System.out.println(Math.floor(12.34)); //12.0
System.out.println(Math.floor(12.54)); //12.0
System.out.println(Math.floor(-12.34)); //-13.0
System.out.println(Math.floor(-12.54)); //-13.0
//四舍五入
System.out.println(Math.round(12.34)); //12
System.out.println(Math.round(12.54)); //13
System.out.println(Math.round(-12.34)); //-12
System.out.println(Math.round(-12.54)); //-13
//获取两个整数较大值
System.out.println(Math.max(20, 30)); //30
//获取两个整数较小值
System.out.println(Math.min(20, 30)); //20
//获取a的b次幂
System.out.println(Math.pow(2, 3)); //8
System.out.println(Math.pow(4, 0.5)); //2
System.out.println(Math.pow(2, -2)); //0.25
//开平方
System.out.println(Math.sqrt(4)); //2.0
//开立法
System.out.println(Math.cbrt(8)); //2.0
//随机数
//获取一个[0.0, 1.0)的随机数
for(int i = 0;i < 10; i++) {
System.out.println(Math.random());
}
//获取[1.0, 100.0]的随机整数
System.out.println(Math.floor(Math.random() * 100) + 1);
}
【练习1】判断一个数是否为一个质数。
public static boolean isPrime(int number) {
for(int i = 0; i <= Math.sqrt(number); i++) {
if(number % i == 0) {
return false;
}
}
return true;
}
【练习2】判断一个数是不是自幂数。求100~999之间的所有自幂数。
※自幂数,一个 n 位自然数等于自身各个数位上数字的 n 次幂之和。
举例1:三位数 1^3 + 5^3 + 3^3 = 153
举例2:四位数 1^4 + 6^4 + 3^4 + 4^3 = 1634
如果自幂数是一位数,也叫做独身数;如果是三位数,也叫做水仙花数;如果是四位数,也叫做四叶玫瑰数。
public class ShuiXianHua {
public static void main(String[] args) {
//水仙花数:100~999
int count = 0;
for(int i = 0; i < 1000; 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("100~999之间的水仙花数一共有" + count + "个。");
}
}
二、System 类
System 也是一个工具类,提供了一些与系统相关的方法。
2.1 计算机中的时间原点
在计算机中,时间是有一个原点的,它表示最初的开始时间。
计算机中的时间原点是:
1970年1月1日 00:00:00
在计算机中:
1秒 = 1000毫秒
1毫秒 = 1000微秒
1微秒 = 1000纳秒
在计算机中,我们可以用这个方法返回当前系统的时间毫秒值形式。
public static long currentTimeMillis()
2.2 其他 System 中的常用方法
- 终止当前运行的 Java 虚拟机。
public static void exit(int status)
- 数组拷贝
public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数);
【练习】使用以上三个方法。
public class SystemTest {
public static void main(String[] args) {
//方法的形参:
//状态码:
//0:表示当前虚拟机是正常停止
//非0:表示当前虚拟机是异常停止
//System.exit(0);
long l = System.currentTimeMillis();
System.out.println(l);
//public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数);
int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = new int[10];
System.arraycopy(a1, 0, a2, 0, 5);
//1 2 3 4 5 0 0 0 0 0
for(int i = 0; i < a2.length; i++) {
System.out.print(a2[i] + " ");
}
}
}
注意:
1.如果数据源数组和目的地数组都是基本数据类型,那么两者的数据类型必须保持一致,否则会报错。
2.在拷贝的时候需要考虑数组的长度,如果超出范围也会报错。
3.如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型(多态)。
public class Test2 {
public static void main(String[] args) {
Student s1 = new Student(23, "zhangsan");
Student s2 = new Student(24, "lisi");
Student s3 = new Student(25, "wangwu");
Student[] arr1 = {s1, s2, s3};
Person[] arr2 = new Person[3];
//子类数组拷贝给父类数组,实现多态
System.arraycopy(arr1, 0, arr2, 0, 3);
//遍历arr2
for(int i = 0; i < arr2.length; i++) {
//在使用时,需要使用强转把数组转回子类
Student stu = (Student) arr2[i];
System.out.println(stu.getName() + ", " + stu.getAge());
}
}
}
三、Runtime 类
Runtime 表示当前虚拟机的运行环境。
常见方法有这些:
- 获取系统的运行环境对象
public static Runtime getRuntime()
- 停止虚拟机(System 类中调用的 exit 方法就是这个方法)
public void exit(int status)
- 获得CPU的线程数
public int availableProcessors()
- JVM能从系统中获取总内存的大小(单位 byte)
public long maxMemory()
- JVM已经从系统中获取总内存大小(单位 byte)
public long totalMemory()
- JVM剩余内存大小(单位 byte)
public long freeMemory()
- 运行 cmd 命令
public Process exec(String command)
【练习】使用 Runtime 类。
package RuntimeTest;
import java.io.IOException;
public class RuntimeDemo1 {
public static void main(String[] args) throws IOException {
//1.获取Runtime对象
//Runtime是私有化构造方法,需要调用它自己的getRuntime()方法来获取对象
//这是为了保证系统运行中永远只有一个Runtime对象
Runtime r = Runtime.getRuntime();
System.out.println(r);
//2.exit 停止虚拟机
//Runtime.getRuntime().exit(0);
//3.获得CPU线程数
System.out.println(Runtime.getRuntime().availableProcessors());
//4.总内存大小,单位byte,/1024是KB,再/1024是MB
System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);
//5.已经获取的总内存大小
System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);
//6.剩余内存大小
System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);
//7.运行cmd命令
Runtime.getRuntime().exec("notepad");
}
}
四、Object
Object 是 Java 中的顶级父类。所有的类都直接或间接的继承于 Object 类。
Object 类中的方法可以被所有子类访问。
- 空参构造
public Object()
- 返回对象的字符串表示形式
public String toString()
- 比较两个对象是否相等
public boolean equals(Object obj)
- 对象克隆
protected Object clone(int a)
【练习】使用这几个方法。
package ObjectTest;
public class ObjectDemo1 {
public static void main(String[] args) {
//1.toString
Object obj = new Object();
String str1 = obj.toString();
System.out.println(str1); //java.lang.Object@4c873330 //包名+ @ +地址值
Student stu = new Student();
String str2 = stu.toString(); //Student也继承于Object,可以直接调用其的toString()方法
System.out.println(str2); //ObjectTest.Student@776ec8df
//当我们直接打印一个对象的时候,底层会调用对象的toString()方法,把对象变成字符串,然后再打印在控制台上,打印完毕后换行处理
//所以,默认情况下,打印一个对象打印的就是地址值
//要想获取对象内部的属性值,可以用get方法
//System.out.println(stu.getName() + " " + stu.getAge());
//要想使代码更简洁,我们可以在Student类中重写toString()方法
System.out.println(stu); //ObjectTest.Student@776ec8df
}
}
package ObjectTest;
public class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public Student() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name + " " + age;
}
}
通过在 Student 类中重写继承自 Object 类的 toString() 方法,我们可以让 System.out.println() 语句直接输出对象的属性值。
一样的道理,equals() 方法比较的是两个对象的地址值,我们可以通过重写,让它实现比较对象的属性值。
package ObjectTest;
public class ObjectDemo2 {
public static void main(String[] args) {
//比较两个对象(地址值)是否相等
Student s1 = new Student();
Student s2 = new Student();
boolean result1 = s1.equals(s2); //false
System.out.println(result1);
Student s3 = s2;
boolean result2 = s2.equals(s3); //true
System.out.println(result2);
}
}
package ObjectTest;
import java.util.Objects;
public class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public Student() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name + " " + age;
}
@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 类,也重写了 equals 方法,它的逻辑是这样的:
package ObjectTest;
public class ObjectDemo3 {
public static void main(String[] args) {
String s = "abc";
StringBuilder sb = new StringBuilder("abc");
//字符串中的equals方法,先判断参数是否为字符串,不是直接返回 false
//如果参数是字符串,再比较字符串的内容
//因为传递的参数是一个StringBuilder对象而非字符串,所以直接返回 false
System.out.println(s.equals(sb)); //false
//StringBuilder中根本没有重写equals方法
//所以StringBuilder中的equals还是Object的原生equals方法,它比较的是对象的地址值
//因为sb和s是两个不同对象,所以返回false
System.out.println(sb.equals(s)); //false
}
}
对象克隆,即把 A 对象的属性值完全拷贝给 B 对象,也叫对象拷贝,对象复制。
package ObjectTest;
public class Student implements Cloneable {
//Cloneable
//如果一个接口里没有抽象方法
//表示当前接口是一个标记性接口
//现在用Student实现Cloneable,表示该接口已经被实现,那么当前类的对象就可以被克隆
//如果没有实现,当前类的对象就不能被克隆(类似flag标记)
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public Student() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name + " " + age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//调用父类中的clone方法
//相当于让Java帮我们克隆一个对象,并把克隆之后的对象返回出去
return super.clone();
}
}
package ObjectTest;
public class ObjectDemo4 {
public static void main(String[] args) throws CloneNotSupportedException {
//protected Object clone(int a)
//因为clone()是一个protected修饰的方法,不能直接用其他对象调用(除非在lang包下创建这个对象)
//因此要在Student类中先重写clone()方法才可以调用
//然后我们需要让Student类实现一个额外接口Cloneable
Student stu1 = new Student();
stu1.setAge(25);
stu1.setName("王五");
//2.克隆对象
//注意clone()方法返回的是Object类,需要强转才能变成Student类
Student stu2 = (Student) stu1.clone();
System.out.println(stu1);
System.out.println(stu2);
}
}
注意:克隆对象的时候有以下书写细节!
1.首先在要克隆的Javabean类内部先重写Object中的clone方法。
2.让这个Javabean类实现Cloneable接口。
3.抛出CloneNotSupportedException异常。
4.创建原对象并调用clone,注意克隆完的对象是Object,要进行强转。
在Java中,存在两种克隆方式,一种叫浅克隆,一种叫深克隆。
- 所谓浅克隆:不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来,记录其值。
- 所谓深克隆:基本数据类型直接拷贝过来,字符串直接复用串池,引用数据类型则会重新创建新的,并记录其地址值。
Object 中的 clone() ,它是浅克隆。
验证:我们可以用 Object 克隆一个含数组的对象,然后修改数组的元素,看看克隆后的对象数组元素是否跟着改变了(如果跟着改变,则说明是浅克隆,两个对象中的数组共用了一个地址值)。
package ObjectTest;
import java.util.Arrays;
public class BeCloned implements Cloneable {
int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int number = 1;
@Override
public String toString() {
return "BeCloned{" +
"data=" + Arrays.toString(data) +
", number=" + number +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class ObjectDemo5 {
public static void main(String[] args) throws CloneNotSupportedException {
//创建bc1,克隆得到bc2
BeCloned bc1 = new BeCloned();
BeCloned bc2 = (BeCloned)bc1.clone();
//修改bc2的数组值
bc2.data[0] = 100;
//bc1的值也发生了改变
System.out.println(bc1);
System.out.println(bc2);
}
}
我们可以用重写的方法把 clone() 改写为一个深克隆。
package ObjectTest;
import java.util.Arrays;
public class BeCloned implements Cloneable {
int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int number = 1;
@Override
public String toString() {
return "BeCloned{" +
"data=" + Arrays.toString(data) +
", number=" + number +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
//先把被克隆对象中的数组获取出来
int[] data = this.data;
//创建新数组
int[] newData = new int[data.length];
//拷贝数据
for(int i = 0; i < newData.length; i++) {
newData[i] = data[i];
}
//先调用Object的clone方法进行浅克隆
BeCloned bc = (BeCloned) super.clone();
//再自己手动将新数组赋给bc,替换克隆出来对象中的数组地址值,完成深克隆
bc.data = newData;
return bc;
}
}
public class ObjectDemo5 {
public static void main(String[] args) throws CloneNotSupportedException {
//创建bc1,克隆得到bc2
BeCloned bc1 = new BeCloned();
BeCloned bc2 = (BeCloned)bc1.clone();
//修改bc2的数组值
bc2.data[0] = 100;
//bc1的值没有发生改变,完成深克隆
System.out.println(bc1);
System.out.println(bc2);
}
}
如果以后想要让 clone() 进行的是深克隆,我们可以使用第三方工具(jar包)。
扩展:使用第三方Jar包完成深克隆
我们下载的第三方 jar 包一般都放到 lib 文件夹下。
之后 Add as Library,就可以使用其他人编写的代码了。
package ObjectTest;
import com.google.gson.Gson;
public class ObjectDemo5 {
public static void main(String[] args) {
//创建一个对象
BeCloned bc1 = new BeCloned();
//通过Gson克隆对象
Gson gson = new Gson();
//把对象变为一个字符串
String s = gson.toJson(bc1);
System.out.println(s); //{"data":[1,2,3,4,5,6,7,8,9,10],"number":1}
//再把字符串变回对象就可以了
BeCloned bc2 = gson.fromJson(s, BeCloned.class);
//验证:实现了深克隆
bc2.data[0] = 100;
System.out.println(bc1); //BeCloned{data=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], number=1}
System.out.println(bc2); //BeCloned{data=[100, 2, 3, 4, 5, 6, 7, 8, 9, 10], number=1}
}
}
五、Objects
Objects 也是 Java 的一个常用工具类。其内部有几个常用方法。
public static boolean equals(Object a, Object b) 先做非空判断,再比较两个对象。
public static boolean isNull(Object obj) 判断对象是否为null,是返回true,反之返回false。
public static boolean nonNull(Object obj) 还是判断,但是结果和isNull相反。
Objects.equals() 的运行逻辑:1.方法先判断s1是不是null,如果是null,直接返回 false。2.如果s1不是null,就利用s1再次调用equals()方法。
package ObjectsTest;
import java.util.Objects;
public class ObjectsDemo1 {
public static void main(String[] args) {
Student s1 = null;
Student s2 = new Student(21, "张三");
boolean result = Objects.equals(s1, s2);
System.out.println(result);
}
}
isNull() 和 nonNull() 的结果是相反的,就是用来判断当前对象是否为空的。
Student s3 = new Student();
System.out.println(Objects.isNull(s3)); //false
System.out.println(Objects.nonNull(s3)); //true