API(全称 Application Programming Interface:应用程序编程接口)
API就是Java帮我们已经写好的一些程序,如类、方法等,可以直接拿过来用
JDK8 API文档:Java Platform SE 8
一. JDK8之前传统的日期、时间
1.1 Date
代表的是日期和时间
构造器 | 说明 |
public Date() | 创建一个Date对象,代表的是系统当前此刻日期时间 |
public Date(long time) | 把时间毫秒值转换成Date日期对象 |
常见方法 | 说明 |
public long getTime() | 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数 |
public void setTime(long time) | 设置日期对象的时间为当前时间毫秒值对应的时间 |
//demo
public class demo {public static void main(String[] args) {//创建一个Date对象,代表的是系统当前此刻日期时间Date d = new Date();System.out.println(d);//返回从1970年1月1日 00:00:00走到此刻的总的毫秒数long time = d.getTime();System.out.println(time);//把时间毫秒值转换成Date日期对象System.out.println(new Date(1473913749209L));//直接把日期对象的时间通过setTime()方法进行修改Date d3 = new Date();d3.setTime(1473913749209L);System.out.println(d3);}
}
1.2 SimpleDateFormat
SimpleDateFormat代表简单日期格式化,可以用来把日期对象、时间毫秒值格式化成我们想要的形式(格式化和解析日期)
常见构造器 | 说明 |
public SimpleDateFormat(String pattern) | 创建简单日期格式化对象,并封装时间的格式 |
格式化时间的方法 | 说明 |
public final String format(Date date) | 将日期格式化成日期/时间字符串 |
public final String format(Object time) | 将时间毫秒值式化成日期/时间字符串 |
public Date parse(String source) | 把字符串时间解析成日期对象 |
//demo
public class demo {public static void main(String[] args) throws ParseException {Date d = new Date();System.out.println(d);long time = d.getTime();System.out.println(time);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a");String s1 = sdf.format(d);System.out.println(s1);String s2 = sdf.format(time);System.out.println(s2);System.out.println("======================");//SimpleDateFormat解析字符串时间称为日期对象String dateStr = "2024-05-20 13:14:15";//创建SimpleDateFormat对象,指定的时间格式必须与被解析的时间格式一致,否则会出问题SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date a = sdf1.parse(dateStr);System.out.println(a);}
}
练习:秒杀活动
//demo
public class demo {public static void main(String[] args) throws ParseException {//活动开始和结束的时间用String保存String start = "2023年11月11日 0:0:0";String end = "2023年11月11日 0:10:0";//用户的下单时间用String保存String datestr1 = "2023年11月11日 0:01:18";String datestr2 = "2023年11月11日 0:10:51";//将用户下单时间从String类型转换成Date类型再到long型(时间毫秒值)SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");Date start_date = sdf.parse(start);Date end_date = sdf.parse(end);Date d1 = sdf.parse(datestr1);Date d2 = sdf.parse(datestr2);long start_time = start_date.getTime();long end_time = end_date.getTime();long d1_time = d1.getTime();long d2_time = d2.getTime();System.out.println(isOK(d1_time,start_time,end_time));System.out.println(isOK(d2_time,start_time,end_time));}public static String isOK(long d,long start,long end) {if(d >= start && d <= end){return "成功参与该活动";}else{return "未能成功参与该活动";}}
}
1.3 Calendar
代表的是系统此刻时间对应的日历,通过它可以单独获取、修改时间中的年、月、日、时、分、秒等
方法名 | 说明 |
public static Calendar getInstance() | 获取当前日历对象 |
public int get(int field) | 获取日历中的某个信息 |
public final Date getTime() | 获取日期对象 |
public long getTimeInMillis() | 获取时间毫秒值 |
public void set(int field,int value) | 修改日历的某个信息 |
public void add(int field,int amount) | 为某个信息增加/减少指定的值 |
注意:calendar是可变对象,一旦修改后其对象本身表示的时间将产生变化
//demo
public class demo {public static void main(String[] args) {Calendar rightNow = Calendar.getInstance();System.out.println(rightNow); //month从0开始记录//获取日历中的某个信息int year = rightNow.get(Calendar.YEAR);System.out.println(year);int days = rightNow.get(Calendar.DAY_OF_YEAR);System.out.println(days);//拿到日历中记录的日期对象Date now = rightNow.getTime();System.out.println(now); //当前月份//拿到日历中记录的时间毫秒值long time = rightNow.getTimeInMillis();System.out.println(time);//修改日历中的某个信息rightNow.set(Calendar.MONTH,9); //把现在日历中的月份值修改成10月份(month从0开始记录)System.out.println(rightNow); //MONTH=9Date m_now = rightNow.getTime();System.out.println(m_now); //Oct//为某个信息增加或者减少指定值rightNow.add(Calendar.DAY_OF_YEAR,100);rightNow.add(Calendar.DAY_OF_YEAR,-10);System.out.println(rightNow);System.out.println(rightNow.getTime());}
}
二. JDK8开始新增的日期、时间
//demo
public class demo {//JDK8之前传统的时间API 不推荐使用//JDK8开始之后新增的时间API 推荐使用public static void main(String[] args) {Date d = new Date();//1.设计不合理,使用不方便,很多都被淘汰了System.out.println(d.getYear());Calendar c = Calendar.getInstance();int year = c.get(Calendar.YEAR); //要用Calendar.YEAR,使用不方便System.out.println(year);//2.传统的时间API都是可变对象,修改后会丢失最开始的时间信息//3.线程不安全//多用户同时使用同一个对象,会出现问题//4.不能精确到纳秒,只能精确到毫秒//1s(秒)=1000ms(毫秒)//1毫秒 = 1000微秒//1微秒 = 1000纳秒System.out.println(c.getTimeInMillis()); //毫秒级}}
JDK8开始新增的日期、时间
2.1 LocalDate、LocalTime、LocalDateTime
LocalDate:代表本地日期(年、月、日、星期)
LocalTime:代表本地时间(时、分、秒、纳秒)
LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
它们获取对象的方案
方法名 | 实例 |
public static Xxxx now() 获取系统当前时间对应的该对象 | LocalDate ld = LocalDate.now(); LocalTime lt = LocalTime.now(); LocalDateTime ldt = LocalDateTime.now(); |
public static Xxxx of(…) 获取指定时间对象 | LocalDate ld = LocalDate.of(9999,12,31); LocalTime lt = LocalTime.now(); LocalDateTime ldt = LocalDateTime.now(); |
LocalDate的常用API(都是处理年、月、日、星期相关的)
LocalTime的常用API(都是处理时、分、秒、纳秒相关的)
LocalTime的常用API(可以处理年、月、日、星期、时、分、秒、纳秒相关的)
2.2 ZoneId(时区)、ZonedDateTime(带时区的时间)
世界标准时间(UTC)
中国标准时间:世界标准时间(UTC)+8小时
ZoneId:代表时区Id(如Asia/Shanghai)
//demo
public class demo {public static void main(String[] args) {//ZoneId//public static ZoneId systemDefault():获取系统默认的时区ZoneId zoneId = ZoneId.systemDefault();System.out.println(zoneId); //Asia/Shanghai //因为直接zoneId时调用toString方法,toString返回的就是getId()System.out.println(zoneId.getId()); //Asia/Shanghai//public static Set<String> getAvailableZoneIds():获取Java支持的全部时区idSystem.out.println(ZoneId.getAvailableZoneIds());;//public static ZoneId of(String zoneId) :把某个时区id封装成ZoneId对象ZoneId zoneId1 = ZoneId.of("Asia/Aden");//ZonedDateTime:带时区的时间//public static ZonedDateTime now(ZoneId zone):获取某个时区的ZonedDateTime对象ZonedDateTime now = ZonedDateTime.now(zoneId1);System.out.println(now);//获取世界标准时间ZonedDateTime now_UTC = ZonedDateTime.now(Clock.systemUTC());System.out.println(now_UTC);//获得系统默认时间ZonedDateTime now2 = ZonedDateTime.now();System.out.println(now2);//Calendar c = Calendar.getInstance(TimeZone.getTimeZone(zoneId1));}
}
2.3 Instant
Instant 时间线上得某个时刻/时间戳
通过获取Instant的对象可以拿到此刻的时间,该时间由两部分组成:从1970-01-01 00:00:00开始走到此刻的总秒数+不够1秒的纳秒数
Instant对象的作用:做代码的性能分析,或记录用户操作的某个时间点
传统的Date类,只能精确到毫秒,并且是可变对象;新增的Instant类,可以精确到纳秒,并且是不可变对象,推荐用Instant代替Date
Instant类相较于LocalDateTime类,可以直接获得从1970-01-01 00:00:00开始走到此刻的总秒数+不够1秒的纳秒数
2.4 DateTimeFormatter
格式化器,用于时间的格式化、解析,线程安全
SimpleDateFormat 线程不安全;DateTimeFormatter 线程安全
//demo
public class demo {public static void main(String[] args) {//创建一个日期时间格式化器对象出来DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");//对时间进行格式化LocalDateTime now = LocalDateTime.now();System.out.println(now);String s = dtf.format(now); //正向格式化System.out.println(s);//格式化时间,其实还有一种方法String s1 = now.format(dtf); //反向格式化System.out.println(s1);//解析时间,一般使用LocalDateTime提供的解析方法来解析String dataStr = "1999-12-31 23:59:59";LocalDateTime ldt = LocalDateTime.parse(dataStr,dtf);System.out.println(ldt);}
}
2.5 Period(一段时期)
可以用于计算两个LocalDate对象相差的年数、月数、天数
//demo
public class demo {public static void main(String[] args) {LocalDate ld1 = LocalDate.of(2000,1,1);LocalDate ld2 = LocalDate.of(2099,5,20);//创建Period对象,封装两个日期对象Period period = Period.between(ld1,ld2);//通过period对象获取两个日期对象相差的信息System.out.println(period.getYears());System.out.println(period.getMonths());System.out.println(period.getDays());}
}
2.6 Duration(持续时间)
可以用于计算两个时间对象相差的天数、小时数、分数、秒数、纳秒数;支持LocalTime、LocalDateTime、Instant等对象
//demo
public class demo {public static void main(String[] args) {LocalDateTime ldt1 = LocalDateTime.of(2000,1,1,0,0,0);LocalDateTime ldt2 = LocalDateTime.of(2099,5,20,13,14,15);//创建Duration对象,封装两个日期对象Duration duration = Duration.between(ldt1,ldt2);//通过period对象获取两个日期对象相差的信息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()); //间隔多少纳秒}
}
三. Arrays
Arrays用来操作数组的一个工具类
注意:如果数组中存储的是对象,不能直接用Arrays.sort()排序
//demo
public class demo {public static void main(String[] args) {int[] arr = {1,2,3,4,5,6};//public static String toString(int[] a) 返回数组的内容System.out.println(Arrays.toString(arr)); //[1, 2, 3, 4, 5, 6]//拷贝数组(指定范围 包前不包后)//public static 类型[] copyOfRange(类型[] arr, int 起始索引, int 结束索引)int[] arr1 = Arrays.copyOfRange(arr,0,5); //[1, 2, 3, 4, 5]System.out.println(Arrays.toString(arr1));//拷贝数组(可以指定新数组的长度):可做数组扩容//public static 类型[] copyOf(类型[] arr, int newLength)int[] arr2 = Arrays.copyOf(arr,10);System.out.println(Arrays.toString(arr2)); //[1, 2, 3, 4, 5, 6, 0, 0, 0, 0]int[] arr3 = Arrays.copyOf(arr,3); //容量没有原数组大的话,就取原数组的前newLength个System.out.println(Arrays.toString(arr3)); //[1, 2, 3]double[] dArr = {13.14,16.8,99.9,20};//需求:把dArr中的数据打八折//把数组中的原数据改为新数据又存进去//public static void setAll(double[] array, IntToDoubleFunction generator)Arrays.setAll(dArr, new IntToDoubleFunction(){@Overridepublic double applyAsDouble(int value) {//value : 数组索引值 取 0 1 2……//return dArr[value] * 0.8; //直接进行浮点数的计算可能出现结果失真的问题//用BigDecimal解决浮点型数据运算失真问题BigDecimal bd1 = BigDecimal.valueOf(dArr[value]); //把double型转换成BigDecimal型BigDecimal bd2 = BigDecimal.valueOf(0.8);BigDecimal rs = bd1.multiply(bd2); //乘法结果return rs.doubleValue();}});System.out.println(Arrays.toString(dArr)); //[10.512, 13.44, 79.92, 16.0]//对数组进行排序(默认是升序排序)//public static void sort(类型[] arr)Arrays.sort(dArr);System.out.println(Arrays.toString(dArr)); //[10.512, 13.44, 16.0, 79.92]//如果数组中存储的是对象,如何排序?Student[] students = new Student[4];students[0] = new Student("张三",20,178);students[1] = new Student("小美",24,166);students[2] = new Student("李四",22,183);students[3] = new Student("李华",22,155);// Arrays.sort(students);
// System.out.println(Arrays.toString(students)); //报错 ClassCastException异常 不能直接用sort比较对象数组System.out.println(Arrays.toString(students));//方式1:让Student类实现Comparable(比较规则)接口,然后重写compareTo方法来指定比较规则
// Arrays.sort(students);
// System.out.println(Arrays.toString(students));//方式2:使用下面这个sort方法,创建Comparator比较器接口的匿名内部类对象,然后自己制定比较规则Arrays.sort(students, new Comparator<Student>() { //匿名内部类@Overridepublic int compare(Student o1, Student o2) {//制定比较规则:左边对象o1 右边对象o2//按照身高升序排列
// if(o1.getHeight() > o2.getHeight()){
// return 1;
// }else if(o1.getHeight() < o2.getHeight()){
// return -1;
// }else{
// return 0;
// }//上述代码可以简化 按照身高升序排列return Double.compare(o1.getHeight(),o2.getHeight());//不要直接return o1.getHeight()-o2.getHeight()//因为返回值要求是int,这里身高是double//用强制转换的话,可能会导致结果出错(如o1身高178.1,o2身高178,178.1-178=0.1,强转后变成0,结果变成两人一样高)//按照身高降序排列
// return Double.compare(o2.getHeight(),o1.getHeight());}});System.out.println(Arrays.toString(students));}
}//Student
public class Student implements Comparable<Student>{private String name; //姓名private int age; //年龄private double height; //身高//指定比较规则(假设这里的比较规则是按照年龄升序排序)//this是比较者 o是被比较者@Overridepublic int compareTo(Student o) {//约定:如果认为左边对象大于右边对象,返回任意正整数//约定:如果认为左边对象小于右边对象,返回任意负整数//约定:如果认为左边对象等于右边对象,返回0
// if(this.age > o.age){
// return 1;
// }else if(this.age < o.age){
// return -1;
// }else{
// return 0;
// }//上述代码可以简化
// return this.age - o.age; //升序return o.age - this.age; //降序}public Student() {}public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}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;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", height=" + height +'}';}
}
四. JDK8新特性:Lambda表达式
Lambda表达式是JDK 8开始新增的一种语法形式:作用是用于简化匿名内部类的代码写法
注意:
· Lambda表达式只能简化函数式接口的匿名内部类!!!(什么是函数式接口?有且仅有一个抽象方法的接口就是函数式接口)
· 大部分函数式接口上面都可能会有一个@FunctionalInterface的注解,有该注解的接口必定是函数式接口
格式:
(被重写方法的形参列表)-> {
被重写方法的方法体代码
}
//demo
public class demo {public static void main(String[] args) {Animal a = new Animal(){@Overridepublic void run() {System.out.println("小狗跑跑跑");}};a.run();//注意:Lambda表达式并不能简化全部匿名内部类的写法,只能简化函数式接口的匿名内部类//上述代码不是接口,因此不能被简化(下面代码是错误示范)
// Animal a = () -> {
// System.out.println("小狗跑跑跑");
// }// Swimming s = new Swimming(){
// @Override
// public void swim() {
// System.out.println("学生在游泳");
// }
// };
// s.swim();Swimming s = () -> {System.out.println("学生在游泳");};s.swim();}
}interface Swimming{void swim();
}abstract class Animal{public abstract void run();
}
Lambda表达式的省略规则(进一步简化Lambda表达式的写法)
· 参数类型可以省略不写
· 如果只有一个参数,参数类型可以省略,同时()也可以省略
· 如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写
五. JDK8新特性:方法引用
进一步简化Lambda表达式的
方法引用的标志性符号“::”
5.1 静态方法的引用
类名::静态方法
使用场景
如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用
5.2 实例方法的引用
对象名::实例方法
使用场景
如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用
public class demo{public static void main(String[] args){Student[] students = new Student[4];students[0] = new Student("张三",20,178);students[1] = new Student("小美",24,166);students[2] = new Student("李四",22,183);students[3] = new Student("李华",22,155);//原始写法
// Arrays.sort(students, new Comparator<Student>() {
// @Override
// public int compare(Student o1, Student o2) {
// return o1.getAge() - o2.getAge(); //按照年龄升序排列
// }
// });//使用Lambda进行简化后的形式//Arrays.sort(students, (o1,o2) -> o1.getAge() - o2.getAge());//Arrays.sort(students, (o1,o2) -> CompareByData.compareByAge(o1,o2));//方法引用(静态方法的引用)Arrays.sort(students, CompareByData::compareByAge);System.out.println(Arrays.toString(students));System.out.println("=================================");//原始写法
// Arrays.sort(students, new Comparator<Student>() {
// @Override
// public int compare(Student o1, Student o2) {
// return o2.getAge() - o1.getAge(); //按照年龄降序排列
// }
// });//使用Lambda进行简化后的形式Arrays.sort(students, (o1, o2) -> o2.getAge() - o1.getAge()); //降序CompareByData compareByData = new CompareByData();//Arrays.sort(students, (o1,o2) -> compareByData.compareByAgeDesc(o1,o2));//实例方法的引用Arrays.sort(students, compareByData::compareByAgeDesc);System.out.println(Arrays.toString(students));}
}//CompareByData
public class CompareByData {//静态方法public static int compareByAge(Student o1,Student o2){return o1.getAge() - o2.getAge(); //升序排序规则}//实例方法public int compareByAgeDesc(Student o1,Student o2){return o2.getAge() - o1.getAge(); //降序排序规则}
}//Student类 参考Arrays里面的代码
5.3 特定类型方法的引用
类型::方法
使用场景
如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用
//demo
public class demo{public static void main(String[] args){String[] names = {"boby","angela","Andy","dlei","caocao","Babo","jack","Cici"};//进行排序(默认是按照字符串的首字符编号进行)Arrays.sort(names); [Andy, Babo, Cici, angela, boby, caocao, dlei, jack]//要求忽略首字符大小写进行排序
// Arrays.sort(names, new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// //指定比较规则: 如o1=Andy o2=angela
// return o1.compareToIgnoreCase(o2);
// }
// });//Lambda简化
// Arrays.sort(names, (o1, o2) -> o1.compareToIgnoreCase(o2));//特定类型方法的引用Arrays.sort(names, String::compareToIgnoreCase);System.out.println(Arrays.toString(names));}
}
5.4 构造器引用
类型::new
使用场景
如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用
//demo
public class demo{public static void main(String[] args){//创建接口的匿名内部类
// createCar cc = new createCar() {
// @Override
// public Car create(String name, double price) {
// return new Car(name,price);
// }
// };//Lambda简化
// createCar cc = (name, price)-> new Car(name,price);//构造器引用createCar cc = Car::new;Car c = cc.create("奔驰",49.9);System.out.println(c);}
}interface createCar{Car create(String name,double price);
}//Car
public class Car {private String name;private double price;public Car() {}public Car(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", price=" + price +'}';}
}