一.Lambda表达式
1.1 介绍
Lambda表达式是一种没有名字的函数,也可称为闭包,是Java8发布的最重要新特性。本质上是一段匿名内部类,也可以是一段可以传递的代码。
java中箭头函数的用来简化匿名内部类
闭包
闭包就是能够读取其他函数内部变量的函数比如在java中.方法内部的局部变量只能在方法内部使用.所以闭包可以理解为定义在一个函数内部的函数
闭包的本质就是将函数内部和函数外部链接起来的桥梁
1.2 特点
允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用Lambda表达式可以使代码变的更加简洁紧凑。
1.3 和匿名内部类比较
//匿名内部类
Collentinos.sort(list,new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return o2 - o1;
}
});
//lambda
Collentinos.sort(list,(o1,o2) -> o2-o1);
1.4 应用场景
列表迭代
Map映射
Reduce聚合
代替一个不想命名的函数或是类,该函数或类往往并不复杂。
想尽量缩短代码量的各个场景均可以
1.5 代码实现
1.5.1 具体语法
1.(parameters) -> expression
2.(parameters) ->{ statements; }
1.5.2 语法特点
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
如果只有一条语句,并且是返回值语句,就可以不写return 不写 {}
如果写上{} 就必须写return 和 ;
如果有 多条语句,必须写{} return 和 ; 也必须写
二.函数式接口
2.1 介绍
英文称为Functional Interface
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
核心目标是为了给Lambda表达式的使用提供更好的支持,进一步达到函数式编程的目标,可通过运用函数式编程极大地提高编程效率。
其可以被隐式转换为 lambda 表达式。
2.2 特点
函数式接口是仅制定一个抽象方法的接口
可以包含一个或多个静态或默认方法
专用注解即@FunctionalInterface 检查它是否是一个函数式接口,也可不添加该注解
如果有两个或以上 抽象方法,就不能当成函数式接口去使用,也不能添加@FunctionalInterface这个注解
如果只有一个抽象方法,那么@FunctionalInterface注解 加不加 都可以当做函数式接口去使用
回调函数
简单来说就是回调函数,方法的参数是一个方法,在这个方法中对传递的方法进行调用
2.3 应用场景
想通过函数式编程,提高编程效率的各种场景均可。
2.4 JDK自带的常用函数式接口
2.4.1 Supplier<T>接口
Supplier<T>接口 代表结果供应商,所以有返回值,可以获取数据
有一个get方法,用于获取数据
2.4.2 Consumer<T>接口
Consumer<T>接口 消费者接口所以不需要返回值
有一个accept(T)方法,用于执行消费操作,可以对给定的参数T 做任意操作
2.4.3 Function<T,R>接口
Function<T,R>接口 表示接收一个参数并产生结果的函数
顾名思义,是函数操作的 有一个R apply(T)方法,Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式
2.4.4 Predicate<T>接口
Predicate接口 断言接口
就是做一些判断,返回值为boolean, 有一个boolean test(T)方法,用于校验传入数据是否符合判断条件,返回boolean类型
三.方法引用和构造器调用
3.1 概念说明
Lambda表达式的另外一种表现形式,提高方法复用率和灵活性。
3.2 特点
更简单、代码量更少、复用性、扩展性更高。
3.3 应用场景
若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用。不需要再复写已有API的Lambda的实现。
3.4 代码实现
3.4.1 方法引用
3.4.1.1 对象的引用 :: 实例方法名
public static void main(String[] args) {
// 对象的引用 :: 实例方法名
Integer i1 = new Integer(22);
System.out.println(i1.toString());
// lambda
Supplier<String> su = () -> i1.toString();
System.out.println(su.get());
// 方法引用
Supplier <String> su1 = i1::toString;
System.out.println(su1.get());
}
3.4.1.2 类名 :: 静态方法名
public static void main(String[] args) {
System.out.println(Integer.max(3, 2));
//lambda
//前两位是参数,最后一个是返回值
BiFunction<Integer, Integer, Integer> bf = (x,y) -> Integer.max(x, y);
System.out.println(bf.apply(2, 3));
//方法引用
bf = Integer::max;
System.out.println(bf.apply(2, 3));
}
3.4.1.3 类名 :: 实例方法名
public static void main(String[] args) {
System.out.println("a".equals("a"));
//lambda
BiPredicate<String,String> bp = (x,y) -> x.equals(y);
System.out.println(bp.test("a","a"));
//
bp = String::equals;
System.out.println(bp.test("a","n"));
}
3.4.3 构造器调用
public static void main(String[] args) {
//无参
//lambda
Supplier<Object> su = () -> new Object();
System.out.println(su.get());
//方法引用
su = Object::new;
System.out.println(su.get());
//有参
Function<String,Integer> fun = (x) -> new Integer(x);
System.out.println(fun.apply("233"));
fun = Integer::new;
System.out.println(fun.apply("233"));
}
3.4.4 数组调用
public static void main(String[] args) {
//lambda
Function<Integer,int[]> fun = (n) -> new int[n];
int[] arr = fun.apply(3);
//方法引用
fun = int[]::new;
arr = fun.apply(4);
for (Integer integer : arr) {
System.out.println();
}
}
四.Stream API
4.1 概念说明
数据渠道、管道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算
即一组用来处理数组,集合的API。
4.2 特点
- Stream 不是数据结构,没有内部存储,自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
- 不支持索引访问。
- 延迟计算
- 支持并行
- 很容易生成数据或集合
- 支持过滤,查找,转换,汇总,聚合等操作。
4.3 应用场景
- 流式计算处理,需要延迟计算、更方便的并行计算
- 更灵活、简洁的集合处理方式场景
4.4 代码实现
4.4.1 生成stream流的5种方式
public static void main(String[] args) {
//1. 通过数组创建
String[] strings = {"a","c","v","b"};
Stream<String> stream1 = Stream.of(strings);
//2. 通过集合创建
List<String> list = Arrays.asList(strings);
Stream<String> stream2 = list.stream();
// 3. 通过Stream.generate创建
// 这是一个无限流,无限大,并且每个元素都是1,所以一般结合limit一起使用,限制最大条数
// generate的参数是Supplier , 只有一个get方法,无参有返回值,并且返回的内容就是Stream中的数据
Stream<Integer> stream3 = Stream.generate(() -> 1);
stream3.limit(3).forEach(x -> System.out.print(x+" "));
System.out.println();
//4. 通过Stream.iterate 创建
// 这是一个无限流,无限大,并且元素起始值为1,步长为2
// iterate的参数是UnaryOperator,继承function,有apply()方法,有参有返回值
Stream<Integer> stream4 = Stream.iterate(1, x -> x+2);
stream4.limit(6).forEach(x -> System.out.print(x+" "));
//5. 其他流的API
String string = "acb";
IntStream chars = string.chars();
chars.forEach(x -> System.out.println(x));
}
4.4.2 常用转换算子
- filter : 对元素进行过滤筛选,不符合的就不要了
- distinct : 去掉重复的元素
- skip : 跳过多少元素
- limit : 取一个集合的前几条数据
- map :
可以理解是在遍历集合的过程中,对元素进行操作,比如判断集合元素是否是a 返回boolean
因为 map的返回值,就是新集合中的元素,所以也可以在遍历的时候对集合的数据进行更改,比如都加 –
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "c", "c", "a", "c");
Stream<String> stream = strings.stream();
System.out.println(strings);
/**
* filter : 对元素进行过滤,不符合条件的就不要了
*/
List<String> value = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
System.out.print(value+" ");
System.out.println();
// stream 使用完之后要重新生成
// 否则报错 java.lang.IllegalStateException
/**
* skip : 跳过前面n个数(n为传入的参数)
*/
stream = strings.stream();
value = stream.skip(3).collect(Collectors.toList());
System.out.print(value+" ");
System.out.println();
/**
* map : 在遍历集合中对元素进行操作,返回的数据就要新stream存储的数据
*
* 比如 所有员工涨薪10%
* stream.map(x -> x+x/10)
*/
stream = strings.stream();
value = stream.map(x -> x+"a").collect(Collectors.toList());
System.out.print(value+" ");
System.out.println();
/**
* distinct : 去重
*/
stream = strings.stream();
value = stream.distinct().collect(Collectors.toList());
System.out.print(value+" ");
System.out.println();
/**
* limit : 最大数量
*/
stream = strings.stream();
value = stream.limit(4).collect(Collectors.toList());
System.out.print(value+" ");
System.out.println();
/**
* sortecd : 排序,默认升序,可以通过匿名内部类修改排序条例
*/
List<Integer> list = Arrays.asList(1,4,6,3,5,7,2);
Stream<Integer> stream1 = list.stream();
List<Integer> value1 = stream1.sorted((x,y) -> y-x).collect(Collectors.toList());
System.out.println(value1);
}
4.4.3 常用动作算子
-
循环 forEach
-
计算 min、max、count、average
-
匹配 anyMatch、allMatch、noneMatch、findFirst、findAny
-
汇聚 reduce
-
收集器 collect
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "c", "a");
Stream <String> stream = strings.stream();
/**
* forEach 遍历
*/
stream.forEach(x -> System.out.print(x+" "));
/**
* count
*/
stream = strings.stream();
System.out.println();
// 一般是结合中间算子一起使用,否则直接使用count 还不如使用size
long count = stream.count();
stream = strings.stream();
count = stream.filter(x -> x.equals("a")).count();
System.out.println(count);
/**
* max 和 min
*/
List<Integer> list = Arrays.asList(1,3,2,6,9,4,7,8,5);
Stream<Integer> stream1 = list.stream();
Integer max = stream1.max((x,y) -> x-y).get();
System.out.println(max);
/**
* anyMatch 匹配数据,比如是否包含
*/
stream1 = list.stream();
boolean flag = stream1.anyMatch(x -> x == 2);
List<User> users = new ArrayList<User>();
users.add(new User("张三1", 18));
users.add(new User("张三2", 19));
users.add(new User("张三3", 20));
// 比如 判断是否有用户19岁了
// false
// System.out.println(users.contains(19));
Stream<User> userStream = users.stream();
flag = userStream.anyMatch(x -> x.age == 19);
System.out.println(flag);
}
}
class User {
String name;
int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
五.接口中的默认方法和静态方法
5.1 概念说明
1.8之前接口中只能定义public static final的变量和public abstract修饰的抽象方法。
1.8及以后版本,不仅兼容1.8以前的,并新增了默认方法定义和静态方法定义的功能。
即default方法和static方法。
让接口更灵活、更多变,更能够适应现实开发需要。
5.2 特点
- 默认方法 :
- 可以被重写,也可以不重写。如果重写的话,就按实现类的方法来执行。
- 调用的时候必须是实例化对象调用。
- 静态方法 :
- 跟之前的普通类的静态方法大体相同
- 唯一不同的是不能通过接口的实现类的对象来调用,必须是类.静态方法的方式。
5.3 应用场景
- 默认方法
- 是为了解决之前版本接口升级导致的大批实现类的强制重写方法升级的问题。
- 涉及到接口升级的场景可以多用,从而提高了程序的扩展性、可维护性。
- 静态方法
- 跟默认方法为类似,也是为了解决接口升级的问题,默认方法解决了接口方法增加后其子类不必要全部重写的问题,静态方法解决了一次编写静态方法所有接口及其子类通用的问题,跟lambda表达式并用效果更加,进一步提高了程序扩展性和可维护性。
- 允许在已有的接口中添加静态方法,接口的静态方法属于接口本身,不被继承,也需要提供方法的静态实现后,子类才可以调用。