Java8新特性
一、新特性简介
- 速度更快(HashMap中增加了红黑树,当链表的数量大于8,总容量大于64,对链表进行转换成红黑树)
- 代码更少(新增了Lambda表达式)
- 强大的Stream API流
- 便于并行
- 最大化减少空指针异常(Optional 容器类)
二、Lambda表达式
Lambda表达式是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码
1、Lambda语法
# 操作符:Lambda操作符 “->”
左侧:Lambda表达式的参数列表
右侧:Lambda表达式所需执行的功能,即Lambda体
# 语法格式:
语法一:无参数,无返回值
() -> sout();
语法二:有一个参数,无返回值(若只有一个参数,参数括号可以不写)
(x) -> sout(); 或者 x -> sout();
语法三:有两个及以上的参数,又返回值,Lambda体中有多条语句
(x,y) -> {
语句1;
return ;
}
语法四:Lambda体中已有一条语句,{} 和 return 关键字都可以省略不写
(x,y) -> Integer.compare(x,y);
语法五:Lambda表达式参数列表的数据类型可以省略不写,因为JVM可以通过上下文推断出,称之为类型推断
# 总结:
1. Lambda表达式需要函数式接口的支持;
2. 函数式接口:若接口中只有一个抽象方法的接口。可以使用@FunctionInterface注解修饰,可以检查是否是函数式接口
2、Java8四大核心内置函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法 void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T对象,包含方法: T get() |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法: R apply(T t) |
Predicate 断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值.包含方法: boolean test(T t) |
3、方法引用和构造器引用
3.1方法引用
# 概念
方法引用:若Lambda体中的方法已经实现了,我们可以使用方法引用。可以理解为方法引用为Lambda表达式的另外一种表现形式。
# 语法
语法一:对象::方法名
要求:Lambda体中调用方法的参数列表和返回值要和函数式接口方法中的参数列表和返回值保持一致。
例如:
User user = new User();
Appliyer<String> str = user::getName;
语法二:类::静态方法名
要求:Lambda体中调用方法的参数列表和返回值要和函数式接口方法中的参数列表和返回值保持一致。
例如:
Function<Integer,Inreger> bp = Integer::compare;
语法三:对象::实例方法名
要求:Lambda参数列表的第一个参数是方法的调用者,第二个参数是实例方法的参数
例如:
BiPridicate<Integer,Inreger> bp = String::equals;
3.2构造器引用
# 概念
构造器引用:通过构造器引用可以返回一个实例对象。
# 语法
要求:需要调用的构造器参数列表要和函数式接口方法中的参数列表保持一致(函数式接口方法中有几个参数就是调用的有几个参数的构造方法)
类::new
3.3数组引用
# 概念
数组引用:数组也是对象,也是通过new的方式返回一个数组对象(List等同理)。
# 语法
数组类型[]::new
例如:
Function<Integer,String[]> fun = String[]::new;
String[] strs = fun.apply(20);
strs的长度为20!!
三、强大的Stream API
1、概念
Stream(流):是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合讲的是数据,流讲的是计算
当我们使用流的时候,通常包括三个步骤:获取数据源(source)→ 数据转换 → 执行操作获取想要的结果,每次转换原有stream对象不改变,返回一个新的stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示。
注意:
- Stream自己不会存储元素
- Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream操作会延迟执行的。这意味着他们会等到需要结果的时候才会执行
2、流的操作步骤
2.1 创建Stream的四种方式
// 1.可以通过Collection系列集合提供的Stream()[串行流] 和 parallelStream()[并行流]、
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// 2.通过Arrays中的静态方法stream()获取数组流
User[] users = new User[10];
Stream<User> stream = Arrays.stream(users);
// 3.通过Stream类中的静态方法of()
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
// 4.创建无限流(两种方式)
// 迭代
Stream<Integer> stream = Stream.iterate(0, (x) -> x + 2);
// 生成
Stream<double> stream = Stream.generate(() -> Math.random());
2.2中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否者中间操作不会执行任何的处理!而是在终止操作时一次性全部处理,称之为==“惰性求值”==
# 筛选与切片
filter():接收Lambda表达式,从流中排出某些元素
limit():截断流,使其元素不超过指定数量
skip():跳过元素,返回一个扔掉前n个元素的流,若流中不足n个,则返回一个空流。
distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素 【自定义的类要重写hashCode()和equals()方法】
# 映射
map():接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会应用到每个元素上,并将其映射成一个新元素。
flatMap():接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有的流连接成一个流。
# 排序
sorted():自然排序 (Comparable)
sorted(Comparator com):定制排序
2.3终止操作
# 查找与匹配
allMatch():检查是否匹配所有元素
anyMatch():检查是否至少匹配一个元素
noneMatch():检查是否没有匹配所有元素
findFirst():返回第一个元素 返回值类型是Optional, 通过get()方法获取元素
findAny():返回流中任何一个元素 返回值类型是Optional, 通过get()方法获取元素
count():返回流中元素的总个数
max():返回流中的最大值 返回值类型是Optional, 通过get()方法获取元素
min():返回流中的最小值 返回值类型是Optional, 通过get()方法获取元素
2.4归约
# 归约
reduce(T identity,BinaryOprater): 可以将流中的元素返回结合起来,得到一个值
reduce(BinaryOprater): 返回值类型是Optional, 通过get()方法获取元素
# 注意
map() 和 reduce() 经常组合使用,和Hadoop中的MapReduce比较相似
2.5收集
# 收集
collect():将流转换成其他形式。接收一个collector的一个实现【Collectors中的静态方法】,用于给Stream中元素做汇总的方法。
对流数据可以获取最大值、最小值、平均值、总数、分组、多级分组、分区(true,false)
3、并行流与顺序流
Fork/Join框架:就是在必要的情况下,将一个大任务进行拆分(fork)成若干个小任务(拆到不可再拆分时),再将一个个小任务运算的结果进行汇总(join);
采用“工作窃取”模式:当执行新的任务时,他可以将其拆分成更小的任务执行,并将小任务加到县城队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
注意:临界值的大小和数据量的大小都会影响执行效率。
在Java8中,使用==parallel()和sequential()==两个方法进行并行流和顺序流的切换
四、Optional类(感觉实用性不是很高)
1、概念
Optional类是一个容器类,代表一个值存在或者不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
2、常用方法
方法 | 说明 |
---|---|
Optional.of(T t) | 创建一个Optional实例(传入的值不能是null,否则会报空指针异常) |
Optional.empty() | 创建一个空的Optional实例 |
Optional.ofNullable(T t) | 若t不为null,创建Optional实例,否则创建空实例(传入的值可以是null) |
isParesent() | 判断是否包含值(有值就获取,否则什么也不做) |
orElse(T t) | 如果调用对象包含值,返回该值,否则返回t(有值就获取,没有值就返回T) |
orElseGet(Supplier s) | 如果调用对象包含值,返回该值;否则返回S获取的值 (有值就获取,没有值就返回S【函数式接口,这里可以进行逻辑编写】) |
map(Function f) | 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty() |
faltMap(Function f) | 与map类似,要求返回值必须是Optional |
五、接口中的默认方法和静态方法
1、默认方法
public interface myInterface{
// 修饰符为default
default String getName(){
return "哈哈哈哈";
}
}
注意
1. 若一个类继承一个父类和实现了一个接口,并且父类和接口中存在相同的方法,此时子类调用该方法时是调用父类中的方法。类优先原则!!!
2. 若一个类实现了多个接口,并且这多个接口中存在相同的方法,则该类在声明时则需要指定实现哪个接口的方法,否则编译器会报错!!!
2、静态方法
public static void getMethod(){
System.out.println("接口中的静态方法");
}
六、时间日期API
1、LocalDate、LocalTime和LocalDateTime
LocalDate、LocalTime和LocalDateTime类实例是不可变的对象。
// 获取当前时间
LocalDateTime ldt = LocalDateTime.now();
// 获取指定时间
LocalDateTime ldt = LocalDateTime.of(2020,11,11,11,11,11);
// 时间运算 加两年
LocalDateTime ldt = ldt.plusYears(2); // 生成一个新的实例
// 时间运算 减两个月
LocalDateTime ldt = ldt.minusMonths(2); // 生成一个新的实例
// 获取年、月、日、时、分、秒使用相应的get方法
2、Instance时间戳
时间戳:以1970年1月1日 00:00:00为起点到某个时间之间的毫秒值。
// 默认获取UTC时区
Instance ins = Instance.now();
ins.toEpochMilli(); // 获取毫秒值
// 时间偏移量
ins.atOffset(Zone)
3、Duration和Period
Duration:计算两个“时间”之间的间隔
Period:计算两个“日期”之间的间隔
// 计算两个时间之间的间隔
Instance start = Instance.now();
// TO-DO 一些逻辑
Instance end = Instance.now();
// 获取毫秒值
Duration.between(start,end).toMillis();
// 计算两个日期之间的间隔
LocalDate start = LocalDate.now();
LocalDate end = LocalDate.of(2022,11,11,11,11,11);
Period.between(start,end).getYear(); // 相差几年
Period.between(start,end).getMonth(); // 相差几月
4、TemporalAdjuster时间校正器
// 比如:可以获取下一个周日、下一个月等等
LocalDateTime ldt = LocalDateTime.now();
ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); // 获取下周一的日期
5、DataTimeFormatter 格式化时间/日期
DataTimeFormatter dtf = DataTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDateTime ldt = LocalDateTime.now();
// 格式转换 将日期转换成指定格式的字符串(以下两种方式都可以)
String date = dtf.format(ldt);
String date = ldt.format(dtf);
// 格式转换 将字符串转换成指定格式的日期
LocalDateTime ldt = ldt.parse(data,dtf); // 待确定
ormatter 格式化时间/日期
DataTimeFormatter dtf = DataTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDateTime ldt = LocalDateTime.now();
// 格式转换 将日期转换成指定格式的字符串(以下两种方式都可以)
String date = dtf.format(ldt);
String date = ldt.format(dtf);
// 格式转换 将字符串转换成指定格式的日期
LocalDateTime ldt = ldt.parse(data,dtf); // 待确定