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=9
Date 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(都是处理时、分、秒、纳秒相关的)
LocalDateTime的常用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支持的全部时区id
System.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(){
@Override
public 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>() { //匿名内部类
@Override
public 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是被比较者
@Override
public 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;
}
@Override
public 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(){
@Override
public 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;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}