Java8新特性
1.1 接口的默认方法
1.1.1 为什么有默认方法
- 解决接口的修改与现有的实现不兼容问题
- 例如:Java8之前的集合框架添加foreach方法,需要在JDK里面相关的接口添加方法及实现。
1.1.2 什么是默认方法
- 默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
- 通过default关键字向接口添加非抽象方法实现,此功能也称之为虚拟扩展方法。
1.1.3 如何使用默认方法
- 语法
public interface Vehicle {
default void print (){
System.out.println("这是一个默认方法!");
}
}
var foo = 'bar';
- 实现多个接口,多个默认方法解决方案
1、创建自己的默认方法,覆盖重写接口的默认方法
2、使用super来调用指定接口的默认方法
1.2 Lambda表达式
1.2.1 为什么使用Lambda表达式
- 开发快速,代码简洁,易于阅读
- 减少匿名内部类的创建,节省资源
- 易于“并发编程”;函数式编程不需要考虑“死锁”,因为它不修改变量,所以根本不存在“锁”线程的问题。
1.2.2 什么是Lambda表达式
Lambda允许把函数作为一个方法的参数(函数作为参数传递到方法中)
1.2.3 如何使用Lambda表达式
- 语法
(paraments)->expression或者(parameters)->{statements;}
1.2.4 Lambda应用场景
1.2.4.1 使用() -> {} 替代匿名类
Lamnda表达式要比匿名类简洁优雅很多
Thread t1=new Thread(new Runnable(){
@Override
public void run(){
System.out.println("no use lambda");
}
});
Thread t2=new Thread(()->System.out.println("use lambda"));
1.2.4.2 已流水线的方式处理数据
- 借助stream api和Lambda表达式,以往需要定义多个变量,编写复杂的集合操作,现在都基本简化在一行之内完成。
//过滤初偶数列表
List<Integer> evens=intList.stream().filter(i->i%2==0).collect(Collections.toList());
//复合操作
List<Integer> list=intList.stream().filter(i->i%2==0).map(i->i*i).distinct.collection(Collections.toList());
1.2.4.3 更简单的数据并行处理
- 数据并行处理,只需要在原来的基础上加一个parallel()就可以开启了。此方法开启的底层并行框架是fork/join,默认的并行数是Ncpu个
List evens=intList.stream().parallel().filter(i->i%2==0).collect(Collections.toList());
1.2.4.4 内部迭代取代外部迭代
- 外部迭代:描述怎么干,代码嵌套2个以上for循环,难读懂。
for(String f:flist){
System.out.println(f);
}
- 内部迭代:描述干什么;
flist.stream().forEach(n->System.out.println(n));
1.3 Lambda表达式作用域
1.3.1 访问局部变量(final)
- 可访问标记了final的外层局部变量
- 可访问隐形的具有final语义的外层变量
int num=1;
Converter<Integer,String> str=(f)->String.volueOf(f+num);
str.convent(2);
//num=3; 表达式之后修改num不允许的
1.3.2 访问字段和静态变量
class Lambda4{
static int num;
int n;
void test{
Converter<Integer,String> strCon1=(f)->{
num=23;
return String.valueOf(f);
}
Converter<Integer,String> strCon2=(f)->{
n=2;
return String.valueOf(f);
}
}
}
1.4 函数式接口
1.4.1 为什么有函数式接口
- 函数式接口作为Lambda表达式的目标类型
- 单独为Lambda表达式新增函数类型,破坏既有的java类型,对类库造成严重影响
1.4.2 什么是函数式接口
- Java8对对一类特殊类型的接口的称呼
- 仅有一个抽象方法,可以有多个非抽象方法的接口
1.4.3 函数式接口的使用
1.4.3.1 常用的四大核心接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer消费型接口 | T | void | 对类型为T的对象应用操作。void accept(T t); |
Supplier | 无 | T | 返回类型为T的对象。T get(); |
public static void main(String args[]){
List<Integer> list=Arrays.asList(1,2,3);
eval(list,n->true);//输出所有元素
eval(list,n->n%2==0);//输出所有的偶数
}
public static void eval(List<Integer> list,Predicate<Integer> pre){
for(Integer n:list){
if(pre.test(n)){
System.out.println(n);
}
}
}
1.4.3.2 自定义函数接口使用
@FunctionalInterface
public interface Con<F,T>{
T con(F f);
}
//将字符串转为整数类型
Con<String,Integer> c=(f)->Integer.valueOf(f);
Integer conInt=c.con("123");
System.out.println(conInt);
1.5 方法或构造函数的引用
1.5.1 为什么使用方法或构造函数的引用
- 根据已有的方法实现来创造Lambda表达式,代码可读性更好
1.5.2 如何使用方法或构造函数的引用
@FunctionalInterface//Java8的接口
public interface Supplier<F,T>{
T get();
}
class Car{
public static void cre(final Supplier<Car> s){
return s.get();
}
public static void col(final Car car){
System.out.println(car.toString());
}
public void fol(final Car c){
System.out.println(c.toString());
}
public void rep(){
System.out.println(this.toString());
}
}
- 构造器引用
final Car car=Car.cre(Car::new);
final List<Car> cars=Arrays.asList(car);
- 静态方法引用
cars.forEach(Car::col);
- 特定类的任意对象的方法引用
cars.forEach(Car::rep);
- 特定对象的方法引用
final Car pol=Car.cre(Car::new);
cars.forEach(pol:fol);
1.6 Optional类
1.6.1 为什么有Optional类
- 去除代码中对null的检查(避免嵌套null检查)
- 解决null引发的问题(NullPointerExpetion)
1.7 Stream
1.7.1 为什么使用Stream
- 声明性—代码更加简洁、易懂
- 可复合型—更灵活
- 可并行—性能更好
1.7.2 什么是Stream
- 一个来自数据源的元素序列并支持聚合操作
- 数据源:流的来源。可以是集合、数组、I/O Channel、产生器generator等
- 集合操作:流的数据处理功能支持类似数据库操作,例如filter、mao、reduce、find、match等
- 流操作的两个重要特点
流水线:很多流操作本身返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。 - 内部迭代:流提供内部迭代的方式,通过访问者模式实现。流库的内部迭代可以自动选择一种适合硬件的数据表示和并行实现。
1.7.3 Stream的使用
1.7.3.1 Stream的两大操作
- 中间操作:可以链接起来的流操作。
- 终端操作:关闭流的操作。
1.7.3.2 筛选(filter)与切片
- filter(过滤/筛选):通过一个predicate接口来过滤并保留符合条件的元素
List<Integer> nums=Arrays.asList(1,2,3);
nums.stream().filter(i->i%2==0).forEach(System.out::println);
- limit(截断流):返回一个不超过给定长度的流。
//返回热量超过300的头三道菜
List<Dish> diahs=menu.stream().filter(d->d.getCalories()>300).limit(3).collect(toList());
- 跳过元素:返回一个扔掉前n元素的流。
//跳过超过300的头两道菜,返回剩余超过300的菜
List<Dish> diahs=menu.stream().filter(d->d.getCalories()>300).skip(2).collect(toList());
1.7.3.3 映射
- map(映射):将元素根据指定的Fuction接口依次将元素转成另外的对象。
//提取流中菜肴的名称
List<Integer> dishNames=menu.stream().map(Dish::getName).collect(toList());
- flatMap(流的扁平化):把一个流中的每个值都换成另外一个流,然后把所有的流链接成一个流。
//将words单词列表["Hellow","world"],返回每个字母去重后的列表["H","e","l","o","w","r","d"]
List<String> s=words.stream().map(w->w.split("")).flatMap(Arrays::stream).district().collect(toList());
//map(w->w.split(""))返回一个Stream<String[]>
//flatMap(Arrays::stream)将Stream<String[]>转变为Stream<String>
1.7.3.4 查找(find)与匹配(Match)
- allMatch、anyMatch、noneMatch、findMatch、findAny