一、Java8新特性
1.新特性简介
- 速度更快
- 代码更少(增加了Lambda表达式)
- 强大的 Stream API
- 便于并行
- 最大化减少空指针异常 Optional
- Nashorn引擎,允许在JVM上允许JS应用
2.并行流与串行流
- 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
快速排序快了4.7倍,分组快了5倍,过滤快了5.5倍。
二、Lambda表达式
1.Lambda表达式是什么?
Lambda 表达式是在 JDK 8 中引入的一个新特性,可用于取代大部分的匿名内部类。使用 Lambda 表达式可以完成用少量的代码实现复杂的功能,极大的简化代码代码量和代码结构。同时,JDK 中也增加了大量的内置函数式接口供我们使用,使得在使用 Lambda 表达式时更加简单、高效。
2.为什么要用?
函数式编程在 C#、Python、JavaScript中都得到充分体现,在 Java 8 版本中也得到了支持。最明显的就是对 Lambda 表达式的支持。很多种迹象表明未来编程语言将是逐渐融合各自的特性,而不是单纯的声明式语言函数编程语言。将来声明式编程语言借鉴函数编程思想,函数编程语言融合声明式编程特性,这几乎是一种必然趋势。
在 Java 中主要引入 Lambda 表达式的作用是对现有编码语义的优化,减少语法冗余。轻量级的将代码封装为数据,使代码简洁,易于理解。
3.怎么用?
演变过程:垃圾代码 --> 策略模式 --> 匿名内部类 --> Lambda表达式
3.1 格式
- ->:lambda操作符 或 箭头操作符
- ->左边:lambda形参列表(其实就是接口中的抽象方法的形参列表)
- ->右边:lambda体(其实就是重写的抽象方法的方法体)
口诀:
- 写死小括号,拷贝右箭头,落地大括号
- 左右遇一括号省
- 左侧推断类型省
3.2 使用(6种情况)
总结:
- 左边:lambda形参列表的参数类型可以省略(类型推断),如果lambda形参列表只有一个参数,其一对()也可以省略;
- 右边:lambda体应该使用一堆{}包裹;如果lambda体只有一条执行语句(可能是return语句),可以省略这一对{}和return关键字
三、函数式接口和定义
1.什么是函数式接口?
函数式接口(Functional Interface)是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。Java 中函数式接口被隐式转换为 Lambda 表达式,只有保证接口类中有且只有一个抽象方法,Java 中的 Lambda 表达式才能对该方法进行推导。
2.函数式接口格式
在 Java 函数式接口类中,需要满足接口类中只能有一个抽象方法。总结如下:
- 接口有且仅有一个抽象方法;
- 允许定义静态方法;
- 允许定义默认方法;
- 允许 java.lang.Object 中的 public 方法;
在创建函数式接口时,可以在接口类上面加上@FunctionalInterface注解,这时编译器就可以对接口结构进行强制检查是否符合函数式接口规则,如果不符合规则就显示错误。当然,这个注解只是用于检查,即使不加上该注解,只要符合函数式接口规则一样也是函数式接口。下面创建个演示的函数式接口,如下:
// @FunctionalInterface 注解说明: // 使用该注解来定义接口,编译器会强制检查接口是否符合函数式接口规则(有且仅有一个抽象方法),如果不符合则会报错。 @FunctionalInterface public interface MyInterface{ /** * 抽象方法(Jdk 8 以后接口类中方法可以省去 public abstract) */ public abstract [返回值类型] [方法名称](参数列表); /** * 其它方法(Jdk 8 以后允许接口类中添加"默认方法"与"静态方法" ) */ ...(略) }
按照上面函数式接口,定义一个示例的函数式接口类,代码如下:
@FunctionalInterface public interface MyCollection { void push(List list); }
3.函数式接口和Lambda表达式的关系
函数式接口和 Lambda 表达式的关系可以总结为:
- 函数式接口只包含一个操作方法;
- Lambda 表达式只能操作一个方法;
- Java 中的 Lambda 表达式核心就是一个函数式编程接口的实现。
4.当前JDK8中存在的函数式接口类
在 JDK 1.8 之前,已经存在部分函数式接口,如下:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
在 JDK 8 中新增了函数接口 java.util.function 包,里面包含了很多用来支持 Java 的函数式编程的接口类,如下:
类名称 | 描述信息 |
---|---|
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果。 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果。 |
BinaryOperator<T> | 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果。 |
BiPredicate<T,U> | 代表了一个两个参数的 boolean 值方法。 |
BooleanSupplier | 代表了 boolean 值结果的提供方。 |
Consumer<T> | 代表了接受一个输入参数并且无返回的操作。 |
DoubleBinaryOperator | 代表了作用于两个 double 值操作符的操作,并且返回了一个 double 值的结果。 |
DoubleConsumer | 代表一个接受 double 值参数的操作,并且不返回结果。 |
DoubleFunction<R> | 代表接受一个 double 值参数的方法,并且返回结果。 |
DoublePredicate | 代表一个拥有 double 值参数的 boolean 值方法。 |
DoubleSupplier | 代表一个 double 值结构的提供方。 |
DoubleToIntFunction | 接受一个 double 类型输入,返回一个 int 类型结果。 |
DoubleToLongFunction | 接受一个 double 类型输入,返回一个 long 类型结果。 |
DoubleUnaryOperator | 接受一个参数同为类型 double,返回值类型也为 double。 |
Function<T,R> | 接受一个输入参数,返回一个结果。 |
IntBinaryOperator | 接受两个参数同为类型 int,返回值类型也为 int。 |
IntConsumer | 接受一个 int 类型的输入参数,无返回值。 |
IntFunction<R> | 接受一个 int 类型输入参数,返回一个结果。 |
IntPredicate | 接受一个 int 输入参数,返回一个布尔值的结果。 |
IntSupplier | 无参数,返回一个 int 类型结果。 |
IntToDoubleFunction | 接受一个 int 类型输入,返回一个double类型结果。 |
IntToLongFunction | 接受一个 int 类型输入,返回一个 long 类型结果。 |
IntUnaryOperator | 接受一个参数同为类型 int,返回值类型也为 int。 |
LongBinaryOperator | 接受两个参数同为类型 long,返回值类型也为 long。 |
LongConsumer | 接受一个 long 类型的输入参数,无返回值。 |
LongFunction<R> | 接受一个 long 类型输入参数,返回一个结果。 |
LongPredicate | R接受一个 long 输入参数,返回一个布尔值类型结果。 |
LongSupplier | 无参数,返回一个结果 long 类型的值。 |
LongToDoubleFunction | 接受一个 long 类型输入,返回一个 double 类型结果。 |
LongToIntFunction | 接受一个 long 类型输入,返回一个 int 类型结果。 |
LongUnaryOperator | 接受一个参数同为类型 long,返回值类型也为 long。 |
ObjDoubleConsumer<T> | 接受一个 object 类型和一个 double 类型的输入参数,无返回值。 |
ObjIntConsumer<T> | 接受一个 object 类型和一个 int 类型的输入参数,无返回值。 |
ObjLongConsumer<T> | 接受一个 object 类型和一个 long 类型的输入参数,无返回值。 |
Predicate<T> | 接受一个输入参数,返回一个布尔值结果。 |
Supplier<T> | 无参数,返回一个结果。 |
ToDoubleBiFunction<T,U> | 接受两个输入参数,返回一个 double 类型结果 |
ToDoubleFunction<T> | 接受一个输入参数,返回一个 double 类型结果。 |
ToIntBiFunction<T,U> | 接受两个输入参数,返回一个 int 类型结果。 |
ToIntFunction<T> | 接受一个输入参数,返回一个 int 类型结果。 |
ToLongBiFunction<T,U> | 接受两个输入参数,返回一个 long 类型结果。 |
ToLongFunction<T> | 接受一个输入参数,返回一个 long 类型结果。 |
UnaryOperator<T> | 接受一个参数为类型 T,返回值类型也为 T。 |
5.JDK中常见的函数式接口类
上面 java.util.function 包提供了众多的函数式接口,其中常用的有:
- java.util.function.Predicate<T>:接收参数对象 T,返回一个 boolean 类型结果。
- java.util.function.Comsumer<T>:接收参数对象 T,不返回结果。
- java.util.function.Function<T,R>:接收参数对象 T,返回结果对象 R。
- java.util.function.Supplier<T>:不接收参数,提供 T 对象的创建工厂。
- java.util.function.UnaryOperator<T>:接收参数对象 T,返回结果对象 T。
- java.util.function.BinaryOperator<T>:接收两个 T 对象,返回一个 T 对象结果。
5.1 java.util.function.Predicate<T>
- 接口类作用: 接收参数对象T,返回一个 boolean 类型结果。
- 接口类源码:
四、Lambda表达式基本语法
1.Lambda表达式的组成
Lambda 表达式的组成可以拆分为:
- 声明: 与 Lambda 表达式绑定的接口类型。
- 参数: 参数包含在一对 () 中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
- 操作符: ->
- 执行代码块: 执行代码块包含在一对 {} 中,出现在操作符的右侧。
[接口声明] = (参数) -> {执行代码块}
2.Lambda表达式的格式
Lambda 表达式可以分为下面几种格式:
- 无参数,无返回值;
- 有一个参数,无返回值;
- 左侧只有一个参数,小括号可以省略不写;
- 有两个以上参数,有返回值,并且Lambda 体中有多条语句;
- 若右侧Lambda体中,只有一条语句,return 和大括号都可以省略不写;
- Lambda 表达式的参数列表的数据类型可以省略不写,jvm编译器会进行上下文推断出,数据类型“类型推断”;
2.1 无参数,无返回值
() -> System.out.println("测试");
2.2 有一个参数,无返回值
(x) -> System.out.println(x);
2.3 左侧只有一个参数,小括号可以省略不写
x -> System.out.println(x);
2.4 有两个以上参数,有返回值,并且Lambda 体中有多条语句
Comparator<Integer> comparator = (x, y) -> { System.out.println("测试"); return Integer.compare(x,y); };
2.5 若右侧Lambda体中,只有一条语句,return 和大括号都可以省略不写
Comparator<Integer> Comparator = (x, y) -> Integer.compare(x, y);
2.6 Lambda 表达式的参数列表的数据类型可以省略不写,JVM 在运行时,会自动根据绑定的抽象方法中的参数,进行数据类型推导
(Integer x, Integer y) -> Integer.compare();
五、Lambda表达式中变量作用域
Java 中的变量捕获与变量隐藏:
- 变量捕获: 局部类和匿名内部类可以访问被 final 修饰的封闭块内的局部变量。
- 变量隐藏: 在一个类中,子类中的成员变量如果和父类中的成员变量同名,那么即使他们类型不一样,只要名字一样,父类中的成员变量都会被隐藏。
在局部类和匿名内部类都存在 变量捕获 与 变量隐藏,而在 Lambda 表达式中则只支持 变量捕获。
六、Lambda表达式方法重载问题
六、Lambda表达式方法引用
1.创建示例的实体类
2.静态方法引用示例
3.实例方法引用示例
4.构造方法引用示例
5.特定类型的任意对象实例方法引用示例
参考链接:
【1】Java8 Stream简介 - -Finley- - 博客园
【2】Arrays.toList() 和Collections.singletonList()的区别_Warren Blog-CSDN博客
【4】Java 8 学习笔记_weixin_45225595的博客-CSDN博客
【5】Java 8 中使用 Stream 方式处理数据 · 小豆丁个人博客