JDK1.8新特性
1.Lambad表达式
1.1 介绍
是一种没有名字的函数,也可以称为闭包
本质上是一段匿名内部类,也可以是一段可以传递的代码,还有叫箭头函数的
闭包:
能够读取其他函数内部变量的函数,比如在Java中,方法内部的局部变量只能
在方法内部使用,本质就是将函数内部和外部链接起来的桥梁
1.2 特点
允许把函数作为一个方法的参数,
使用Lambda表达式可以是代码变得更加简洁紧凑
1.2 和匿名内部类对比
//asList:把数组转换为List集合
List<Integer> integer = Arrays.asList(arr);
//匿名内部类写法
Collections.sort(integers, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// lambda
Collections.sort(integers, (i1, i2) -> i2 - i1);
System.out.println(integers);
1.3应用场景
列表迭代
Map映射
Reduce聚合
代替一个不想命名的函数或是类,该函数或类往往并不复杂
1.4 代码实现
特点:
可选类型声明:不需要声明参数类型,编译器何以统一识别参数值
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
可选的大括号:如果主体包含了一个语句,就不需要使用大括号
可选的返回关键字:如果主题只有一个表达式返回值则编译器会自动返回值,
大括号需要指定明表达式返回了一个数值
如果只有一条语句,并且是返回值语句,就可以不写return 不写{}
如果写上{},就必须写return和;
如果有多条语句,必须写{} return 和 ;
*简单说明:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2*x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
集合遍历:
list.forEach(x->{ System.out.println(x);})
集合排序:
list.sort(new Comparator<Integer>(){
public int compare(Integer o1,Integer o2){
return o1 - o2;
}
})
list = Arrays.asList(arr);
list.sort((x,y)->x-y);
System.out.println(list);
2.函数式接口
2.1 介绍:
本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
核心目标是为了给Lambda表达式的使用提供更好的支持,提高效率
2.2 回调函数:
方法的参数是一个方法,在这个方法中对传递的方法进行调用
2.3 应用场景
想通过函数式编程,提高编程效率的各种场景均可
2.4 代码实现
2.4.1 无参情况
public class FunInterface_01 {
// 自定义静态方法,接收接口对象
public static void call(MyFunctionInter func) {
// 调用接口内的成员方法
func.printMessage();
}
public static void main(String[] args) {
// 第一种调用 : 直接调用自定义call方法,传入函数
FunInterface_01.call(() -> {
System.out.println("HelloWorld!!!");
});
// 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象
MyFunctionInter inter = () -> {
System.out.println("HelloWorld2!!!!");
};
// 调用这个实现的方法
inter.printMessage();
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionInter {
void printMessage();
}
2.4.2 有参情况
public class FunInterface_02 {
// 自定义静态方法,接收接口对象
public static void call(MyFunctionInter_02 func, String message) {
// 调用接口内的成员方法
func.printMessage(message);
}
public static void main(String[] args) {
// 调用需要传递的数据
String message = "有参函数式接口调用!!!";
// 第一种调用 : 直接调用自定义call方法,传入函数,并传入数据
FunInterface_02.call((str) -> {
System.out.println(str);
}, message);
// 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象
MyFunctionInter_02 inter = (str) -> {
System.out.println(str);
};
// 调用这个实现的方法
inter.printMessage(message);
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionInter_02 {
void printMessage(String message);
}
2.5 JDK自带常用的函数式接口
2.5.1 Supplier<T>接口
Supplier<T>接口 代表结果供应商,所以有返回值,可以获取数据
有一个get方法,用于获取数据
2.5.2 Consumer<T>接口 消费者接口 不需要返回值,有一个accept(T)方法,
用于执行消费操作,可以对给定的参数T做任意操作
2.5.3 Function<T,R>接口
表示接收一个参数并产生结果的函数有一个R apply(T)方法,Function中没有具体的操作,
具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式
2.5.4 Predicate<T>接口
断言接口,返回值为boolean,有一个boolean test(T)方法,用于校验传入数据是否符合判断条件,返回boolean类型
3.方法引用和构造器调用
3.1概念说明
Lambda表达式的另外一种表现形式,提高方法复用率和灵活性。
3.2 应用场景:
若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用。
不需要再复写已有API的Lambda的实现。
3.4代码实现:
1.对象的引用::实例方法名
Integer intObj = new Integer(123456);
// 常规lambda写法
Supplier<String> su = () -> intObj.toString();
System.out.println(su.get());
// 方法引用写法
Supplier<String> su1 = intObj::toString;
System.out.println(su1.get());
2.类名::静态方法名
// 常规lambda写法
// 前两个泛型是参数类型,第三个是返回值类型
BiFunction<Integer, Integer, Integer> bi = (x, y) -> Integer.max(x, y);
int apply = bi.apply(10, 11);
System.out.println(apply);
// 方法引用写法
BiFunction<Integer, Integer, Integer> bif = Integer::max;
int apply2 = bif.apply(10, 11);
System.out.println(apply2);
3.类名::实例方法名
// 常规lambda写法
// 两个泛型都是参数类型
BiPredicate<String, String> predicate = (x, y) -> x.equals(y);
System.out.println(predicate.test("a", "a"));
// 方法引用写法
// 使用第一个参数调用成员方法把第二个参数传入
// ::前面的类型 要和 第一个参数的类型一致
BiPredicate<String, String> predicate2 = String::equals;
System.out.println(predicate2.test("a", "b"));
构造器调用:
// 无参构造器
// 常规lambda写法
Supplier<Object> objSup = () -> new Object();
System.out.println(objSup.get());
// 方法引用写法
Supplier<Object> s1 = Object::new;
System.out.println(s1.get());
// 有参构造器
// 常规lambda写法
Function<String, Integer> func = (x) -> new Integer(x);
System.out.println(func.apply("123456"));
// 方法引用写法
Function<String, Integer> func2 = Integer::new;
System.out.println(func2.apply("123456"));
4.Stream API
1.特点:
Stream 不是数据结构,没有内部存储,自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
不支持索引访问。
延迟计算
支持并行
很容易生成数据或集合
支持过滤,查找,转换,汇总,聚合等操作。
2.运行机制说明
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
中间操作也称为转换算子-transformation
Stream只有遇到终止操作,它的数据源会开始执行遍历操作。
终止操作也称为动作算子-action
因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
3.生成stream流的五种方式说明
// 1 通过数组,Stream.of()
String[] str = { "a", "b", "c" };
Stream<String> str1 = Stream.of(str);
// System.out.println(str1);
// 2 通过集合
List<String> strings = Arrays.asList("a", "b", "c");
Stream<String> stream = strings.stream();
System.out.println(stream);
// 3 通过Stream.generate方法来创建
// 这是一个无限流,通过这种方法创建在操作的时候最好加上limit进行限制
Stream<Integer> generate = Stream.generate(() -> 1);
generate.limit(10).forEach(x -> System.out.println(x));
// 4 通过Stream iterate
Stream<Integer> iterate = Stream.iterate(1, x -> x + 2);
iterate.limit(100).forEach(x -> System.out.println(x));
// 5 已有类的stream源生成API
String str2 = "123456";
IntStream chars = str2.chars();
chars.forEach(x -> System.out.println(x));
4.常用转换算子
常用转换算子 filter,distinct,map,limit,skip,flatMap等
filter : 对元素进行过滤筛选,不符合的就不要了
distinct : 去掉重复的元素
skip : 跳过多少元素
limit : 取一个集合的前几条数据
map :
可以理解是在遍历集合的过程中,对元素进行操作,比如判断集合元素是否是a 返回boolean
因为 map的返回值,就是新集合中的元素,所以也可以在遍历的时候对集合的数据进行更改,比如都加 --
flatMap : 解决一个字符串数组 返回单一的字符串使用flatMap
本来集合中有两个数组,可以通过flatMap 把数组中的每一个元素都放到集合中,然后把数组去掉
注意只用此算子是不会真正进行计算的,只有后边调用动作算子才会真正计算。
List<String> strings = Arrays.asList("a", "b", "c", "a");
Stream<String> stream = strings.stream();
/**
* 对元素进行过滤筛选,不符合的就不要了
*/
// collect 把符合条件的转换为集合strings,属于动作算子,因为不用动作算子这些转换算子不会执行,所以看不到结果
// 只要 a
List<String> value = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
// 集合中只有 a 了
System.out.println(value);
// 使用过之后,需要重新生成stream
stream = strings.stream();
/**
* 跳过1个元素 原来是 abca 现在就是 bca
*/
value = stream.skip(1).collect(Collectors.toList());
// bca
System.out.println(value);
/**
* map : 可以理解是在遍历集合的过程中,对元素进行操作,比如判断集合元素是否是a 返回boolean
* 或者对集合元素进行更改数据,比如都加--
*/
stream = strings.stream();
// 判断集合元素,这样就是booelean 是a 就是true 否则就是false
List<Boolean> value1=stream.map(x -> x.equals("a")).collect(Collectors.toList());
// true,false,false,true
System.out.println(value1);
stream = strings.stream();
// 更改集合元素
value =stream.map(x -> x+"--").collect(Collectors.toList());
// a--, b--, c--, a--
System.out.println(value);
/**
* 去掉重复元素
*/
stream = strings.stream();
value = stream.distinct().collect(Collectors.toList());
// 去除一个a 只有a,b,c
System.out.println(value);
/**
* 取一个集合的前几条数据
*/
stream = strings.stream();
value = stream.limit(2).collect(Collectors.toList());
// ab
System.out.println(value);
/**
* 解决一个字符串数组 返回单一的字符串使用flatMap
*/
strings = Arrays.asList("1,2,3", "a,b,c");
stream = strings.stream();
// 本来集合中有两个数据 "1,2,3" 和 "a,b,c"
// 会把每一个元素 以 , 分割,得到字符串数组
// 然后把数组中每一个元素,都单独拿出来
// 最终就会得到 1,2,3,a,b,c 6个元素
// 通过 collect 把这6个元素 放到集合中
value =stream.map(x -> x.split(",")).flatMap(arr -> Arrays.stream(arr)).collect(Collectors.toList());
// 1, 2, 3, a, b, c
System.out.println(value);
5.常用动作算子
循环 forEach
计算 min、max、count、average
匹配 anyMatch、allMatch、noneMatch、findFirst、findAny
汇聚 reduce
收集器 collect
5.接口中的默认方法和静态方法
1.特点:
默认方法
可以被重写,也可以不重写。如果重写的话,就按实现类的方法来执行。
调用的时候必须是实例化对象调用。
静态方法
跟之前的普通类的静态方法大体相同
唯一不同的是不能通过接口的实现类的对象来调用,必须是类.静态方法的方式。
2.代码实现
public static void main(String[] args) {
MyInter inter = new MyInterImpl();
System.out.println(inter.add(3, 4));
inter.printMessage();
}
}
interface MyInter {
// 之前的用法
int add(int i, int j);
// 新加的默认方法
default void printMessage() {
System.out.println("在接口中的默认实现");
}
}
class MyInterImpl implements MyInter {
@Override
public int add(int i, int j) {
return i + j;
}
@Override
public void printMessage() {
System.out.println("在接口的实现类中的默认实现");
}