1 Math类
1.1 概述
tips:了解内容
查看API文档,我们可以看到API文档中关于Math类的定义如下:
Math类所在包为java.lang包,因此在使用的时候不需要进行导包。并且Math类被final修饰了,因此该类是不能被继承的。
Math类包含执行基本数字运算的方法,我们可以使用Math类完成基本的数学运算。
要想使用Math类我们就需要先创建该类的对象,那么创建对象就需要借助于构造方法。因此我们就需要首先查看一下API文档,看看API文档中针对Math类有没有提供对应的构造方法。通过API文档来查看
在API文档中没有体现可用的构造方法,因此我们就不能直接通过new关键字去创建Math类的对象。同时我们发现Math类中的方法都是静态的,因此在使用的时候我们可以直接通过类名去调用。在Math类中
定义了很多数学运算的方法,但是我们并不可能将所有的方法学习一遍,我们主要学习的就是一些常见的方法。
1.2 常见方法
常见方法介绍
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)的随机值
案例演示
public class MathDemo01 {public static void main(String[] args) {// public static int abs(int a) 返回参数的绝对值System.out.println("-2的绝对值为:" + Math.abs(-2));System.out.println("2的绝对值为:" + Math.abs(2));// public static double ceil(double a) 返回大于或等于参数的最小整数System.out.println("大于或等于23.45的最小整数位:" + Math.ceil(23.45));System.out.println("大于或等于-23.45的最小整数位:" + Math.ceil(-23.45));// public static double floor(double a) 返回小于或等于参数的最大整数System.out.println("小于或等于23.45的最大整数位:" + Math.floor(23.45));System.out.println("小于或等于-23.45的最大整数位:" + Math.floor(-23.45));// public static int round(float a) 按照四舍五入返回最接近参数的intSystem.out.println("23.45四舍五入的结果为:" + Math.round(23.45));System.out.println("23.55四舍五入的结果为:" + Math.round(23.55));// public static int max(int a,int b) 返回两个int值中的较大值System.out.println("23和45的最大值为: " + Math.max(23, 45));// public static int min(int a,int b) 返回两个int值中的较小值System.out.println("12和34的最小值为: " + Math.min(12 , 34));// public static double pow (double a,double b)返回a的b次幂的值System.out.println("2的3次幂计算结果为: " + Math.pow(2,3));// public static double random()返回值为double的正值,[0.0,1.0)System.out.println("获取到的0-1之间的随机数为: " + Math.random());}}
运行程序进行测试,控制台输出结果如下:
-2的绝对值为:2
2的绝对值为:2
大于或等于23.45的最小整数位:24.0
大于或等于-23.45的最小整数位:-23.0
小于或等于23.45的最大整数位:23.0
小于或等于-23.45的最大整数位:-24.0
23.45四舍五入的结果为:23
23.55四舍五入的结果为:24
23和45的最大值为: 45
12和34的最小值为: 12
2的3次幂计算结果为: 8.0
获取到的0-1之间的随机数为: 0.7322484131745958
1.3 算法小题(质数)
需求:
判断一个数是否为一个质数
代码实现:
public class MathDemo2 {public static void main(String[] args) {//判断一个数是否为一个质数System.out.println(isPrime(997));//997 2~996 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;}
}
2 System类
2.1 概述
tips:了解内容
查看API文档,我们可以看到API文档中关于System类的定义如下:
System类所在包为java.lang包,因此在使用的时候不需要进行导包。并且System类被final修饰了,因此该类是不能被继承的。
System包含了系统操作的一些常用的方法。比如获取当前时间所对应的毫秒值,再比如终止当前JVM等等。
要想使用System类我们就需要先创建该类的对象,那么创建对象就需要借助于构造方法。因此我们就需要首先查看一下API文档,看看API文档中针对System类有没有提供对应的构造方法。通过API文档来
查看一下System类的成员,如下所示:
在API文档中没有体现可用的构造方法,因此我们就不能直接通过new关键字去创建System类的对象。同时我们发现System类中的方法都是静态的,因此在使用的时候我们可以直接通过类名去调用(Nested
Class Summary内部类或者内部接口的描述)。
2.2 常见方法
tips:重点讲解内容
常见方法介绍
我们要学习的System类中的常见方法如下所示:
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方法
public class SystemDemo01 {public static void main(String[] args) {// 获取当前时间所对应的毫秒值long millis = System.currentTimeMillis();// 输出结果System.out.println("当前时间所对应的毫秒值为:" + millis);}}
案例2:演示exit方法
public class SystemDemo01 {public static void main(String[] args) {// 输出System.out.println("程序开始执行了.....");// 终止JVMSystem.exit(0);// 输出System.out.println("程序终止了..........");}}
运行程序进行测试,控制台输出结果如下:
程序开始执行了.....
此时可以看到在控制台只输出了"程序开始了…",由于JVM终止了,因此输出"程序终止了…"这段代码没有被执行。
案例3:演示arraycopy方法
方法参数说明:
// src: 源数组
// srcPos: 源数值的开始位置
// dest: 目标数组
// destPos: 目标数组开始位置
// length: 要复制的元素个数
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
代码如下
public class SystemDemo01 {public static void main(String[] args) {// 定义源数组int[] srcArray = {23 , 45 , 67 , 89 , 14 , 56 } ;// 定义目标数组int[] desArray = new int[10] ;// 进行数组元素的copy: 把srcArray数组中从0索引开始的3个元素,从desArray数组中的1索引开始复制过去System.arraycopy(srcArray , 0 , desArray , 1 , 3);// 遍历目标数组for(int x = 0 ; x < desArray.length ; x++) {if(x != desArray.length - 1) {System.out.print(desArray[x] + ", ");}else {System.out.println(desArray[x]);}}}}
运行程序进行测试,控制台输出结果如下所示:
0, 23, 45, 67, 0, 0, 0, 0, 0, 0
通过控制台输出结果我们可以看到,数组元素的确进行复制了。
使用这个方法我们也可以完成数组元素的删除操作,如下所示:
public class SystemDemo02 {public static void main(String[] args) {// 定义一个数组int[] srcArray = {23 , 45 , 67 , 89 , 14 , 56 } ;// 删除数组中第3个元素(67):要删除67这个元素,我们只需要将67后面的其他元素依次向前进行移动即可System.arraycopy(srcArray , 3 , srcArray , 2 , 3);// 遍历srcArray数组for(int x = 0 ; x < srcArray.length ; x++) {if(x != desArray.length - 1) {System.out.print(srcArray[x] + ", ");}else {System.out.println(srcArray[x]);}}}
}
运行程序进行测试,控制台的输出结果如下所示:
23, 45, 89, 14, 56, 56
通过控制台输出结果我们可以看到此时多出了一个56元素,此时我们只需要将最后一个位置设置为0即可。如下所示
public class SystemDemo02 {public static void main(String[] args) {// 定义一个数组int[] srcArray = {23 , 45 , 67 , 89 , 14 , 56 } ;// 删除数组中第3个元素(67):要删除67这个元素,我们只需要将67后面的其他元素依次向前进行移动即可System.arraycopy(srcArray , 3 , srcArray , 2 , 3);// 将最后一个位置的元素设置为0srcArray[srcArray.length - 1] = 0 ;// 遍历srcArray数组for(int x = 0 ; x < srcArray.length ; x++) {if(x != srcArray.length - 1 ) {System.out.print(srcArray[x] + ", ");}else {System.out.println(srcArray[x]);}}}
}
运行程序进行测试,控制台输出结果如下所示:
23, 45, 89, 14, 56, 0
此时我们可以看到元素"67"已经被删除掉了。67后面的其他元素依次向前进行移动了一位。
arraycopy方法底层细节:
1.如果数据源数组和目的地数组都是基本数据类型,那么两者的类型必须保持一致,否则会报错
2.在拷贝的时候需要考虑数组的长度,如果超出范围也会报错
3.如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型
代码示例:
public class SystemDemo3 {public static void main(String[] args) {//public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数) 数组拷贝//细节://1.如果数据源数组和目的地数组都是基本数据类型,那么两者的类型必须保持一致,否则会报错//2.在拷贝的时候需要考虑数组的长度,如果超出范围也会报错//3.如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型Student s1 = new Student("zhangsan", 23);Student s2 = new Student("lisi", 24);Student s3 = new Student("wangwu", 25);Student[] arr1 = {s1, s2, s3};Person[] arr2 = new Person[3];//把arr1中对象的地址值赋值给arr2中System.arraycopy(arr1, 0, arr2, 0, 3);//遍历数组arr2for (int i = 0; i < arr2.length; i++) {Student stu = (Student) arr2[i];System.out.println(stu.getName() + "," + stu.getAge());}}
}class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}/*** 获取** @return name*/public String getName() {return name;}/*** 设置** @param name*/public void setName(String name) {this.name = name;}/*** 获取** @return age*/public int getAge() {return age;}/*** 设置** @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Person{name = " + name + ", age = " + age + "}";}
}class Student extends Person {public Student() {}public Student(String name, int age) {super(name, age);}
}
3 Runtime
3.1 概述
Runtime表示Java中运行时对象,可以获取到程序运行时设计到的一些信息
3.2 常见方法
常见方法介绍
我们要学习的Object类中的常见方法如下所示:
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 class RunTimeDemo1 {public static void main(String[] args) throws IOException {/*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.获取Runtime的对象//Runtime r1 =Runtime.getRuntime();//2.exit 停止虚拟机//Runtime.getRuntime().exit(0);//System.out.println("看看我执行了吗?");//3.获得CPU的线程数System.out.println(Runtime.getRuntime().availableProcessors());//8//4.总内存大小,单位byte字节System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);//4064//5.已经获取的总内存大小,单位byte字节System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);//254//6.剩余内存大小System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);//251//7.运行cmd命令//shutdown :关机//加上参数才能执行//-s :默认在1分钟之后关机//-s -t 指定时间 : 指定关机时间//-a :取消关机操作//-r: 关机并重启Runtime.getRuntime().exec("shutdown -s -t 3600");}
}
4 Object类
4.1 概述
tips:重点讲解内容
查看API文档,我们可以看到API文档中关于Object类的定义如下:
Object类所在包是java.lang包。Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类;换句话说,该类所具备的方法,其他所有类都继承了。
查看API文档我们可以看到,在Object类中提供了一个无参构造方法,如下所示:
但是一般情况下我们很少去主动的创建Object类的对象,调用其对应的方法。更多的是创建Object类的某个子类对象,然后通过子类对象调用Object类中的方法。
4.2 常见方法
tips:重点讲解内容
常见方法介绍
我们要学习的Object类中的常见方法如下所示:
public String toString() //返回该对象的字符串表示形式(可以看做是对象的内存地址值)
public boolean equals(Object obj) //比较两个对象地址值是否相等;true表示相同,false表示不相同
protected Object clone() //对象克隆
案例演示
接下来我们就来通过一些案例演示一下这些方法的特点。
案例1:演示toString方法
实现步骤:
- 创建一个学生类,提供两个成员变量(name , age);并且提供对应的无参构造方法和有参构造方法以及get/set方法
- 创建一个测试类(ObjectDemo01),在测试类的main方法中去创建学生对象,然后调用该对象的toString方法获取该对象的字符串表现形式,并将结果进行输出
如下所示:
Student类
public class Student {private String name ; // 姓名private String age ; // 年龄// 无参构造方法和有参构造方法以及get和set方法略...}
ObjectDemo01测试类
public class ObjectDemo01 {public static void main(String[] args) {// 创建学生对象Student s1 = new Student("itheima" , "14") ;// 调用toString方法获取s1对象的字符串表现形式String result1 = s1.toString();// 输出结果System.out.println("s1对象的字符串表现形式为:" + result1);}}
运行程序进行测试,控制台输出结果如下所示:
s1对象的字符串表现形式为:com.itheima.api.system.demo04.Student@3f3afe78
为什么控制台输出的结果为:com.itheima.api.system.demo04.Student@3f3afe78; 此时我们可以查看一下Object类中toString方法的源码,如下所示:
public String toString() { // Object类中toString方法的源码定义return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
其中getClass().getName()对应的结果就是:com.itheima.api.system.demo04.Student;Integer.toHexString(hashCode())对应的结果就是3f3afe78。
我们常常将"com.itheima.api.system.demo04.Student@3f3afe78"这一部分称之为对象的内存地址值。但是一般情况下获取对象的内存地址值没有太大的意义。获取对象的成员变量的字符串拼接形式才
算有意义,怎么实现呢?此时我们就需要在Student类中重写Object的toString方法。我们可以通过idea开发工具进行实现,具体步骤如下所示:
-
在空白处使用快捷键:alt + insert。此时会弹出如下的对话框
-
选择toString,此时会弹出如下的对话框
同时选择name和age属性,点击OK。此时就会完成toString方法的重写,代码如下所示:
@Override
public String toString() {return "Student{" +"name='" + name + '\'' +", age='" + age + '\'' +'}';
}
这段代码就是把Student类中的成员变量进行了字符串的拼接。重写完毕以后,再次运行程序,控制台输出结果如下所示:
s1对象的字符串表现形式为:Student{name=‘itheima’, age=‘14’}
此时我们就可以清楚的查看Student的成员变量值,因此重写toString方法的意义就是以良好的格式,更方便的展示对象中的属性值
我们再来查看一下如下代码的输出:
// 创建学生对象
Student s1 = new Student("itheima" , "14") ;// 直接输出对象s1
System.out.println(s1)
运行程序进行测试,控制台输出结果如下所示:
Student{name='itheima', age='14'}
我们可以看到和刚才的输出结果是一致的。那么此时也就证明直接输出一个对象,那么会默认调用对象的toString方法,因此如上代码的等同于如下代码:
// 创建学生对象
Student s1 = new Student("itheima" , "14") ;// 调用s1的toString方法,把结果进行输出
System.out.println(s1.toString());
因此后期为了方便进行测试,我们常常是通过输出语句直接输出一个对象的名称。
小结:
- 在通过输出语句输出一个对象时,默认调用的就是toString()方法
- 输出地址值一般没有意义,我们可以通过重写toString方法去输出对应的成员变量信息(快捷键:atl + insert , 空白处 右键 -> Generate -> 选择toString)
- toString方法的作用:以良好的格式,更方便的展示对象中的属性值
- 一般情况下Jdk所提供的类都会重写Object类中的toString方法
案例2:演示equals方法
实现步骤:
- 在测试类(ObjectDemo02)的main方法中,创建两个学生对象,然后比较两个对象是否相同
代码如下所示:
public class ObjectDemo02 {public static void main(String[] args) {// 创建两个学生对象Student s1 = new Student("itheima" , "14") ;Student s2 = new Student("itheima" , "14") ;// 比较两个对象是否相等System.out.println(s1 == s2);}}
运行程序进行测试,控制台的输出结果如下所示:
false
因为"=="号比较的是对象的地址值,而我们通过new关键字创建了两个对象,它们的地址值是不相同的。因此比较结果就是false。
我们尝试调用Object类中的equals方法进行比较,代码如下所示:
// 调用equals方法比较两个对象是否相等
boolean result = s1.equals(s2);// 输出结果
System.out.println(result);
运行程序进行测试,控制台的输出结果为: false
为什么结果还是false呢?我们可以查看一下Object类中equals方法的源码,如下所示:
public boolean equals(Object obj) { // Object类中的equals方法的源码return (this == obj);
}
通过源码我们可以发现默认情况下equals方法比较的也是对象的地址值。比较内存地址值一般情况下是没有意义的,我们希望比较的是对象的属性,如果两个对象的属性相同,我们认为就是同一个对象;
那么要比较对象的属性,我们就需要在Student类中重写Object类中的equals方法。equals方法的重写,我们也可以使用idea开发工具完成,具体的操作如下所示:
-
在空白处使用快捷键:alt + insert。此时会弹出如下的对话框
-
选择equals() and hashCode()方法,此时会弹出如下的对话框
-
点击next,会弹出如下对话框:
-
选择neme和age属性点击next,此时就会弹出如下对话框:
@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属性值
}@Override
public int hashCode() {return 0;
}
重写完毕以后运行程序进行测试,控制台输出结果如下所示:
true
此时equals方法比较的是对象的成员变量值,而s1和s2两个对象的成员变量值都是相同的。因此比较完毕以后的结果就是true。
小结:
- 默认情况下equals方法比较的是对象的地址值
- 比较对象的地址值是没有意义的,因此一般情况下我们都会重写Object类中的equals方法
案例2:对象克隆
把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制
对象克隆的分类:
深克隆和浅克隆
浅克隆:
不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值。
Object类默认的是浅克隆
深克隆:
基本数据类型拷贝过来,字符串复用,引用数据类型会重新创建新的
代码实现:
package com.itheima.a04objectdemo;public class ObjectDemo4 {public static void main(String[] args) throws CloneNotSupportedException {// protected object clone(int a) 对象克隆 //1.先创建一个对象int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};User u1 = new User(1, "zhangsan", "1234qwer", "girl11", data);//2.克隆对象//细节://方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。//书写细节://1.重写Object中的clone方法//2.让javabean类实现Cloneable接口//3.创建原对象并调用clone就可以了//User u2 =(User)u1.clone();//验证一件事情:Object中的克隆是浅克隆//想要进行深克隆,就需要重写clone方法并修改里面的方法体//int[] arr = u1.getData();//arr[0] = 100;//System.out.println(u1);//System.out.println(u2);//以后一般会用第三方工具进行克隆//1.第三方写的代码导入到项目中//2.编写代码//Gson gson =new Gson();//把对象变成一个字符串//String s=gson.toJson(u1);//再把字符串变回对象就可以了//User user =gson.fromJson(s, User.class);//int[] arr=u1.getData();//arr[0] = 100;//打印对象//System.out.println(user);}
}package com.itheima.a04objectdemo;import java.util.StringJoiner;//Cloneable
//如果一个接口里面没有抽象方法
//表示当前的接口是一个标记性接口
//现在Cloneable表示一旦实现了,那么当前类的对象就可以被克降
//如果没有实现,当前类的对象就不能克隆
public class User implements Cloneable {private int id;private String username;private String password;private String path;private int[] data;public User() {}public User(int id, String username, String password, String path, int[] data) {this.id = id;this.username = username;this.password = password;this.path = path;this.data = data;}/*** 获取** @return id*/public int getId() {return id;}/*** 设置** @param id*/public void setId(int id) {this.id = id;}/*** 获取** @return username*/public String getUsername() {return username;}/*** 设置** @param username*/public void setUsername(String username) {this.username = username;}/*** 获取** @return password*/public String getPassword() {return password;}/*** 设置** @param password*/public void setPassword(String password) {this.password = password;}/*** 获取** @return path*/public String getPath() {return path;}/*** 设置** @param path*/public void setPath(String path) {this.path = path;}/*** 获取** @return data*/public int[] getData() {return data;}/*** 设置** @param data*/public void setData(int[] data) {this.data = data;}public String toString() {return "角色编号为:" + id + ",用户名为:" + username + "密码为:" + password + ", 游戏图片为:" + path + ", 进度:" + arrToString();}public String arrToString() {StringJoiner sj = new StringJoiner(", ", "[", "]");for (int i = 0; i < data.length; i++) {sj.add(data[i] + "");}return sj.toString();}@Overrideprotected 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;}
}
5 Objects类
5.2 常见方法
tips:重点讲解内容
常见方法介绍
我们要重点学习的Objects类中的常见方法如下所示:
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类中的常见方法如下所示:
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类型。
案例演示
接下来我们就来通过一些案例演示一下Objects类中的这些方法特点。
案例1:演示重点学习方法
实现步骤:
- 创建一个学生类,提供两个成员变量(name , age);并且提供对应的无参构造方法和有参构造方法以及get/set方法,并且重写toString方法和equals方法
- 创建一个测试类(ObjectsDemo01), 在该类中编写测试代码
如下所示:
Student类
public class Student {private String name ; // 姓名private String age ; // 年龄// 其他代码略...
}
ObjectsDemo01测试类
public class ObjectsDemo01 {public static void main(String[] args) {// 调用方法method_04() ;}// 测试nonNull方法public static void method_04() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects类中的nonNull方法boolean result = Objects.nonNull(s1);// 输出结果System.out.println(result);}// 测试isNull方法public static void method_03() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects类中的isNull方法boolean result = Objects.isNull(s1);// 输出结果System.out.println(result);}// 测试equals方法public static void method_02() {// 创建两个学生对象Student s1 = new Student("itheima" , "14") ;Student s2 = new Student("itheima" , "14") ;// 调用Objects类中的equals方法,比较两个对象是否相等boolean result = Objects.equals(s1, s2); // 如果Student没有重写Object类中的equals方法,此处比较的还是对象的地址值// 输出结果System.out.println(result);}// 测试toString方法public static void method_01() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects中的toString方法,获取s1对象的字符串表现形式String result = Objects.toString(s1); // 如果Student没有重写Object类中的toString方法,此处还是返回的对象的地址值// 输出结果System.out.println(result);}}
案例2:演示需要了解的方法
public class ObjectsDemo02 {public static void main(String[] args) {// 调用方法method_03();}// 演示requireNonNullElseGetpublic static void method_03() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects对象的requireNonNullElseGet方法,该方法的第二个参数是Supplier类型的,查看源码我们发现Supplier是一个函数式接口,// 那么我们就可以为其传递一个Lambda表达式,而在Supplier接口中所定义的方法是无参有返回值的方法,因此具体调用所传入的Lambda表达式如下所示Student student = Objects.requireNonNullElseGet(s1, () -> {return new Student("itcast", "14");});// 输出System.out.println(student);}// 演示requireNonNullElsepublic static void method_02() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects对象的requireNonNullElse方法Student student = Objects.requireNonNullElse(s1, new Student("itcast", "14"));// 输出System.out.println(student);}// 演示requireNonNullpublic static void method_01() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects对象的requireNonNull方法Student student = Objects.requireNonNull(s1);// 输出System.out.println(student);}}
6 BigInteger类
6.1 引入
平时在存储整数的时候,Java中默认是int类型,int类型有取值范围:-2147483648 ~ 2147483647。如果数字过大,我们可以使用long类型,但是如果long类型也表示不下怎么办呢?
就需要用到BigInteger,可以理解为:大的整数。
有多大呢?理论上最大到42亿的21亿次方
基本上在内存撑爆之前,都无法达到这个上限。
6.2 概述
查看API文档,我们可以看到API文档中关于BigInteger类的定义如下:
BigInteger所在包是在java.math包下,因此在使用的时候就需要进行导包。我们可以使用BigInteger类进行大整数的计算
6.3 常见方法
构造方法
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类中使用最多的还是提供的进行四则运算的方法,如下:
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类型整数,超出范围数据有误
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次方-11public BigInteger(String val) 获取指定的大整数public BigInteger(String val, int radix) 获取指定进制的大整数public static BigInteger valueOf(long val) 静态方法获取BigInteger的对象,内部有优化细节:对象一旦创建里面的数据不能发生改变。*///1.获取一个随机的大整数/* Random r=new Random();for (int i = 0; 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.字符串中的数字必须要跟进制吻合。//比如二进制中,那么只能写0和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);//trueBigInteger 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}
}
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.maxBigInteger 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}
}
7 BigDecimal类
7.1 引入
首先我们来分析一下如下程序的执行结果:
public class BigDecimalDemo01 {public static void main(String[] args) {System.out.println(0.09 + 0.01);}}
这段代码比较简单,就是计算0.09和0.01之和,并且将其结果在控制台进行输出。那么按照我们的想法在控制台输出的结果应该为0.1。那么实际的运行结果是什么呢?我们来运行一下程序,控制台的输出
结果如下所示:
0.09999999999999999
这样的结果其实就是一个丢失精度的结果。为什么会产生精度丢失呢?
在使用float或者double类型的数据在进行数学运算的时候,很有可能会产生精度丢失问题。我们都知道计算机底层在进行运算的时候,使用的都是二进制数据; 当我们在程序中写了一个十进制数据 ,在
进行运算的时候,计算机会将这个十进制数据转换成二进制数据,然后再进行运算,计算完毕以后计算机会把运算的结果再转换成十进制数据给我们展示; 如果我们使用的是整数类型的数据进行计算,那
么在把十进制数据转换成二进制数据的时候不会存在精度问题; 如果我们的数据是一个浮点类型的数据,有的时候计算机并不会将这个数据完全转换成一个二进制数据,而是将这个将其转换成一个无限的
趋近于这个十进数的二进制数据; 这样使用一个不太准确的数据进行运算的时候, 最终就会造成精度丢失;为了提高精度,Java就给我们提供了BigDecimal供我们进行数据运算。
7.2 概述
查看API文档,我们可以看到API文档中关于BigDecimal类的定义如下:
BigDecimal所在包是在java.math包下,因此在使用的时候就需要进行导包。我们可以使用BigDecimal类进行更加精准的数据计算。
7.3 常见方法
构造方法
要用BigDecimal类,那么就需要首先学习一下如何去创建BigDecimal的对象。通过查看API文档,我们可以发现Jdk中针对BigDecimal类提供了很多的构造方法,但是最常用的构造方法是:
了解完常见的构造方法以后,我们接下来就重点介绍一下常见的成员方法。
常见成员方法
BigDecimal类中使用最多的还是提供的进行四则运算的方法,如下:
public BigDecimal add(BigDecimal value) // 加法运算
public BigDecimal subtract(BigDecimal value) // 减法运算
public BigDecimal multiply(BigDecimal value) // 乘法运算
public BigDecimal divide(BigDecimal value) // 触发运算
案例1:演示基本的四则运算
代码如下所示:
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)); // 进行除法运算}}
案例2:演示除法的特殊情况
如果使用BigDecimal类型的数据进行除法运算的时候,得到的结果是一个无限循环小数,那么就会报错:ArithmeticException。 如下代码所示:
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));}}
运行程序进行测试,控制台输出结果如下所示:
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方法,如下所示:
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
上述divide方法参数说明:
divisor: 除数对应的BigDecimal对象;
scale: 精确的位数;
roundingMode: 取舍模式;
取舍模式被封装到了RoundingMode这个枚举类中(关于枚举我们后期再做重点讲解),在这个枚举类中定义了很多种取舍方式。最常见的取舍方式有如下几个:
UP(直接进1) , FLOOR(直接删除) , HALF_UP(4舍五入),我们可以通过如下格式直接访问这些取舍模式:枚举类名.变量名
接下来我们就来演示一下这些取舍模式,代码如下所示:
public class BigDecimalDemo02 {public static void main(String[] args) {// 调用方法method_03() ;}// 演示取舍模式HALF_UPpublic 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));}// 演示取舍模式FLOORpublic 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));}// 演示取舍模式UPpublic 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));}}
7.4 底层存储方式:
把数据看成字符串,遍历得到里面的每一个字符,把这些字符在ASCII码表上的值,都存储到数组中。
正则表达式
1.1 正则表达式的概念及演示
- 在Java中,我们经常需要验证一些字符串,例如:年龄必须是2位的数字、用户名必须是8位长度而且只能包含大小写字母、数字等。正则表达式就是用来验证各种字符串的规则。它内部描述了一些规则,我们可以验证用户输入的字符串是否匹配这个规则。
- 先看一个不使用正则表达式验证的例子:下面的程序让用户输入一个QQ号码,我们要验证:
- QQ号码必须是5–15位长度
- 而且必须全部是数字
- 而且首位不能为0
package com.itheima.a08regexdemo;public class RegexDemo1 {public static void main(String[] args) {/* 假如现在要求校验一个qq号码是否正确。规则:6位及20位之内,日不能在开头,必须全部是数字。先使用目前所学知识完成校验需求然后体验一下正则表达式检验。*/String qq ="1234567890";System.out.println(checkQQ(qq));System.out.println(qq.matches("[1-9]\\d{5,19}"));}public static boolean checkQQ(String qq) {//规则:6位及20位之内,日不能在开头,必须全部是数字 。//核心思想://先把异常数据进行过滤//下面的就是满足要求的数据了。int len = qq.length();if (len < 6 || len > 20) {return false;}//0不能在开头if (qq.startsWith("0")) {return false;}//必须全部是数字for (int i = 0; i < qq.length(); i++) {char c = qq.charAt(i);if (c < '0' | c > '9') {return false;}}return true;}
}
- 使用正则表达式验证:
public class Demo {public static void main(String[] args) {String qq ="1234567890";System.out.println(qq.matches("[1-9]\\d{5,19}"));}
}
1.2 正则表达式-字符类
- 语法示例:
- [abc]:代表a或者b,或者c字符中的一个。
- [^abc]:代表除a,b,c以外的任何字符。
- [a-z]:代表a-z的所有小写字符中的一个。
- [A-Z]:代表A-Z的所有大写字符中的一个。
- [0-9]:代表0-9之间的某一个数字字符。
- [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
- [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符。
- 代码示例:
package com.itheima.a08regexdemo;public class RegexDemo2 {public static void main(String[] args) {//public boolean matches(String regex):判断是否与正则表达式匹配,匹配返回true// 只能是a b cSystem.out.println("-----------1-------------");System.out.println("a".matches("[abc]")); // trueSystem.out.println("z".matches("[abc]")); // false// 不能出现a b cSystem.out.println("-----------2-------------");System.out.println("a".matches("[^abc]")); // falseSystem.out.println("z".matches("[^abc]")); // trueSystem.out.println("zz".matches("[^abc]")); //falseSystem.out.println("zz".matches("[^abc][^abc]")); //true// a到zA到Z(包括头尾的范围)System.out.println("-----------3-------------");System.out.println("a".matches("[a-zA-z]")); // trueSystem.out.println("z".matches("[a-zA-z]")); // trueSystem.out.println("aa".matches("[a-zA-z]"));//falseSystem.out.println("zz".matches("[a-zA-Z]")); //falseSystem.out.println("zz".matches("[a-zA-Z][a-zA-Z]")); //trueSystem.out.println("0".matches("[a-zA-Z]"));//falseSystem.out.println("0".matches("[a-zA-Z0-9]"));//true// [a-d[m-p]] a到d,或m到pSystem.out.println("-----------4-------------");System.out.println("a".matches("[a-d[m-p]]"));//trueSystem.out.println("d".matches("[a-d[m-p]]")); //trueSystem.out.println("m".matches("[a-d[m-p]]")); //trueSystem.out.println("p".matches("[a-d[m-p]]")); //trueSystem.out.println("e".matches("[a-d[m-p]]")); //falseSystem.out.println("0".matches("[a-d[m-p]]")); //false// [a-z&&[def]] a-z和def的交集。为:d,e,fSystem.out.println("----------5------------");System.out.println("a".matches("[a-z&[def]]")); //falseSystem.out.println("d".matches("[a-z&&[def]]")); //trueSystem.out.println("0".matches("[a-z&&[def]]")); //false// [a-z&&[^bc]] a-z和非bc的交集。(等同于[ad-z])System.out.println("-----------6------------_");System.out.println("a".matches("[a-z&&[^bc]]"));//trueSystem.out.println("b".matches("[a-z&&[^bc]]")); //falseSystem.out.println("0".matches("[a-z&&[^bc]]")); //false// [a-z&&[^m-p]] a到z和除了m到p的交集。(等同于[a-1q-z])System.out.println("-----------7-------------");System.out.println("a".matches("[a-z&&[^m-p]]")); //trueSystem.out.println("m".matches("[a-z&&[^m-p]]")); //falseSystem.out.println("0".matches("[a-z&&[^m-p]]")); //false}
}
1.3 正则表达式-逻辑运算符
- 语法示例:
- &&:并且
- | :或者
- \ :转义字符
- 代码示例:
public class Demo {public static void main(String[] args) {String str = "had";//1.要求字符串是小写辅音字符开头,后跟adString regex = "[a-z&&[^aeiou]]ad";System.out.println("1." + str.matches(regex));//2.要求字符串是aeiou中的某个字符开头,后跟adregex = "[a|e|i|o|u]ad";//这种写法相当于:regex = "[aeiou]ad";System.out.println("2." + str.matches(regex));}
}
package com.itheima.a08regexdemo;public class RegexDemo3 {public static void main(String[] args) {// \ 转义字符 改变后面那个字符原本的含义//练习:以字符串的形式打印一个双引号//"在Java中表示字符串的开头或者结尾//此时\表示转义字符,改变了后面那个双引号原本的含义//把他变成了一个普普通通的双引号而已。System.out.println("\"");// \表示转义字符//两个\的理解方式:前面的\是一个转义字符,改变了后面\原本的含义,把他变成一个普普通通的\而已。System.out.println("c:Users\\moon\\IdeaProjects\\basic-code\\myapi\\src\\com\\itheima\\a08regexdemo\\RegexDemo1.java");}
}
1.4 正则表达式-预定义字符
- 语法示例:
- “.” : 匹配任何字符。
- “\d”:任何数字[0-9]的简写;
- “\D”:任何非数字[^0-9]的简写;
- “\s”: 空白字符:[ \t\n\x0B\f\r] 的简写
- “\S”: 非空白字符:[^\s] 的简写
- “\w”:单词字符:[a-zA-Z_0-9]的简写
- “\W”:非单词字符:[^\w]
- 代码示例:
public class Demo {public static void main(String[] args) {//.表示任意一个字符System.out.println("你".matches("..")); //falseSystem.out.println("你".matches(".")); //trueSystem.out.println("你a".matches(".."));//true// \\d 表示任意的一个数字// \\d只能是任意的一位数字// 简单来记:两个\表示一个\System.out.println("a".matches("\\d")); // falseSystem.out.println("3".matches("\\d")); // trueSystem.out.println("333".matches("\\d")); // false//\\w只能是一位单词字符[a-zA-Z_0-9]System.out.println("z".matches("\\w")); // trueSystem.out.println("2".matches("\\w")); // trueSystem.out.println("21".matches("\\w")); // falseSystem.out.println("你".matches("\\w"));//false// 非单词字符System.out.println("你".matches("\\W")); // trueSystem.out.println("---------------------------------------------");// 以上正则匹配只能校验单个字符。// 必须是数字 字母 下划线 至少 6位System.out.println("2442fsfsf".matches("\\w{6,}"));//trueSystem.out.println("244f".matches("\\w{6,}"));//false// 必须是数字和字符 必须是4位System.out.println("23dF".matches("[a-zA-Z0-9]{4}"));//trueSystem.out.println("23 F".matches("[a-zA-Z0-9]{4}"));//falseSystem.out.println("23dF".matches("[\\w&&[^_]]{4}"));//trueSystem.out.println("23_F".matches("[\\w&&[^_]]{4}"));//false}
}
1.5 正则表达式-数量词
- 语法示例:
- X? : 0次或1次
- X* : 0次到多次
- X+ : 1次或多次
- X{n} : 恰好n次
- X{n,} : 至少n次
- X{n,m}: n到m次(n和m都是包含的)
- 代码示例:
public class Demo {public static void main(String[] args) {// 必须是数字 字母 下划线 至少 6位System.out.println("2442fsfsf".matches("\\w{6,}"));//trueSystem.out.println("244f".matches("\\w{6,}"));//false// 必须是数字和字符 必须是4位System.out.println("23dF".matches("[a-zA-Z0-9]{4}"));//trueSystem.out.println("23 F".matches("[a-zA-Z0-9]{4}"));//falseSystem.out.println("23dF".matches("[\\w&&[^_]]{4}"));//trueSystem.out.println("23_F".matches("[\\w&&[^_]]{4}"));//false}
}
1.6 正则表达式练习1
需求:
请编写正则表达式验证用户输入的手机号码是否满足要求。
请编写正则表达式验证用户输入的邮箱号是否满足要求。
请编写正则表达式验证用户输入的电话号码是否满足要求。
验证手机号码 13112345678 13712345667 13945679027 139456790271
验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434
验证邮箱号码 3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn
代码示例:
package com.itheima.a08regexdemo;public class RegexDemo4 {public static void main(String[] args) {/*需求请编写正则表达式验证用户输入的手机号码是否满足要求。请编写正则表达式验证用户输入的邮箱号是否满足要求。请编写正则表达式验证用户输入的电话号码是否满足要求。验证手机号码 13112345678 13712345667 13945679027 139456790271验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434验证邮箱号码 3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn*///心得://拿着一个正确的数据,从左到右依次去写。//13112345678//分成三部分://第一部分:1 表示手机号码只能以1开头//第二部分:[3-9] 表示手机号码第二位只能是3-9之间的//第三部分:\\d{9} 表示任意数字可以出现9次,也只能出现9次String regex1 = "1[3-9]\\d{9}";System.out.println("13112345678".matches(regex1));//trueSystem.out.println("13712345667".matches(regex1));//trueSystem.out.println("13945679027".matches(regex1));//trueSystem.out.println("139456790271".matches(regex1));//falseSystem.out.println("-----------------------------------");//座机电话号码//020-2324242 02122442 027-42424 0712-3242434//思路://在书写座机号正则的时候需要把正确的数据分为三部分//一:区号@\\d{2,3}// 0:表示区号一定是以0开头的// \\d{2,3}:表示区号从第二位开始可以是任意的数字,可以出现2到3次。//二:- ?表示次数,0次或一次//三:号码 号码的第一位也不能以0开头,从第二位开始可以是任意的数字,号码的总长度:5-10位String regex2 = "0\\d{2,3}-?[1-9]\\d{4,9}";System.out.println("020-2324242".matches(regex2));System.out.println("02122442".matches(regex2));System.out.println("027-42424".matches(regex2));System.out.println("0712-3242434".matches(regex2));//邮箱号码//3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn//思路://在书写邮箱号码正则的时候需要把正确的数据分为三部分//第一部分:@的左边 \\w+// 任意的字母数字下划线,至少出现一次就可以了//第二部分:@ 只能出现一次//第三部分:// 3.1 .的左边[\\w&&[^_]]{2,6}// 任意的字母加数字,总共出现2-6次(此时不能出现下划线)// 3.2 . \\. \\. = \.// 3.3 大写字母,小写字母都可以,只能出现2-3次[a-zA-Z]{2,3}// 我们可以把3.2和3.3看成一组,这一组可以出现1次或者两次String regex3 = "\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}";System.out.println("3232323@qq.com".matches(regex3));System.out.println("zhangsan@itcast.cnn".matches(regex3));System.out.println("dlei0009@163.com".matches(regex3));System.out.println("dlei0009@pci.com.cn".matches(regex3));//24小时的正则表达式String regex4 = "([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d";System.out.println("23:11:11".matches(regex4));String regex5 = "([01]\\d 2[0-3])(:[0-5]\\d){2}";System.out.println("23:11:11".matches(regex5));}
}
1.7 正则表达式练习2
需求
请编写正则表达式验证用户名是否满足要求。要求:大小写字母,数字,下划线一共4-16位
请编写正则表达式验证身份证号码是否满足要求。
简单要求:
18位,前17位任意数字,最后一位可以是数字可以是大写或小写的x
复杂要求:
按照身份证号码的格式严格要求。
身份证号码:
41080119930228457x
510801197609022309
15040119810705387X
130133197204039024
430102197606046442
代码示例:
public class RegexDemo5 {public static void main(String[] args) {/*正则表达式练习:需求请编写正则表达式验证用户名是否满足要求。要求:大小写字母,数字,下划线一共4-16位请编写正则表达式验证身份证号码是否满足要求。简单要求:18位,前17位任意数字,最后一位可以是数字可以是大写或小写的x复杂要求:按照身份证号码的格式严格要求。身份证号码:41080119930228457x51080119760902230915040119810705387X130133197204039024 I430102197606046442*///用户名要求:大小写字母,数字,下划线一共4-16位String regex1 = "\\w{4,16}";System.out.println("zhangsan".matches(regex1));System.out.println("lisi".matches(regex1));System.out.println("wangwu".matches(regex1));System.out.println("$123".matches(regex1));//身份证号码的简单校验://18位,前17位任意数字,最后一位可以是数字可以是大写或小写的xString regex2 = "[1-9]\\d{16}(\\d|x|x)";String regex3 = "[1-9]\\d{16}[\\dXx]";String regex5 = "[1-9]\\d{16}(\\d(?i)x)";System.out.println("41080119930228457x".matches(regex3));System.out.println("510801197609022309".matches(regex3));System.out.println("15040119810705387X".matches(regex3));System.out.println("130133197204039024".matches(regex3));System.out.println("430102197606046442".matches(regex3));//忽略大小写的书写方式//在匹配的时候忽略abc的大小写String regex4 = "a((?i)b)c";System.out.println("------------------------------");System.out.println("abc".matches(regex4));//trueSystem.out.println("ABC".matches(regex4));//falseSystem.out.println("aBc".matches(regex4));//true//身份证号码的严格校验//编写正则的小心得://第一步:按照正确的数据进行拆分//第二步:找每一部分的规律,并编写正则表达式//第三步:把每一部分的正则拼接在一起,就是最终的结果//书写的时候:从左到右去书写。//410801 1993 02 28 457x//前面6位:省份,市区,派出所等信息,第一位不能是0,后面5位是任意数字 [1-9]\\d{5}//年的前半段: 18 19 20 (18|19|20)//年的后半段: 任意数字出现两次 \\d{2}//月份: 01~ 09 10 11 12 (@[1-9]|1[0-2])//日期: 01~09 10~19 20~29 30 31 (0[1-9]|[12]\\d|3[01])//后面四位: 任意数字出现3次 最后一位可以是数字也可以是大写x或者小写x \\d{3}[\\dXx]String regex6 = "[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]";System.out.println("41080119930228457x".matches(regex6));System.out.println("510801197609022309".matches(regex6));System.out.println("15040119810705387X".matches(regex6));System.out.println("130133197204039024".matches(regex6));System.out.println("430102197606046442".matches(regex6));}
}
1.8 本地数据爬取
Pattern:表示正则表达式
Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取。
在大串中去找符合匹配规则的子串。
代码示例:
package com.itheima.a08regexdemo;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class RegexDemo6 {public static void main(String[] args) {/* 有如下文本,请按照要求爬取数据。Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台要求:找出里面所有的JavaXX*/String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";//1.获取正则表达式的对象Pattern p = Pattern.compile("Java\\d{0,2}");//2.获取文本匹配器的对象//拿着m去读取str,找符合p规则的子串Matcher m = p.matcher(str);//3.利用循环获取while (m.find()) {String s = m.group();System.out.println(s);}}private static void method1(String str) {//Pattern:表示正则表达式//Matcher: 文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取。// 在大串中去找符合匹配规则的子串。//获取正则表达式的对象Pattern p = Pattern.compile("Java\\d{0,2}");//获取文本匹配器的对象//m:文本匹配器的对象//str:大串//p:规则//m要在str中找符合p规则的小串Matcher m = p.matcher(str);//拿着文本匹配器从头开始读取,寻找是否有满足规则的子串//如果没有,方法返回false//如果有,返回true。在底层记录子串的起始索引和结束索引+1// 0,4boolean b = m.find();//方法底层会根据find方法记录的索引进行字符串的截取// substring(起始索引,结束索引);包头不包尾// (0,4)但是不包含4索引// 会把截取的小串进行返回。String s1 = m.group();System.out.println(s1);//第二次在调用find的时候,会继续读取后面的内容//读取到第二个满足要求的子串,方法会继续返回true//并把第二个子串的起始索引和结束索引+1,进行记录b = m.find();//第二次调用group方法的时候,会根据find方法记录的索引再次截取子串String s2 = m.group();System.out.println(s2);}
}
1.10 爬取数据练习
需求:
把下面文本中的座机电话,邮箱,手机号,热线都爬取出来。
来黑马程序员学习Java,手机号:18512516758,18512508907或者联系邮箱:boniu@itcast.cn,座机电话:01036517895,010-98951256邮箱:bozai@itcast.cn,热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090手机号的正则表达式:1[3-9]\d{9}
代码示例:
package com.itheima.a08regexdemo;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class RegexDemo8 {public static void main(String[] args) {/*需求:把下面文本中的座机电话,邮箱,手机号,热线都爬取出来。来黑马程序员学习Java,手机号:18512516758,18512508907或者联系邮箱:boniu@itcast.cn,座机电话:01036517895,010-98951256邮箱:bozai@itcast.cn,热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090手机号的正则表达式:1[3-9]\d{9}邮箱的正则表达式:\w+@[\w&&[^_]]{2,6}(\.[a-zA-Z]{2,3}){1,2}座机电话的正则表达式:θ\d{2,3}-?[1-9]\d{4,9}热线电话的正则表达式:400-?[1-9]\\d{2}-?[1-9]\\d{3}*/String s = "来黑马程序员学习Java," +"电话:18512516758,18512508907" + "或者联系邮箱:boniu@itcast.cn," +"座机电话:01036517895,010-98951256" + "邮箱:bozai@itcast.cn," +"热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";System.out.println("400-618-9090");String regex = "(1[3-9]\\d{9})|(\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2})" +"|(0\\d{2,3}-?[1-9]\\d{4,9})" +"(400-?[1-9]\\d{2}-?[1-9]\\d{3})";//1.获取正则表达式的对象Pattern p = Pattern.compile(regex);//2.获取文本匹配器的对象//利用m去读取s,会按照p的规则找里面的小串Matcher m = p.matcher(s);//3.利用循环获取每一个数据 while(m.find()){String str = m.group();System.out.println(str);}
}
1.11 按要求爬取
需求:
有如下文本,按要求爬取数据。
Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台。
需求1:
爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。
需求2:
爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17
需求3:
爬取除了版本号为8,11,17的Java文本。
代码示例:
public class RegexDemo9 {public static void main(String[] args) {/*有如下文本,按要求爬取数据。Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台需求1:爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。需求2:爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17需求3:爬取除了版本号为8,11.17的Java文本,*/String s = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";//1.定义正则表达式//?理解为前面的数据Java//?i 表示忽略大小写//=表示在Java后面要跟随的数据//但是在获取的时候,只获取前半部分//需求1:String regex1 = "((?i)Java)(?=8|11|17)";//需求2:String regex2 = "((?i)Java)(8|11|17)";String regex3 = "((?i)Java)(?:8|11|17)";//需求3:String regex4 = "((?i)Java)(?!8|11|17)";Pattern p = Pattern.compile(regex4);Matcher m = p.matcher(s);while (m.find()) {System.out.println(m.group());}}
}
1.12 贪婪爬取和非贪婪爬取
只写+和表示贪婪匹配,如果在+和后面加问号表示非贪婪爬取
+? 非贪婪匹配
*? 非贪婪匹配
贪婪爬取:在爬取数据的时候尽可能的多获取数据
非贪婪爬取:在爬取数据的时候尽可能的少获取数据
举例:
如果获取数据:ab+
贪婪爬取获取结果:abbbbbbbbbbbb
非贪婪爬取获取结果:ab
public class RegexDemo10 {public static void main(String[] args) {/*只写+和*表示贪婪匹配+? 非贪婪匹配*? 非贪婪匹配贪婪爬取:在爬取数据的时候尽可能的多获取数据非贪婪爬取:在爬取数据的时候尽可能的少获取数据ab+:贪婪爬取:abbbbbbbbbbbb非贪婪爬取:ab*/String s = "Java自从95年问世以来,abbbbbbbbbbbbaaaaaaaaaaaaaaaaaa" +"经历了很多版木,目前企业中用的最多的是]ava8和]ava11,因为这两个是长期支持版木。" +"下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";String regex = "ab+";Pattern p = Pattern.compile(regex);Matcher m = p.matcher(s);while (m.find()) {System.out.println(m.group());}}
}
1.13 String的split方法中使用正则表达式
- String类的split()方法原型:
public String[] split(String regex)
//参数regex表示正则表达式。可以将当前字符串中匹配regex正则表达式的符号作为"分隔符"来切割字符串。
/*有一段字符串:小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠要求1:把字符串中三个姓名之间的字母替换为vs要求2:把字符串中的三个姓名切割出来*/String s = "小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
//细节:
//方法在底层跟之前一样也会创建文本解析器的对象
//然后从头开始去读取字符串中的内容,只要有满足的,那么就切割。
String[] arr = s.split("[\\w&&[^_]]+");
for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);
}
1.15 正则表达式-分组括号( )
细节:如何识别组号?
只看左括号,不看有括号,按照左括号的顺序,从左往右,依次为第一组,第二组,第三组等等
//需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
//举例: a123a b456b 17891 &abc& a123b(false)
// \\组号:表示把第X组的内容再出来用一次
String regex1 = "(.).+\\1";
System.out.println("a123a".matches(regex1));
System.out.println("b456b".matches(regex1));
System.out.println("17891".matches(regex1));
System.out.println("&abc&".matches(regex1));
System.out.println("a123b".matches(regex1));
System.out.println("--------------------------");//需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
//举例: abc123abc b456b 123789123 &!@abc&!@ abc123abd(false)
String regex2 = "(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
System.out.println("b456b".matches(regex2));
System.out.println("123789123".matches(regex2));
System.out.println("&!@abc&!@".matches(regex2));
System.out.println("abc123abd".matches(regex2));
System.out.println("---------------------");//需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
//举例: aaa123aaa bbb456bbb 111789111 &&abc&&
//(.):把首字母看做一组
// \\2:把首字母拿出来再次使用
// *:作用于\\2,表示后面重复的内容出现0次或多次
String regex3 = "((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex3));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("aaa123aab".matches(regex3));
1.16 分组练习
需求:
将字符串:我要学学编编编编程程程程程程。
替换为:我要学编程
String str = "我要学学编编编编程程程程程程";//需求:把重复的内容 替换为 单个的
//学学 学
//编编编编 编
//程程程程程程 程
// (.)表示把重复内容的第一个字符看做一组
// \\1表示第一字符再次出现
// + 至少一次
// $1 表示把正则表达式中第一组的内容,再拿出来用
String result = str.replaceAll("(.)\\1+", "$1");
System.out.println(result);
1.17 忽略大小写的写法
//(?i) :表示忽略后面数据的大小写
//忽略abc的大小写
String regex = "(?i)abc";
//a需要一模一样,忽略bc的大小写
String regex = "a(?i)bc";
//ac需要一模一样,忽略b的大小写
String regex = "a((?i)b)c";
1.18 非捕获分组
非捕获分组:分组之后不需要再用本组数据,仅仅是把数据括起来。
//身份证号码的简易正则表达式
//非捕获分组:仅仅是把数据括起来
//特点:不占用组号
//这里\\1报错原因:(?:)就是非捕获分组,此时是不占用组号的。//(?:) (?=) (?!)都是非捕获分组//更多的使用第一个
//String regex1 ="[1-9]\\d{16}(?:\\d|x|x)\\1";
String regex2 ="[1-9]\\d{16}(\\d Xx)\\1";
//^([01]\d|2[0-3]):[0-5]\d:[@-5]\d$System.out.println("41080119930228457x".matches(regex2));
1.19 正则表达式练习
手机号码:1[3-9]\\d{9}
座机号码:0\\d{2,3}-?[1-9]\\d{4,9}
邮箱号码:\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}
24小时:([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d([01]\\d|2[0-3])(:[0-5]\\d){2}
用户名: \\w{4,16}
身份证号码,简单校验:[1-9]\\d{16}(\\d|X|x)[1-9]\\d{16}[\\dXx][1-9]\\d{16}(\\d(?i)X)
身份证号码,严格校验:[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9|[12])\\d|3[01])\\d{3}[\\dXx]
第一章 Date类
1.1 Date概述
java.util.Date`类 表示特定的瞬间,精确到毫秒。
继续查阅Date类的描述,发现Date拥有多个构造函数,只是部分已经过时,我们重点看以下两个构造函数
public Date()
:从运行程序的此时此刻到时间原点经历的毫秒值,转换成Date对象,分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。public Date(long date)
:将指定参数的毫秒值date,转换成Date对象,分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。
tips: 由于中国处于东八区(GMT+08:00)是比世界协调时间/格林尼治时间(GMT)快8小时的时区,当格林尼治标准时间为0:00时,东八区的标准时间为08:00。
简单来说:使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定long类型的构造参数,可以自定义毫秒时刻。例如:
import java.util.Date;public class Demo01Date {public static void main(String[] args) {// 创建日期对象,把当前的时间System.out.println(new Date()); // Tue Jan 16 14:37:35 CST 2020// 创建日期对象,把当前的毫秒值转成日期对象System.out.println(new Date(0L)); // Thu Jan 01 08:00:00 CST 1970}
}
1.2 Date常用方法
Date类中的多数方法已经过时,常用的方法有:
public long getTime()
把日期对象转换成对应的时间毫秒值。public void setTime(long time)
把方法参数给定的毫秒值设置给日期对象
示例代码
public class DateDemo02 {public static void main(String[] args) {//创建日期对象Date d = new Date();//public long getTime():获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值//System.out.println(d.getTime());//System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");//public void setTime(long time):设置时间,给的是毫秒值//long time = 1000*60*60;long time = System.currentTimeMillis();d.setTime(time);System.out.println(d);}
}
小结:Date表示特定的时间瞬间,我们可以使用Date对象对时间进行操作。
第二章 SimpleDateFormat类
java.text.SimpleDateFormat
是日期/时间格式化类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。
- 格式化:按照指定的格式,把Date对象转换为String对象。
- 解析:按照指定的格式,把String对象转换为Date对象。
2.1 构造方法
由于DateFormat为抽象类,不能直接使用,所以需要常用的子类java.text.SimpleDateFormat
。这个类需要一个模式(格式)来指定格式化或解析的标准。构造方法为:
public SimpleDateFormat(String pattern)
:用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数pattern是一个字符串,代表日期时间的自定义格式。
2.2 格式规则
常用的格式规则为:
标识字母(区分大小写) | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
2.3 常用方法
DateFormat类的常用方法有:
-
public String format(Date date)
:将Date对象格式化为字符串。 -
public Date parse(String source)
:将字符串解析为Date对象。
package com.itheima.a01jdk7datedemo;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class A03_SimpleDateFormatDemo1 {public static void main(String[] args) throws ParseException {/*public simpleDateFormat() 默认格式public simpleDateFormat(String pattern) 指定格式public final string format(Date date) 格式化(日期对象 ->字符串)public Date parse(string source) 解析(字符串 ->日期对象)*///1.定义一个字符串表示时间String str = "2023-11-11 11:11:11";//2.利用空参构造创建simpleDateFormat对象// 细节://创建对象的格式要跟字符串的格式完全一致SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf.parse(str);//3.打印结果System.out.println(date.getTime());//1699672271000}private static void method1() {//1.利用空参构造创建simpleDateFormat对象,默认格式SimpleDateFormat sdf1 = new SimpleDateFormat();Date d1 = new Date(0L);String str1 = sdf1.format(d1);System.out.println(str1);//1970/1/1 上午8:00//2.利用带参构造创建simpleDateFormat对象,指定格式SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");String str2 = sdf2.format(d1);System.out.println(str2);//1970年01月01日 08:00:00//课堂练习:yyyy年MM月dd日 时:分:秒 星期}
}
2.5 练习2(秒杀活动)
/* 需求:秒杀活动开始时间:2023年11月11日 0:0:0(毫秒值)秒杀活动结束时间:2023年11月11日 0:10:0(毫秒值)小贾下单并付款的时间为:2023年11月11日 0:01:0小皮下单并付款的时间为:2023年11月11日 0:11:0用代码说明这两位同学有没有参加上秒杀活动?*///1.定义字符串表示三个时间
String startstr = "2023年11月11日 0:0:0";
String endstr = "2023年11月11日 0:10:0";
String orderstr = "2023年11月11日 0:01:00";
//2.解析上面的三个时间,得到Date对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
Date startDate = sdf.parse(startstr);
Date endDate = sdf.parse(endstr);
Date orderDate = sdf.parse(orderstr);//3.得到三个时间的毫秒值
long startTime = startDate.getTime();
long endTime = endDate.getTime();
long orderTime = orderDate.getTime();//4.判断
if (orderTime >= startTime && orderTime <= endTime) {System.out.println("参加秒杀活动成功");
} else {System.out.println("参加秒杀活动失败");
}
第三章 Calendar类
3.1 概述
- java.util.Calendar类表示一个“日历类”,可以进行日期运算。它是一个抽象类,不能创建对象,我们可以使用它的子类:java.util.GregorianCalendar类。
- 有两种方式可以获取GregorianCalendar对象:
- 直接创建GregorianCalendar对象;
- 通过Calendar的静态方法getInstance()方法获取GregorianCalendar对象【本次课使用】
3.2 常用方法
方法名 | 说明 |
---|---|
public static Calendar getInstance() | 获取一个它的子类GregorianCalendar对象。 |
public int get(int field) | 获取某个字段的值。field参数表示获取哪个字段的值, 可以使用Calender中定义的常量来表示: Calendar.YEAR : 年 Calendar.MONTH :月 Calendar.DAY_OF_MONTH:月中的日期 Calendar.HOUR:小时 Calendar.MINUTE:分钟 Calendar.SECOND:秒 Calendar.DAY_OF_WEEK:星期 |
public void set(int field,int value) | 设置某个字段的值 |
public void add(int field,int amount) | 为某个字段增加/减少指定的值 |
3.3 get方法示例
public class Demo {public static void main(String[] args) {//1.获取一个GregorianCalendar对象Calendar instance = Calendar.getInstance();//获取子类对象//2.打印子类对象System.out.println(instance);//3.获取属性int year = instance.get(Calendar.YEAR);int month = instance.get(Calendar.MONTH) + 1;//Calendar的月份值是0-11int day = instance.get(Calendar.DAY_OF_MONTH);int hour = instance.get(Calendar.HOUR);int minute = instance.get(Calendar.MINUTE);int second = instance.get(Calendar.SECOND);int week = instance.get(Calendar.DAY_OF_WEEK);//返回值范围:1--7,分别表示:"星期日","星期一","星期二",...,"星期六"System.out.println(year + "年" + month + "月" + day + "日" + hour + ":" + minute + ":" + second);System.out.println(getWeek(week));}//查表法,查询星期几public static String getWeek(int w) {//w = 1 --- 7//做一个表(数组)String[] weekArray = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};// 索引 [0] [1] [2] [3] [4] [5] [6]//查表return weekArray[w - 1];}
}
3.4 set方法示例:
public class Demo {public static void main(String[] args) {//设置属性——set(int field,int value):Calendar c1 = Calendar.getInstance();//获取当前日期//计算班长出生那天是星期几(假如班长出生日期为:1998年3月18日)c1.set(Calendar.YEAR, 1998);c1.set(Calendar.MONTH, 3 - 1);//转换为Calendar内部的月份值c1.set(Calendar.DAY_OF_MONTH, 18);int w = c1.get(Calendar.DAY_OF_WEEK);System.out.println("班长出生那天是:" + getWeek(w));}//查表法,查询星期几public static String getWeek(int w) {//w = 1 --- 7//做一个表(数组)String[] weekArray = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};// 索引 [0] [1] [2] [3] [4] [5] [6]//查表return weekArray[w - 1];}
}
3.5 add方法示例:
public class Demo {public static void main(String[] args) {//计算200天以后是哪年哪月哪日,星期几?Calendar c2 = Calendar.getInstance();//获取当前日期c2.add(Calendar.DAY_OF_MONTH, 200);//日期加200int y = c2.get(Calendar.YEAR);int m = c2.get(Calendar.MONTH) + 1;//转换为实际的月份int d = c2.get(Calendar.DAY_OF_MONTH);int wk = c2.get(Calendar.DAY_OF_WEEK);System.out.println("200天后是:" + y + "年" + m + "月" + d + "日" + getWeek(wk));}//查表法,查询星期几public static String getWeek(int w) {//w = 1 --- 7//做一个表(数组)String[] weekArray = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};// 索引 [0] [1] [2] [3] [4] [5] [6]//查表return weekArray[w - 1];}
}
第四章 JDK8时间相关类
JDK8时间类类名 | 作用 |
---|---|
ZoneId | 时区 |
Instant | 时间戳 |
ZoneDateTime | 带时区的时间 |
DateTimeFormatter | 用于时间的格式化和解析 |
LocalDate | 年、月、日 |
LocalTime | 时、分、秒 |
LocalDateTime | 年、月、日、时、分、秒 |
Duration | 时间间隔(秒,纳,秒) |
Period | 时间间隔(年,月,日) |
ChronoUnit | 时间间隔(所有单位) |
4.1 ZoneId 时区
/*static Set<string> getAvailableZoneIds() 获取Java中支持的所有时区static ZoneId systemDefault() 获取系统默认时区static Zoneld of(string zoneld) 获取一个指定时区*///1.获取所有的时区名称
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println(zoneIds.size());//600
System.out.println(zoneIds);// Asia/Shanghai//2.获取当前系统的默认时区
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);//Asia/Shanghai//3.获取指定的时区
ZoneId zoneId1 = ZoneId.of("Asia/Pontianak");
System.out.println(zoneId1);//Asia/Pontianak
4.2 Instant 时间戳
/*static Instant now() 获取当前时间的Instant对象(标准时间)static Instant ofXxxx(long epochMilli) 根据(秒/毫秒/纳秒)获取Instant对象ZonedDateTime atZone(ZoneIdzone) 指定时区boolean isxxx(Instant otherInstant) 判断系列的方法Instant minusXxx(long millisToSubtract) 减少时间系列的方法Instant plusXxx(long millisToSubtract) 增加时间系列的方法*/
//1.获取当前时间的Instant对象(标准时间)
Instant now = Instant.now();
System.out.println(now);//2.根据(秒/毫秒/纳秒)获取Instant对象
Instant instant1 = Instant.ofEpochMilli(0L);
System.out.println(instant1);//1970-01-01T00:00:00zInstant instant2 = Instant.ofEpochSecond(1L);
System.out.println(instant2);//1970-01-01T00:00:01ZInstant instant3 = Instant.ofEpochSecond(1L, 1000000000L);
System.out.println(instant3);//1970-01-01T00:00:027//3. 指定时区
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(time);//4.isXxx 判断
Instant instant4=Instant.ofEpochMilli(0L);
Instant instant5 =Instant.ofEpochMilli(1000L);//5.用于时间的判断
//isBefore:判断调用者代表的时间是否在参数表示时间的前面
boolean result1=instant4.isBefore(instant5);
System.out.println(result1);//true//isAfter:判断调用者代表的时间是否在参数表示时间的后面
boolean result2 = instant4.isAfter(instant5);
System.out.println(result2);//false//6.Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant instant6 =Instant.ofEpochMilli(3000L);
System.out.println(instant6);//1970-01-01T00:00:03ZInstant instant7 =instant6.minusSeconds(1);
System.out.println(instant7);//1970-01-01T00:00:02Z
4.3 ZoneDateTime 带时区的时间
/*static ZonedDateTime now() 获取当前时间的ZonedDateTime对象static ZonedDateTime ofXxxx(。。。) 获取指定时间的ZonedDateTime对象ZonedDateTime withXxx(时间) 修改时间系列的方法ZonedDateTime minusXxx(时间) 减少时间系列的方法ZonedDateTime plusXxx(时间) 增加时间系列的方法*/
//1.获取当前时间对象(带时区)
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);//2.获取指定的时间对象(带时区)1/年月日时分秒纳秒方式指定
ZonedDateTime time1 = ZonedDateTime.of(2023, 10, 1,11, 12, 12, 0, ZoneId.of("Asia/Shanghai"));
System.out.println(time1);//通过Instant + 时区的方式指定获取时间对象
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time2 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(time2);//3.withXxx 修改时间系列的方法
ZonedDateTime time3 = time2.withYear(2000);
System.out.println(time3);//4. 减少时间
ZonedDateTime time4 = time3.minusYears(1);
System.out.println(time4);//5.增加时间
ZonedDateTime time5 = time4.plusYears(1);
System.out.println(time5);
4.4DateTimeFormatter 用于时间的格式化和解析
/*static DateTimeFormatter ofPattern(格式) 获取格式对象String format(时间对象) 按照指定方式格式化*/
//获取时间对象
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));// 解析/格式化器
DateTimeFormatter dtf1=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm;ss EE a");
// 格式化
System.out.println(dtf1.format(time));
4.5LocalDate 年、月、日
//1.获取当前时间的日历对象(包含 年月日)
LocalDate nowDate = LocalDate.now();
//System.out.println("今天的日期:" + nowDate);
//2.获取指定的时间的日历对象
LocalDate ldDate = LocalDate.of(2023, 1, 1);
System.out.println("指定日期:" + ldDate);System.out.println("=============================");//3.get系列方法获取日历中的每一个属性值//获取年
int year = ldDate.getYear();
System.out.println("year: " + year);
//获取月//方式一:
Month m = ldDate.getMonth();
System.out.println(m);
System.out.println(m.getValue());//方式二:
int month = ldDate.getMonthValue();
System.out.println("month: " + month);//获取日
int day = ldDate.getDayOfMonth();
System.out.println("day:" + day);//获取一年的第几天
int dayofYear = ldDate.getDayOfYear();
System.out.println("dayOfYear:" + dayofYear);//获取星期
DayOfWeek dayOfWeek = ldDate.getDayOfWeek();
System.out.println(dayOfWeek);
System.out.println(dayOfWeek.getValue());//is开头的方法表示判断
System.out.println(ldDate.isBefore(ldDate));
System.out.println(ldDate.isAfter(ldDate));//with开头的方法表示修改,只能修改年月日
LocalDate withLocalDate = ldDate.withYear(2000);
System.out.println(withLocalDate);//minus开头的方法表示减少,只能减少年月日
LocalDate minusLocalDate = ldDate.minusYears(1);
System.out.println(minusLocalDate);//plus开头的方法表示增加,只能增加年月日
LocalDate plusLocalDate = ldDate.plusDays(1);
System.out.println(plusLocalDate);//-------------
// 判断今天是否是你的生日
LocalDate birDate = LocalDate.of(2000, 1, 1);
LocalDate nowDate1 = LocalDate.now();MonthDay birMd = MonthDay.of(birDate.getMonthValue(), birDate.getDayOfMonth());
MonthDay nowMd = MonthDay.from(nowDate1);System.out.println("今天是你的生日吗? " + birMd.equals(nowMd));//今天是你的生日吗?
4.6 LocalTime 时、分、秒
// 获取本地时间的日历对象。(包含 时分秒)
LocalTime nowTime = LocalTime.now();
System.out.println("今天的时间:" + nowTime);int hour = nowTime.getHour();//时
System.out.println("hour: " + hour);int minute = nowTime.getMinute();//分
System.out.println("minute: " + minute);int second = nowTime.getSecond();//秒
System.out.println("second:" + second);int nano = nowTime.getNano();//纳秒
System.out.println("nano:" + nano);
System.out.println("------------------------------------");
System.out.println(LocalTime.of(8, 20));//时分
System.out.println(LocalTime.of(8, 20, 30));//时分秒
System.out.println(LocalTime.of(8, 20, 30, 150));//时分秒纳秒
LocalTime mTime = LocalTime.of(8, 20, 30, 150);//is系列的方法
System.out.println(nowTime.isBefore(mTime));
System.out.println(nowTime.isAfter(mTime));//with系列的方法,只能修改时、分、秒
System.out.println(nowTime.withHour(10));//plus系列的方法,只能修改时、分、秒
System.out.println(nowTime.plusHours(10));
4.7 LocalDateTime 年、月、日、时、分、秒
// 当前时间的的日历对象(包含年月日时分秒)
LocalDateTime nowDateTime = LocalDateTime.now();System.out.println("今天是:" + nowDateTime);//今天是:
System.out.println(nowDateTime.getYear());//年
System.out.println(nowDateTime.getMonthValue());//月
System.out.println(nowDateTime.getDayOfMonth());//日
System.out.println(nowDateTime.getHour());//时
System.out.println(nowDateTime.getMinute());//分
System.out.println(nowDateTime.getSecond());//秒
System.out.println(nowDateTime.getNano());//纳秒
// 日:当年的第几天
System.out.println("dayofYear:" + nowDateTime.getDayOfYear());
//星期
System.out.println(nowDateTime.getDayOfWeek());
System.out.println(nowDateTime.getDayOfWeek().getValue());
//月份
System.out.println(nowDateTime.getMonth());
System.out.println(nowDateTime.getMonth().getValue());LocalDate ld = nowDateTime.toLocalDate();
System.out.println(ld);LocalTime lt = nowDateTime.toLocalTime();
System.out.println(lt.getHour());
System.out.println(lt.getMinute());
System.out.println(lt.getSecond());
4.8 Duration 时间间隔(秒,纳,秒)
// 本地日期时间对象。
LocalDateTime today = LocalDateTime.now();
System.out.println(today);// 出生的日期时间对象
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
System.out.println(birthDate);Duration duration = Duration.between(birthDate, today);//第二个参数减第一个参数
System.out.println("相差的时间间隔对象:" + duration);System.out.println("============================================");
System.out.println(duration.toDays());//两个时间差的天数
System.out.println(duration.toHours());//两个时间差的小时数
System.out.println(duration.toMinutes());//两个时间差的分钟数
System.out.println(duration.toMillis());//两个时间差的毫秒数
System.out.println(duration.toNanos());//两个时间差的纳秒数
4.9 Period 时间间隔(年,月,日)
// 当前本地 年月日
LocalDate today = LocalDate.now();
System.out.println(today);// 生日的 年月日
LocalDate birthDate = LocalDate.of(2000, 1, 1);
System.out.println(birthDate);Period period = Period.between(birthDate, today);//第二个参数减第一个参数System.out.println("相差的时间间隔对象:" + period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());System.out.println(period.toTotalMonths());
4.10 ChronoUnit 时间间隔(所有单位)
// 当前时间
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
// 生日时间
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1,0, 0, 0);
System.out.println(birthDate);System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));
System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today));
System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));
System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));
System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));
System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));
System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));
System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));
System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));
System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));
System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today));
System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today));
System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today));
System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today));
System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today));
第五章 包装类
5.1 概述
Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
5.2 Integer类
-
Integer类概述
包装一个对象中的原始类型 int 的值
-
Integer类构造方法及静态方法
方法名 | 说明 |
---|---|
public Integer(int value) | 根据 int 值创建 Integer 对象(过时) |
public Integer(String s) | 根据 String 值创建 Integer 对象(过时) |
public static Integer valueOf(int i) | 返回表示指定的 int 值的 Integer 实例 |
public static Integer valueOf(String s) | 返回保存指定String值的 Integer 对象 |
static string tobinarystring(int i) | 得到二进制 |
static string tooctalstring(int i) | 得到八进制 |
static string toHexstring(int i) | 得到十六进制 |
static int parseInt(string s) | 将字符串类型的整数转成int类型的整数 |
- 示例代码
//public Integer(int value):根据 int 值创建 Integer 对象(过时)
Integer i1 = new Integer(100);
System.out.println(i1);//public Integer(String s):根据 String 值创建 Integer 对象(过时)
Integer i2 = new Integer("100");
//Integer i2 = new Integer("abc"); //NumberFormatException
System.out.println(i2);
System.out.println("--------");//public static Integer valueOf(int i):返回表示指定的 int 值的 Integer 实例
Integer i3 = Integer.valueOf(100);
System.out.println(i3);//public static Integer valueOf(String s):返回保存指定String值的Integer对象
Integer i4 = Integer.valueOf("100");
System.out.println(i4);
/*public static string tobinarystring(int i) 得到二进制public static string tooctalstring(int i) 得到八进制public static string toHexstring(int i) 得到十六进制public static int parseInt(string s) 将字符串类型的整数转成int类型的整数*///1.把整数转成二进制,十六进制
String str1 = Integer.toBinaryString(100);
System.out.println(str1);//1100100//2.把整数转成八进制
String str2 = Integer.toOctalString(100);
System.out.println(str2);//144//3.把整数转成十六进制
String str3 = Integer.toHexString(100);
System.out.println(str3);//64//4.将字符串类型的整数转成int类型的整数
//强类型语言:每种数据在java中都有各自的数据类型
//在计算的时候,如果不是同一种数据类型,是无法直接计算的。
int i = Integer.parseInt("123");
System.out.println(i);
System.out.println(i + 1);//124
//细节1:
//在类型转换的时候,括号中的参数只能是数字不能是其他,否则代码会报错
//细节2:
//8种包装类当中,除了Character都有对应的parseXxx的方法,进行类型转换
String str = "true";
boolean b = Boolean.parseBoolean(str);
System.out.println(b);
5.3 装箱与拆箱
基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:
- 装箱:从基本类型转换为对应的包装类对象。
- 拆箱:从包装类对象转换为对应的基本类型。
用Integer与 int为例:(看懂代码即可)
基本数值---->包装对象
Integer i = new Integer(4);//使用构造函数函数
Integer iii = Integer.valueOf(4);//使用包装类中的valueOf方法
包装对象---->基本数值
int num = i.intValue();
5.4 自动装箱与自动拆箱
由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。
5.5 基本类型与字符串之间的转换
基本类型转换为String
- 转换方式
- 方式一:直接在数字后加一个空字符串
- 方式二:通过String类静态方法valueOf()
- 示例代码
public class IntegerDemo {public static void main(String[] args) {//int --- Stringint number = 100;//方式1String s1 = number + "";System.out.println(s1);//方式2//public static String valueOf(int i)String s2 = String.valueOf(number);System.out.println(s2);System.out.println("--------");}
}
String转换成基本类型
除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型:
public static byte parseByte(String s)
:将字符串参数转换为对应的byte基本类型。public static short parseShort(String s)
:将字符串参数转换为对应的short基本类型。public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型。public static long parseLong(String s)
:将字符串参数转换为对应的long基本类型。public static float parseFloat(String s)
:将字符串参数转换为对应的float基本类型。public static double parseDouble(String s)
:将字符串参数转换为对应的double基本类型。public static boolean parseBoolean(String s)
:将字符串参数转换为对应的boolean基本类型。
代码使用(仅以Integer类的静态方法parseXxx为例)如:
- 转换方式
- 方式一:先将字符串数字转成Integer,再调用valueOf()方法
- 方式二:通过Integer静态方法parseInt()进行转换
- 示例代码
public class IntegerDemo {public static void main(String[] args) {//String --- intString s = "100";//方式1:String --- Integer --- intInteger i = Integer.valueOf(s);//public int intValue()int x = i.intValue();System.out.println(x);//方式2//public static int parseInt(String s)int y = Integer.parseInt(s);System.out.println(y);}
}
5.6 底层原理
建议:获取Integer对象的时候不要自己new,而是采取直接赋值或者静态方法valueOf的方式
因为在实际开发中,-128~127之间的数据,用的比较多。如果每次使用都是new对象,那么太浪费内存了。
所以,提前把这个范围之内的每一个数据都创建好对象,如果要用到了不会创建新的,而是返回已经创建好的对象。
//1.利用构造方法获取Integer的对象(JDK5以前的方式)
/*Integer i1 = new Integer(1);Integer i2 = new Integer("1");System.out.println(i1);System.out.println(i2);*///2.利用静态方法获取Integer的对象(JDK5以前的方式)
Integer i3 = Integer.valueOf(123);
Integer i4 = Integer.valueOf("123");
Integer i5 = Integer.valueOf("123", 8);System.out.println(i3);
System.out.println(i4);
System.out.println(i5);//3.这两种方式获取对象的区别(掌握)
//底层原理:
//因为在实际开发中,-128~127之间的数据,用的比较多。
//如果每次使用都是new对象,那么太浪费内存了
//所以,提前把这个范围之内的每一个数据都创建好对象
//如果要用到了不会创建新的,而是返回已经创建好的对象。
Integer i6 = Integer.valueOf(127);
Integer i7 = Integer.valueOf(127);
System.out.println(i6 == i7);//trueInteger i8 = Integer.valueOf(128);
Integer i9 = Integer.valueOf(128);
System.out.println(i8 == i9);//false//因为看到了new关键字,在Java中,每一次new都是创建了一个新的对象
//所以下面的两个对象都是new出来,地址值不一样。
/*Integer i10 = new Integer(127);Integer i11 = new Integer(127);System.out.println(i10 == i11);Integer i12 = new Integer(128);Integer i13 = new Integer(128);System.out.println(i12 == i13);*/
练习四:
需求:
请使用代码实现计算你活了多少天,用JDK7和JDK8两种方式完成
代码示例:
public class Test4 {public static void main(String[] args) throws ParseException {//请使用代码实现计算你活了多少天,用JDK7和JDK8两种方式完成//JDK7//规则:只要对时间进行计算或者判断,都需要先获取当前时间的毫秒值//1.计算出生年月日的毫秒值String birthday = "2000年1月1日";SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");Date date = sdf.parse(birthday);long birthdayTime = date.getTime();//2.获取当前时间的毫秒值long todayTime = System.currentTimeMillis();//3.计算间隔多少天long time = todayTime - birthdayTime;System.out.println(time / 1000 / 60 / 60 / 24);//JDK8LocalDate ld1 = LocalDate.of(2000, 1, 1);LocalDate ld2 = LocalDate.now();long days = ChronoUnit.DAYS.between(ld1, ld2);System.out.println(days);}
}
常见的七种查找算法:
1. 基本查找
也叫做顺序查找
说明:顺序查找适合于存储结构为数组或者链表。
基本思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线的一端开始,顺序扫描,依次将遍历到的结点与要查找的值相比较,若相等则表示查找成功;若遍历结束仍没有找到相同的,表示查找失败。
示例代码:
public class A01_BasicSearchDemo1 {public static void main(String[] args) {//基本查找/顺序查找//核心://从0索引开始挨个往后查找//需求:定义一个方法利用基本查找,查询某个元素是否存在//数据如下:{131, 127, 147, 81, 103, 23, 7, 79}int[] arr = {131, 127, 147, 81, 103, 23, 7, 79};int number = 82;System.out.println(basicSearch(arr, number));}//参数://一:数组//二:要查找的元素//返回值://元素是否存在public static boolean basicSearch(int[] arr, int number){//利用基本查找来查找number在数组中是否存在for (int i = 0; i < arr.length; i++) {if(arr[i] == number){return true;}}return false;}
}
2. 二分查找
也叫做折半查找
说明:元素必须是有序的,从小到大,或者从大到小都是可以的。
如果是无序的,也可以先进行排序。但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。
基本思想:也称为是折半查找,属于有序查找算法。用给定值先与中间结点比较。比较完之后有三种情况:
-
相等
说明找到了
-
要查找的数据比中间节点小
说明要查找的数字在中间节点左边
-
要查找的数据比中间节点大
说明要查找的数字在中间节点右边
代码示例:
package com.itheima.search;public class A02_BinarySearchDemo1 {public static void main(String[] args) {//二分查找/折半查找//核心://每次排除一半的查找范围//需求:定义一个方法利用二分查找,查询某个元素在数组中的索引//数据如下:{7, 23, 79, 81, 103, 127, 131, 147}int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};System.out.println(binarySearch(arr, 150));}public static int binarySearch(int[] arr, int number){//1.定义两个变量记录要查找的范围int min = 0;int max = arr.length - 1;//2.利用循环不断的去找要查找的数据while(true){if(min > max){return -1;}//3.找到min和max的中间位置int mid = (min + max) / 2;//4.拿着mid指向的元素跟要查找的元素进行比较if(arr[mid] > number){//4.1 number在mid的左边//min不变,max = mid - 1;max = mid - 1;}else if(arr[mid] < number){//4.2 number在mid的右边//max不变,min = mid + 1;min = mid + 1;}else{//4.3 number跟mid指向的元素一样//找到了return mid;}}}
}
3. 插值查找
在介绍插值查找之前,先考虑一个问题:
为什么二分查找算法一定要是折半,而不是折四分之一或者折更多呢?
其实就是因为方便,简单,但是如果我能在二分查找的基础上,让中间的mid点,尽可能靠近想要查找的元素,那不就能提高查找的效率了吗?
二分查找中查找点计算如下:
mid=(low+high)/2, 即mid=low+1/2*(high-low);
我们可以将查找的点改进为如下:
mid=low+(key-a[low])/(a[high]-a[low])*(high-low),
这样,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。
基本思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。
**细节:**对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
代码跟二分查找类似,只要修改一下mid的计算方式即可。
4. 斐波那契查找
在介绍斐波那契查找算法之前,我们先介绍一下很它紧密相连并且大家都熟知的一个概念——黄金分割。
黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。
0.618被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。因此被称为黄金分割。
在数学中有一个非常有名的数学规律:斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….
(从第三个数开始,后边每一个数都是前两个数的和)。
然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
基本思想:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
斐波那契查找也是在二分查找的基础上进行了优化,优化中间点mid的计算方式即可
代码示例:
public class FeiBoSearchDemo {public static int maxSize = 20;public static void main(String[] args) {int[] arr = {1, 8, 10, 89, 1000, 1234};System.out.println(search(arr, 1234));}public static int[] getFeiBo() {int[] arr = new int[maxSize];arr[0] = 1;arr[1] = 1;for (int i = 2; i < maxSize; i++) {arr[i] = arr[i - 1] + arr[i - 2];}return arr;}public static int search(int[] arr, int key) {int low = 0;int high = arr.length - 1;//表示斐波那契数分割数的下标值int index = 0;int mid = 0;//调用斐波那契数列int[] f = getFeiBo();//获取斐波那契分割数值的下标while (high > (f[index] - 1)) {index++;}//因为f[k]值可能大于a的长度,因此需要使用Arrays工具类,构造一个新法数组,并指向temp[],不足的部分会使用0补齐int[] temp = Arrays.copyOf(arr, f[index]);//实际需要使用arr数组的最后一个数来填充不足的部分for (int i = high + 1; i < temp.length; i++) {temp[i] = arr[high];}//使用while循环处理,找到key值while (low <= high) {mid = low + f[index - 1] - 1;if (key < temp[mid]) {//向数组的前面部分进行查找high = mid - 1;/*对k--进行理解1.全部元素=前面的元素+后面的元素2.f[k]=k[k-1]+f[k-2]因为前面有k-1个元素没所以可以继续分为f[k-1]=f[k-2]+f[k-3]即在f[k-1]的前面继续查找k--即下次循环,mid=f[k-1-1]-1*/index--;} else if (key > temp[mid]) {//向数组的后面的部分进行查找low = mid + 1;index -= 2;} else {//找到了//需要确定返回的是哪个下标if (mid <= high) {return mid;} else {return high;}}}return -1;}
}
5. 分块查找
当数据表中的数据元素很多时,可以采用分块查找。
汲取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找
分块查找适用于数据较多,但是数据不会发生变化的情况,如果需要一边添加一边查找,建议使用哈希查找
分块查找的过程:
- 需要把数据分成N多小块,块与块之间不能有数据重复的交集。
- 给每一块创建对象单独存储到数组当中
- 查找数据的时候,先在数组查,当前数据属于哪一块
- 再到这一块中顺序查找
代码示例:
package com.itheima.search;public class A03_BlockSearchDemo {public static void main(String[] args) {/*分块查找核心思想:块内无序,块间有序实现步骤:1.创建数组blockArr存放每一个块对象的信息2.先查找blockArr确定要查找的数据属于哪一块3.再单独遍历这一块数据即可*/int[] arr = {16, 5, 9, 12,21, 18,32, 23, 37, 26, 45, 34,50, 48, 61, 52, 73, 66};//创建三个块的对象Block b1 = new Block(21,0,5);Block b2 = new Block(45,6,11);Block b3 = new Block(73,12,17);//定义数组用来管理三个块的对象(索引表)Block[] blockArr = {b1,b2,b3};//定义一个变量用来记录要查找的元素int number = 37;//调用方法,传递索引表,数组,要查找的元素int index = getIndex(blockArr,arr,number);//打印一下System.out.println(index);}//利用分块查找的原理,查询number的索引private static int getIndex(Block[] blockArr, int[] arr, int number) {//1.确定number是在那一块当中int indexBlock = findIndexBlock(blockArr, number);if(indexBlock == -1){//表示number不在数组当中return -1;}//2.获取这一块的起始索引和结束索引 --- 30// Block b1 = new Block(21,0,5); ---- 0// Block b2 = new Block(45,6,11); ---- 1// Block b3 = new Block(73,12,17); ---- 2int startIndex = blockArr[indexBlock].getStartIndex();int endIndex = blockArr[indexBlock].getEndIndex();//3.遍历for (int i = startIndex; i <= endIndex; i++) {if(arr[i] == number){return i;}}return -1;}//定义一个方法,用来确定number在哪一块当中public static int findIndexBlock(Block[] blockArr,int number){ //100//从0索引开始遍历blockArr,如果number小于max,那么就表示number是在这一块当中的for (int i = 0; i < blockArr.length; i++) {if(number <= blockArr[i].getMax()){return i;}}return -1;}}class Block{private int max;//最大值private int startIndex;//起始索引private int endIndex;//结束索引public Block() {}public Block(int max, int startIndex, int endIndex) {this.max = max;this.startIndex = startIndex;this.endIndex = endIndex;}/*** 获取* @return max*/public int getMax() {return max;}/*** 设置* @param max*/public void setMax(int max) {this.max = max;}/*** 获取* @return startIndex*/public int getStartIndex() {return startIndex;}/*** 设置* @param startIndex*/public void setStartIndex(int startIndex) {this.startIndex = startIndex;}/*** 获取* @return endIndex*/public int getEndIndex() {return endIndex;}/*** 设置* @param endIndex*/public void setEndIndex(int endIndex) {this.endIndex = endIndex;}public String toString() {return "Block{max = " + max + ", startIndex = " + startIndex + ", endIndex = " + endIndex + "}";}
}
6. 哈希查找
哈希查找是分块查找的进阶版,适用于数据一边添加一边查找的情况。
一般是数组 + 链表的结合体或者是数组+链表 + 红黑树的结合体
在课程中,为了让大家方便理解,所以规定:
- 数组的0索引处存储1~100
- 数组的1索引处存储101~200
- 数组的2索引处存储201~300
- 以此类推
但是实际上,我们一般不会采取这种方式,因为这种方式容易导致一块区域添加的元素过多,导致效率偏低。
更多的是先计算出当前数据的哈希值,用哈希值跟数组的长度进行计算,计算出应存入的位置,再挂在数组的后面形成链表,如果挂的元素太多而且数组长度过长,我们也会把链表转化为红黑树,进一步提高效率。
7. 树表查找
本知识点涉及到数据结构:树。
建议先看一下后面阿玮讲解的数据结构,再回头理解。
基本思想:二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。 这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树。
二叉查找树(BinarySearch Tree,也叫二叉搜索树,或称二叉排序树Binary Sort Tree),具有下列性质的二叉树:
1)若任意节点左子树上所有的数据,均小于本身;
2)若任意节点右子树上所有的数据,均大于本身;
二叉查找树性质:对二叉查找树进行中序遍历,即可得到有序的数列。
不同形态的二叉查找树如下图所示:
十大排序算法:
1. 冒泡排序
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。
它重复的遍历过要排序的数列,一次比较相邻的两个元素,如果他们的顺序错误就把他们交换过来。
这个算法的名字由来是因为越大的元素会经由交换慢慢"浮"到最后面。
当然,大家可以按照从大到小的方式进行排列。
1.1 算法步骤
- 相邻的元素两两比较,大的放右边,小的放左边
- 第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推
- 如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以
1.3 代码示例
public class A01_BubbleDemo {public static void main(String[] args) {/*冒泡排序:核心思想:1,相邻的元素两两比较,大的放右边,小的放左边。2,第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推。3,如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以。*///1.定义数组int[] arr = {2, 4, 5, 3, 1};//2.利用冒泡排序将数组中的数据变成 1 2 3 4 5//外循环:表示我要执行多少轮。 如果有n个数据,那么执行n - 1 轮for (int i = 0; i < arr.length - 1; i++) {//内循环:每一轮中我如何比较数据并找到当前的最大值//-1:为了防止索引越界//-i:提高效率,每一轮执行的次数应该比上一轮少一次。for (int j = 0; j < arr.length - 1 - i; j++) {//i 依次表示数组中的每一个索引:0 1 2 3 4if(arr[j] > arr[j + 1]){int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}printArr(arr);}private static void printArr(int[] arr) {//3.遍历数组for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}
}
2. 选择排序
2.1 算法步骤
- 从0索引开始,跟后面的元素一一比较
- 小的放前面,大的放后面
- 第一次循环结束后,最小的数据已经确定
- 第二次循环从1索引开始以此类推
- 第三轮循环从2索引开始以此类推
- 第四轮循环从3索引开始以此类推。
2.2 动图展示
public class A02_SelectionDemo {public static void main(String[] args) {/*选择排序:1,从0索引开始,跟后面的元素一一比较。2,小的放前面,大的放后面。3,第一次循环结束后,最小的数据已经确定。4,第二次循环从1索引开始以此类推。*///1.定义数组int[] arr = {2, 4, 5, 3, 1};//2.利用选择排序让数组变成 1 2 3 4 5/* //第一轮://从0索引开始,跟后面的元素一一比较。for (int i = 0 + 1; i < arr.length; i++) {//拿着0索引跟后面的数据进行比较if(arr[0] > arr[i]){int temp = arr[0];arr[0] = arr[i];arr[i] = temp;}}*///最终代码://外循环:几轮//i:表示这一轮中,我拿着哪个索引上的数据跟后面的数据进行比较并交换for (int i = 0; i < arr.length -1; i++) {//内循环:每一轮我要干什么事情?//拿着i跟i后面的数据进行比较交换for (int j = i + 1; j < arr.length; j++) {if(arr[i] > arr[j]){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}}printArr(arr);}private static void printArr(int[] arr) {//3.遍历数组for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}}
3. 插入排序
插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过创建有序序列和无序序列,然后再遍历无序序列得到里面每一个数字,把每一个数字插入到有序序列中正确的位置。
插入排序在插入的时候,有优化算法,在遍历有序序列找正确位置时,可以采取二分查找
3.1 算法步骤
将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。
遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。
N的范围:0~最大索引
package com.itheima.mysort;public class A03_InsertDemo {public static void main(String[] args) {/*插入排序:将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。N的范围:0~最大索引*/int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};//1.找到无序的哪一组数组是从哪个索引开始的。 2int startIndex = -1;for (int i = 0; i < arr.length; i++) {if(arr[i] > arr[i + 1]){startIndex = i + 1;break;}}//2.遍历从startIndex开始到最后一个元素,依次得到无序的哪一组数据中的每一个元素for (int i = startIndex; i < arr.length; i++) {//问题:如何把遍历到的数据,插入到前面有序的这一组当中//记录当前要插入数据的索引int j = i;while(j > 0 && arr[j] < arr[j - 1]){//交换位置int temp = arr[j];arr[j] = arr[j - 1];arr[j - 1] = temp;j--;}}printArr(arr);}private static void printArr(int[] arr) {//3.遍历数组for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}}
4. 快速排序
快速排序是由东尼·霍尔所发展的一种排序算法。
快速排序又是一种分而治之思想在排序算法上的典型应用。
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!
它是处理大数据最快的排序算法之一了。
4.1 算法步骤
- 从数列中挑出一个元素,一般都是左边第一个数字,称为 “基准数”;
- 创建两个指针,一个从前往后走,一个从后往前走。
- 先执行后面的指针,找出第一个比基准数小的数字
- 再执行前面的指针,找出第一个比基准数大的数字
- 交换两个指针指向的数字
- 直到两个指针相遇
- 将基准数跟指针指向位置的数字交换位置,称之为:基准数归位。
- 第一轮结束之后,基准数左边的数字都是比基准数小的,基准数右边的数字都是比基准数大的。
- 把基准数左边看做一个序列,把基准数右边看做一个序列,按照刚刚的规则递归排序
package com.itheima.mysort;import java.util.Arrays;public class A05_QuickSortDemo {public static void main(String[] args) {System.out.println(Integer.MAX_VALUE);System.out.println(Integer.MIN_VALUE);/*快速排序:第一轮:以0索引的数字为基准数,确定基准数在数组中正确的位置。比基准数小的全部在左边,比基准数大的全部在右边。后面以此类推。*/int[] arr = {1,1, 6, 2, 7, 9, 3, 4, 5, 1,10, 8};//int[] arr = new int[1000000];/* Random r = new Random();for (int i = 0; i < arr.length; i++) {arr[i] = r.nextInt();}*/long start = System.currentTimeMillis();quickSort(arr, 0, arr.length - 1);long end = System.currentTimeMillis();System.out.println(end - start);//149System.out.println(Arrays.toString(arr));//课堂练习://我们可以利用相同的办法去测试一下,选择排序,冒泡排序以及插入排序运行的效率//得到一个结论:快速排序真的非常快。/* for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}*/}/** 参数一:我们要排序的数组* 参数二:要排序数组的起始索引* 参数三:要排序数组的结束索引* */public static void quickSort(int[] arr, int i, int j) {//定义两个变量记录要查找的范围int start = i;int end = j;if(start > end){//递归的出口return;}//记录基准数int baseNumber = arr[i];//利用循环找到要交换的数字while(start != end){//利用end,从后往前开始找,找比基准数小的数字//int[] arr = {1, 6, 2, 7, 9, 3, 4, 5, 10, 8};while(true){if(end <= start || arr[end] < baseNumber){break;}end--;}System.out.println(end);//利用start,从前往后找,找比基准数大的数字while(true){if(end <= start || arr[start] > baseNumber){break;}start++;}//把end和start指向的元素进行交换int temp = arr[start];arr[start] = arr[end];arr[end] = temp;}//当start和end指向了同一个元素的时候,那么上面的循环就会结束//表示已经找到了基准数在数组中应存入的位置//基准数归位//就是拿着这个范围中的第一个数字,跟start指向的元素进行交换int temp = arr[i];arr[i] = arr[start];arr[start] = temp;//确定6左边的范围,重复刚刚所做的事情quickSort(arr,i,start - 1);//确定6右边的范围,重复刚刚所做的事情quickSort(arr,start + 1,j);}
}
1.Collection集合
1.1数组和集合的区别【理解】
-
相同点
都是容器,可以存储多个数据
-
不同点
-
数组的长度是不可变的,集合的长度是可变的
-
数组可以存基本数据类型和引用数据类型
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
-
1.3Collection 集合概述和使用【应用】
-
Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
-
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
-
Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
1.4Collection集合的遍历
1.4.1 迭代器遍历
-
迭代器介绍
- 迭代器,集合的专用遍历方式
- Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
-
Iterator中的常用方法
boolean hasNext(): 判断当前位置是否有元素可以被取出
E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置 -
Collection集合的遍历
public class IteratorDemo1 {public static void main(String[] args) {//创建集合对象Collection<String> c = new ArrayList<>();//添加元素c.add("hello");c.add("world");c.add("java");c.add("javaee");//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到Iterator<String> it = c.iterator();//用while循环改进元素的判断和获取while (it.hasNext()) {String s = it.next();System.out.println(s);}}
}
- 迭代器中删除的方法
void remove(): 删除迭代器对象当前指向的元素
public class IteratorDemo2 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("a");list.add("b");list.add("b");list.add("c");list.add("d");Iterator<String> it = list.iterator();while(it.hasNext()){String s = it.next();if("b".equals(s)){//指向谁,那么此时就删除谁.it.remove();}}System.out.println(list);}
}
1.4.2 增强for
-
介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
-
格式
for(集合/数组中元素的数据类型 变量名 : 集合/数组名) {
// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}
-
代码
public class MyCollectonDemo1 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("a");list.add("b");list.add("c");list.add("d");list.add("e");list.add("f");//1,数据类型一定是集合或者数组中元素的类型//2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素//3,list就是要遍历的集合或者数组for(String str : list){System.out.println(str);}}
}
- 细节点注意:
1.报错NoSuchElementException
2.迭代器遍历完毕,指针不会复位
3.循环中只能用一次next方法
4.迭代器遍历时,不能用集合的方法进行增加或者删除
public class A04_CollectionDemo4 {public static void main(String[] args) {/*迭代器的细节注意点:1.报错NoSuchElementException2.迭代器遍历完毕,指针不会复位3.循环中只能用一次next方法4.迭代器遍历时,不能用集合的方法进行增加或者删除暂时当做一个结论先行记忆,在今天我们会讲解源码详细的再来分析。如果我实在要删除:那么可以用迭代器提供的remove方法进行删除。如果我要添加,暂时没有办法。(只是暂时)*///1.创建集合并添加元素Collection<String> coll = new ArrayList<>();coll.add("aaa");coll.add("bbb");coll.add("ccc");coll.add("ddd");//2.获取迭代器对象//迭代器就好比是一个箭头,默认指向集合的0索引处Iterator<String> it = coll.iterator();//3.利用循环不断的去获取集合中的每一个元素while(it.hasNext()){//4.next方法的两件事情:获取元素并移动指针String str = it.next();System.out.println(str);}//当上面循环结束之后,迭代器的指针已经指向了最后没有元素的位置//System.out.println(it.next());//NoSuchElementException//迭代器遍历完毕,指针不会复位System.out.println(it.hasNext());//如果我们要继续第二次遍历集合,只能再次获取一个新的迭代器对象Iterator<String> it2 = coll.iterator();while(it2.hasNext()){String str = it2.next();System.out.println(str);}}
}
1.4.3 lambda表达式
利用forEach方法,再结合lambda表达式的方式进行遍历
public class A07_CollectionDemo7 {public static void main(String[] args) {/* lambda表达式遍历:default void forEach(Consumer<? super T> action):*///1.创建集合并添加元素Collection<String> coll = new ArrayList<>();coll.add("zhangsan");coll.add("lisi");coll.add("wangwu");//2.利用匿名内部类的形式//底层原理://其实也会自己遍历集合,依次得到每一个元素//把得到的每一个元素,传递给下面的accept方法//s依次表示集合中的每一个数据/* coll.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});*///lambda表达式coll.forEach(s -> System.out.println(s));}
}
2.List集合
2.1List集合的概述和特点【记忆】
- List集合的概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
- List集合的特点
- 存取有序
- 可以重复
- 有索引
2.2List集合的特有方法【应用】
-
方法介绍
方法名 描述 void add(int index,E element) 在此集合中的指定位置插入指定的元素 E remove(int index) 删除指定索引处的元素,返回被删除的元素 E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 E get(int index) 返回指定索引处的元素
public class MyListDemo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");//method1(list);//method2(list);//method3(list);//method4(list);}private static void method4(List<String> list) {// E get(int index) 返回指定索引处的元素String s = list.get(0);System.out.println(s);}private static void method3(List<String> list) {// E set(int index,E element) 修改指定索引处的元素,返回被修改的元素//被替换的那个元素,在集合中就不存在了.String result = list.set(0, "qqq");System.out.println(result);System.out.println(list);}private static void method2(List<String> list) {// E remove(int index) 删除指定索引处的元素,返回被删除的元素//在List集合中有两个删除的方法//第一个 删除指定的元素,返回值表示当前元素是否删除成功//第二个 删除指定索引的元素,返回值表示实际删除的元素String s = list.remove(0);System.out.println(s);System.out.println(list);}private static void method1(List<String> list) {// void add(int index,E element) 在此集合中的指定位置插入指定的元素//原来位置上的元素往后挪一个索引.list.add(0,"qqq");System.out.println(list);}
}
2.3List集合的五种遍历方式【应用】
- 迭代器
- 列表迭代器
- 增强for
- Lambda表达式
- 普通for循环
代码示例:
//创建集合并添加元素
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");//1.迭代器
/*Iterator<String> it = list.iterator();while(it.hasNext()){String str = it.next();System.out.println(str);
}*///2.增强for
//下面的变量s,其实就是一个第三方的变量而已。
//在循环的过程中,依次表示集合中的每一个元素
/* for (String s : list) {System.out.println(s);}*///3.Lambda表达式
//forEach方法的底层其实就是一个循环遍历,依次得到集合中的每一个元素
//并把每一个元素传递给下面的accept方法
//accept方法的形参s,依次表示集合中的每一个元素
//list.forEach(s->System.out.println(s) );//4.普通for循环
//size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素
/*for (int i = 0; i < list.size(); i++) {//i:依次表示集合中的每一个索引String s = list.get(i);System.out.println(s);}*/// 5.列表迭代器
//获取一个列表迭代器的对象,里面的指针默认也是指向0索引的//额外添加了一个方法:在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){String str = it.next();if("bbb".equals(str)){//qqqit.add("qqq");}
}
System.out.println(list);
2.4 细节点注意:
List系列集合中的两个删除的方法
1.直接删除元素
2.通过索引进行删除
代码示例:
//List系列集合中的两个删除的方法
//1.直接删除元素
//2.通过索引进行删除//1.创建集合并添加元素
List<Integer> list = new ArrayList<>();list.add(1);
list.add(2);
list.add(3);//2.删除元素
//请问:此时删除的是1这个元素,还是1索引上的元素?
//为什么?
//因为在调用方法的时候,如果方法出现了重载现象
//优先调用,实参跟形参类型一致的那个方法。//list.remove(1);//手动装箱,手动把基本数据类型的1,变成Integer类型
Integer i = Integer.valueOf(1);list.remove(i);System.out.println(list);
3.数据结构
3.1数据结构之栈和队列【记忆】
-
栈结构
先进后出
-
队列结构
先进先出
3.2数据结构之数组和链表【记忆】
-
数组结构
查询快、增删慢
-
队列结构
查询慢、增删快
4.List集合的实现类
4.1List集合子类的特点【记忆】
-
ArrayList集合
底层是数组结构实现,查询快、增删慢
-
LinkedList集合
底层是链表结构实现,查询慢、增删快
4.2LinkedList集合的特有功能【应用】
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
- 示例代码
public class MyLinkedListDemo4 {public static void main(String[] args) {LinkedList<String> list = new LinkedList<>();list.add("aaa");list.add("bbb");list.add("ccc");
// public void addFirst(E e) 在该列表开头插入指定的元素//method1(list);// public void addLast(E e) 将指定的元素追加到此列表的末尾//method2(list);// public E getFirst() 返回此列表中的第一个元素
// public E getLast() 返回此列表中的最后一个元素//method3(list);// public E removeFirst() 从此列表中删除并返回第一个元素
// public E removeLast() 从此列表中删除并返回最后一个元素//method4(list);}private static void method4(LinkedList<String> list) {String first = list.removeFirst();System.out.println(first);String last = list.removeLast();System.out.println(last);System.out.println(list);}private static void method3(LinkedList<String> list) {String first = list.getFirst();String last = list.getLast();System.out.println(first);System.out.println(last);}private static void method2(LinkedList<String> list) {list.addLast("www");System.out.println(list);}private static void method1(LinkedList<String> list) {list.addFirst("qqq");System.out.println(list);}
}
1.泛型
1.1泛型概述
-
泛型的介绍
泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制
-
泛型的好处
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
-
泛型的定义格式
- <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如:
- <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V>
2.Set集合
2.1Set集合概述和特点【应用】
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
2.2Set集合的使用【应用】
存储字符串并遍历
public class MySet1 {public static void main(String[] args) {//创建集合对象Set<String> set = new TreeSet<>();//添加元素set.add("ccc");set.add("aaa");set.add("aaa");set.add("bbb");// for (int i = 0; i < set.size(); i++) {
// //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
// }//遍历集合Iterator<String> it = set.iterator();while (it.hasNext()){String s = it.next();System.out.println(s);}System.out.println("-----------------------------------");for (String s : set) {System.out.println(s);}}
}
3.TreeSet集合
3.1TreeSet集合概述和特点【应用】
- 不可以存储重复元素
- 没有索引
- 可以将元素按照规则进行排序
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator) :根据指定的比较器进行排序
3.2TreeSet集合基本使用【应用】
存储Integer类型的整数并遍历
public class TreeSetDemo01 {public static void main(String[] args) {//创建集合对象TreeSet<Integer> ts = new TreeSet<Integer>();//添加元素ts.add(10);ts.add(40);ts.add(30);ts.add(50);ts.add(20);ts.add(30);//遍历集合for(Integer i : ts) {System.out.println(i);}}
}
3.3自然排序Comparable的使用【应用】
-
案例需求
- 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
-
实现步骤
- 使用空参构造创建TreeSet集合
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自定义的Student类实现Comparable接口
- 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
- 重写接口中的compareTo方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
- 使用空参构造创建TreeSet集合
-
代码实现
学生类
public class Student implements Comparable<Student>{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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {//按照对象的年龄进行排序//主要判断条件: 按照年龄从小到大排序int result = this.age - o.age;//次要判断条件: 年龄相同时,按照姓名的字母顺序排序result = result == 0 ? this.name.compareTo(o.getName()) : result;return result;}
}
测试类:
public class MyTreeSet2 {public static void main(String[] args) {//创建集合对象TreeSet<Student> ts = new TreeSet<>();//创建学生对象Student s1 = new Student("zhangsan",28);Student s2 = new Student("lisi",27);Student s3 = new Student("wangwu",29);Student s4 = new Student("zhaoliu",28);Student s5 = new Student("qianqi",30);//把学生添加到集合ts.add(s1);ts.add(s2);ts.add(s3);ts.add(s4);ts.add(s5);//遍历集合for (Student student : ts) {System.out.println(student);}}
}
3.4比较器排序Comparator的使用【应用】
-
案例需求
- 存储老师对象并遍历,创建TreeSet集合使用带参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
-
实现步骤
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
-
代码实现
老师类
public class Teacher {private String name;private int age;public Teacher() {}public Teacher(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;}@Overridepublic String toString() {return "Teacher{" +"name='" + name + '\'' +", age=" + age +'}';}
}
测试类
public class MyTreeSet4 {public static void main(String[] args) {//创建集合对象TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {@Overridepublic int compare(Teacher o1, Teacher o2) {//o1表示现在要存入的那个元素//o2表示已经存入到集合中的元素//主要条件int result = o1.getAge() - o2.getAge();//次要条件result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;return result;}});//创建老师对象Teacher t1 = new Teacher("zhangsan",23);Teacher t2 = new Teacher("lisi",22);Teacher t3 = new Teacher("wangwu",24);Teacher t4 = new Teacher("zhaoliu",24);//把老师添加到集合ts.add(t1);ts.add(t2);ts.add(t3);ts.add(t4);//遍历集合for (Teacher teacher : ts) {System.out.println(teacher);}}
}
3.5两种比较方式总结【理解】
- 两种比较方式小结
- 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
- 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
- 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
- 两种方式中关于返回值的规则
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
5.1HashSet集合概述和特点【应用】
- 底层数据结构是哈希表
- 存取无序
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
5.2HashSet集合的基本应用【应用】
存储字符串并遍历
public class HashSetDemo {public static void main(String[] args) {//创建集合对象HashSet<String> set = new HashSet<String>();//添加元素set.add("hello");set.add("world");set.add("java");//不包含重复元素的集合set.add("world");//遍历for(String s : set) {System.out.println(s);}}
}
5.3哈希值【理解】
-
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
-
如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值
-
哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同