JDK8新特性
1 日期、时间
1.1 JDK8之前传统的日期、时间
设计不合理,使用不方便;都是可变时间,修改后会丢失最初的时间信息;线程不安全;只能精确到毫秒
- Date
- SimpleDateFormat
- Calendar
1.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) | 设置日期对象的时间为当前时间毫秒值对应的时间 |
1.1.2 SimpleDateFormat
构造器 | 说明 |
---|---|
public SimpleDateFormat(String pattern) | 创建简单日期格式化对象,并封装时间的格式 |
常见方法 | 说明 |
---|---|
public final String format(Date date) | 将日期格式化成日期/时间字符串 |
public final String format(Object time) | 将时间毫秒值格式化成日期/时间字符串 |
public Date parse(String source) | 把字符串时间解析成日期对象(注意:待转换的字符串时间格式要和SimpleDateFormat对象指定的时间格式保持一致) |
常用日期时间字符串格式 | 格式化后的字符串 | 备注 |
---|---|---|
“yyyy.MM.dd HH:mm:ss” | “2024.06.01 17:02:11” | "HH"表示24小时制 |
“yyyy.MM.dd hh:mm:ss” | “2024.06.01 5:02:11” | "hh"表示12小时制 |
“yyyy年MM月dd日 HH时mm分ss秒” | “2024年06月01日 17时02分11秒” | "HH"表示24小时制 |
其他时间格式符号:EEE 表示星期几;a 表示上午/下午;S(大写) 表示毫秒;
1.1.3 Calendar
Calendar代表的是系统此刻时间对应的日历
通过Calendar可以单独获取、修改时间中的年,月,日,时,分,秒等
获取Calendar对象的方法:
Calendar rightNow=Calendar.getInstance();
注意: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) | 为某个信息增加/减少指定的值 |
1.2 JDK8新增的日期、时间
设计更合理,功能丰富,使用方便;都是不可变对象,修改后会返回新的时间对象,不会丢失最开始的时间信息;线程安全;能精确到毫秒、纳秒。
- LocalDate(年,月,日,星期)、LocalTime(时,分,秒,纳秒)、LocalDateTime(年,月,日,星期, 时,分,秒,纳秒)
- ZoneId(时区)、ZoneDateTime(带时区的时间)
- Instant(时间戳/时间线)
- DateTimeFormatter(勇于时间的格式化和解析)
- Duration(时间间隔,时、分、秒,纳秒)、Period(时间间隔年,月,日)
代替Calendar的类
- LocalDate
- LocalTime
- LocalDateTime
- ZoneId
- ZoneDateTime
代替Date的类
- Instant
代替SimpleDateFormat的类
- DateTimeFormatter
其他补充
- Period
- Duration
1.2.1 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(2099,11,11) |
LocalTime lt = LocalTime.of(9,8,59) | |
LocalDateTime ldt = LocalDateTime.of(2099,11,11,9,8,59) |
LocalDate常用方法 | 作用 |
---|---|
int getYear() | 获取年份 |
int getMonthValue() | 获取月份(1-12) |
int getDayOfMonth() | 获取日(几号) |
int getDayOfYear() | 一年中的第几天 |
int getDayOfWeek().getValue() | 星期几 |
LocalDate withYear(int year) | 修改年份 |
LocalDate withMonth(int month) | 修改月份 |
LocalDate withDayOfMonth(int day) | 修改日(几号) |
LocalDate withDayOfYear(int day) | 修改天数(一年中的第几天) |
LocalDate plusYears(int year) | 把年份加多少 |
LocalDate plusMonths(int month) | 把月份加多少 |
LocalDate plusDays(int day) | 把日(几号)加多少 |
LocalDate plusWeeks(int week) | 把星期几加多少 |
LocalDate minusYears(int year) | 把年份减多少 |
LocalDate minusMonths(int month) | 把月份减多少 |
LocalDate minusDays(int day) | 把日(几号)减多少 |
LocalDate minusWeeks(int week) | 把星期几减多少 |
public static LocalDate of(int year,int month,int dayOfMonth) | 获取指定日期的LocalDate对象 |
boolean equals(LocalDate localDate) | 判断两个LocalDate对象是否相等 |
boolean isBefore(LocalDate localDate) | 某个日期是否在localDate之前 |
boolean isAfter(LocalDate localDate) | 某个日期是否在localDate之后 |
LocalTime和LocalDate常用方法形式相同
LocalTime常用方法 | 作用 |
---|---|
int getHour() | 时 |
int getMinute() | 分 |
int getSecond() | 秒 |
int getNano() | 纳秒 |
… | … |
LocalDateTime特有方法 | 作用 |
---|---|
public LocalDate toLocalDate() | 把LocalDateTime对象转换为LocalDate对象 |
public LocalTime toLocalTime() | 把LocalDateTime对象转换为LocalTime对象 |
public static LocalDateTime of(LocalDate date,LocalTime time) | 把LocalDate和LocalTime对象合并为一个LocalDateTime对象 |
1.2.2 ZoneId(时区)、ZoneDateTime(带时区的时间)
ZoneId常用方法 | 作用 |
---|---|
public static ZoneId systemDefault() | 获取系统默认的时区 |
public static Set getAvailableZoneIds() | 获取Java支持的全部时区Id |
public static ZoneId of(String zoneId) | 把某个时区Id封装成ZoneId对象 |
ZoneDateTime常用方法 | 作用 |
---|---|
public static ZoneDateTime now(ZoneId zone) | 获取某个时区的ZoneDateTime对象 |
public static ZoneDateTime now() | 获取系统默认时区的ZoneDateTime对象 |
ZoneDateTime其他功能用法和LocalDateTime几乎全部一样
1.2.3 Instant
Instant对象由两部分组成:从1970-01-01 00:00:00 开始走到此刻的总秒数+不够1秒的纳秒数
作用:可以用来记录代码的执行时间,或用于记录用户操作某个事件的时间点
Instant常用方法 | 作用 |
---|---|
public static Instant now() | 获取当前的Instant对象 |
public long getEpochSecond() | 获取从1970-01-01 00:00:00开始记录的秒数 |
public int getNano() | 获取不够一秒的纳秒数 |
plusMillis(),plusSeconds(),plusNanos() | 增加时间系列的方法 |
minusMillis(),minusSeconds(),minusNanos() | 减少时间系列的方法 |
equals(),isBefore(),isAfter() | 判断时间系列的方法 |
1.2.4 DateTimeFormatter
线程安全
DateTimeFormatter常用方法 | 作用 |
---|---|
public static DateTimeFormatter ofPattern(时间格式) | 获取格式化器对象 |
public String format(时间对象) | 格式化时间 |
LocalDateTime提供的格式化、解析时间的方法
方法名 | 作用 |
---|---|
public String format(DateTimeFormatter formatter) | 格式化时间 |
public static LocalDateTime parse(CharSequence text,DateTimeFormatter formatter) | 解析时间 |
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now=LocalDateTime.now();
//使用DateTimeFormatter格式化时间
String rs=formatter.format(now);//正向格式化
//使用LocalDateTime提供的格式化方法来格式化
String rs2=now.format(formatter);//反向格式化
//解析时间:解析时间一般使用LocalDateTime提供的解析方法来解析
String dateStr="2024-6-1 22:58:23";
LocalDateTime ldt=LocalDateTime.parse(dateStr,formatter);
1.2.5 Duration、Period
Period(一段时间):
- 可以用来计算两个LocalDate对象相差的年数,月数,天数
Period常用方法 | 作用 |
---|---|
public static Period between(LocalDate start,LocalDate end) | 传入两个LocalDate日期对象,得到Period对象 |
public int getYears() | 计算隔几年,并返回 |
public int getMonths() | 计算隔几月,并返回 |
public int getDays() | 计算隔几天,并返回(例如:2024-3-7到2024-6-15返回的天数就是8) |
Duration(持续时间):
- 可以用于计算两个时间对象相差的天数,小时数,分数,秒数,纳秒数
- 支持LocalTime,LocalDateTime,Instant等时间
Duration常用方法 | 作用 |
---|---|
public static Duration between(开始时间对象1,截止时间对象2) | 传入两个时间对象,得到Duration对象 |
public long toDays() | 计算隔多少天,并返回 |
public long toHours() | 计算隔多少小时,并返回 |
public long toMinutes() | 计算隔多少分钟,并返回 |
public long toSeconds() | 计算隔多少秒,并返回 |
public long toMillis() | 计算隔多少毫秒,并返回 |
public long toNanos() | 计算隔多少纳秒,并返回 |
2 Lambda表达式
JDK8以上版本可用
作用:简化匿名内部类的代码写法。
使用前提:只能简化函数式接口的匿名内部类
函数式接口: 有且仅有一个抽象方法的接口,使用@FunctionalInterface注解修饰的接口必定是函数式接口
假设有以下一个函数式接口
public Interface A{
int fun(int a);
}
如果使用常规匿名内部类的形式书写,格式如下:
A a = new A{
public int fun(int a){
return a+11;
}
};
如果使用Lambda表达式形式书写,格式如下:
A a = (int a) -> {
return a+11;
};
2.1 Lambda表达式的省略写法(进一步简化的写法)
- 参数类型可以省略不写
- 如果只有一个参数,参数类型可以省略不写,同时()也可以省略
- 如果Lambda表达式中的方法体代码只有一行,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写。
所以上边的Lambda表达式可以进一步简化为:
A a = a -> a+11;
3 方法引用
作用:进一步简化Lambda表达式
方法引用的标志性符号为 “::”
3.1 静态方法引用
使用方法:类名::静态方法
使用场景:如果某个Lambda表达式中只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用
例如:
class CompareByData{
public static int compareByAge(Student o1,Student o2){
return o1.getAge() - o2.getAge();
}
}
//常规Lambda写法
Arrays.sort(students,(o1,o2) -> o1.getAge()-o2.getAge());
//使用CompareByData类的写法
Arrays.sort(students,(o1,o2) -> CompareByData.compareByAge(o1,o2));
//使用静态方法引用后的写法
Arrays.sort(students,CompareByData::compareByAge)
3.2 实例方法的引用
使用方法:对象名::实例方法
使用场景:如果某个Lambda表达式中只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用
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();
}
}
//常规Lambda写法
Arrays.sort(students,(o1,o2) -> o2.getAge()-o1.getAge());
//使用CompareByData类实例对象的写法
CompareByData compareByData = new CompareByData();
Arrays.sort(students,(o1,o2) -> compareByData.compareByAgeDesc(o1,o2));
//使用实例方法引用后的写法
Arrays.sort(students,compareByData::compareByAgeDesc)
3.3 特定类型的方法引用
使用方法:类型::方法
使用场景:如果某个Lambda表达式中只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该石实例方法入参的,则此时就可以使用特定类型的方法引用
String[] names = {"Abddd","abdcc","Bdacc","Bbcca","dd"};
//要求:忽略首字母大小写进行排序
//常规写法
Arrays.sort(names,new Comparator<String>(){
@Override
public int compare(String o1,String o2){
return o1.compareToIgnoreCase(o2);
}
});
//使用Lambda表达式的写法
Arrays.sort(names,(o1,o2) -> o1.compareToIgnoreCase(o2));
//使用特定类型的方法引用后的写法
Arrays.sort(names,String::compareToIgnoreCase);
3.4 构造器引用
使用方法:类名::new
使用场景:如果某个Lambda表达式中只是在创建对象,并且前后参数情况一致,就可以使用构造器引用。
class Car{
private String name;
private double price;
public Car(){
}
public Car(String name,double price){
this.name=name;
this.price=price;
}
.....
}
interface CreateCar{
Car create(String name,double price);
}
//不使用Lambda表达式写法
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;
4 Stream流
可以用来操作集合或者数组的数据
优势:Stream流大量的结合了Lambda的语法风格来编程,提供了一种更强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。
Stream流的使用步骤:
- 从数据源(集合/数组)获取Stream流
- 调用流的各种中间方法对数据进行处理,计算
- 使用终结方法获取处理结果,遍历、统计、收集到一个新集合中返回
4.1 获取Stream流
获取集合的Stream流
Collection接口提供的方法
default Stream stream() 获取当前集合对象的Stream流
获取数组的Stream流
Arrays类提供的方法:
public static Stream stream(T[] array) 获取当前数组的Stream流
Stream类提供的方法:
public static Stream of(T… values) 获取当前接收数据的Stream流
//获取集合的流
List<String> names=new ArrayList<>();
...
Set<String> set=new HashSet<>();
...
Map<String,Double> map=new HashMap<>();
...
//获取Stream流
Stream<String>stream1= names.stream();
Stream<String>stream2= set.stream();
//因为stream方法是Collection接口提供的所以Map集合没办法直接用,但是可以把Map转换为Set集合后使用stream方法获得Stream流
//获取键集合的流
Set<String> keys=map.keySet();
Stream<String> ks=keys.stream();
//获取值集合的流
Collection<Double> values=map.Values();
Stream<Double> vs=values.stream();
//把键值对作为一个整体,获取这个整体集合的流
Set<Map.Entry<String,Double>> entries=map.entrySet();
Stream<Map.Entry<String,Double>> kvs=entries.stream();
//获取数组的流
String[] names={"1","2","3","4"};
//使用Arrays类提供的方法获取流
Stream<String> stream3=Arrays.stream(names);
//使用Stream类提供的of方法获取流
Stream<String> stream4=Stream.of(names);
4.2 Stream流的中间方法
中间方法是指调用完成之后会返回新的Stream流,可以继续使用(支持链式编程)
Stream提供的常用中间方法 | 说明 |
---|---|
Stream< T> filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 |
Stream< T> sorted() | 对流中的数据进行生序排序 |
Stream< T> sorted(Comparator<? super T> comparator) | 对流中的数据按照指定规则排序 |
Stream< T> limit(long maxSize) | 获取前几个元素 |
Stream< T> skip(long n) | 跳过前几个元素 |
Stream< T> distinct() | 去除流中重复的元素 |
< R> Stream< R> map(Function<? super T,? extends R>mapper) | 对元素加工并返回对应的新流(又称为映射方法) |
static< T> Stream< T> concat(Stream a,Stream b) | 合并a和b两个流为一个流 |
4.3 Stream流的终结方法
终结方法是指调用完成之后,不会返回新Stream了,没法继续使用流了
Stream提供的常用终结方法 | 说明 |
---|---|
void forEach(Consumer action) | 对此流运算后的元素执行遍历 |
long count() | 统计此流运算后的元素个数 |
Optional< T> max(Comparator<? super T> comparator) | 获取此流运算后的最大值元素,执行完后使用get()方法获取最大值元素 |
Optional< T> min(Comparator<? super T> comparator) | 获取此流运算后的最小值元素,执行完后使用get()方法获取最小值元素 |
收集Stream流:就是把Stream流操作后的结果转回集合或者数组中去返回
Stream流:方便操作集合/数组的手段,集合/数组:才是开发中的目的
注意:流只能收集一次,收集后流就被关闭了
Stream提供的常用终结收集方法 | 说明 |
---|---|
R collect(Collector collector) | 把流处理后的结果收集到一个指定的集合中去(例如:想收集为List集合,则参数为Collectors.toList()) |
Object[] toArray() | 把流处理后的结果收集到一个数组中去 |
Stream<Student> stream1=...;
//把流收集到List集合
List<Student> studentList = stream1.collect(Collectors.toList());
//把流收集到Map集合中
//注意:此方法使用前要先把作为键的数据去重,不然会抛异常
Map<String,Double> map=stream1
.collect(Collectors.toMap(a->a.getName(),a->a.getHeight()));
//把留收集到数组中
Student[] arr=stream1.toArray(len->new Student[len]);