<Java>27 Java 8 新特性
本文最后更新于:2023年6月27日 上午
27 Java 8 新特性
Java 8 是 Java 语言开发的一个主要版本,其为 Java 带来了大量新特性
27.1 Lambda 表达式
Lambda 是一个匿名函数,可以理解为一段可传递的代码。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 语言的表达能力得到了提升。
(Java 中)Lambda 的本质:作为函数式接口的实例
原代码:
Comparator<Integer> comp = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(comp.compare(11, 10));
使用 Lambda 表达式重写的代码:
Comparator<Integer> comp = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(comp.compare(11, 10));
通过 方法引用 进一步改写代码:
Comparator<Integer> comp = Integer::compare;
System.out.println(comp.compare(11, 10));
27.1.1 Lambda 表达式的使用
Lambda 表达式:在 Java 8 语言中引入的一种新的语法元素和操作符。这个操作符
->
被称为 Lambda 操作符(箭头操作符)。它将 Lambda 分为两部分:左侧:Lambda 形参列表。可以进行类型推断的场合,能省略参数类型;参数唯一的场合,能省略小括号。
右侧:Lambda 体。只有一条语句的场合,能省略大括号和
return
。
-
无参,无返回值:
Runnable r1 = () -> { System.out.println("格式一"); };
-
有参,无返回值:
Consumer<String> con = (String str) -> { System.out.println("格式二"); };
-
有参,无返回值。但参数的数据类型可由编译器推断得出,即 “类型推断”:
Consumer<String> con = (str) -> { System.out.println("格式三"); };
-
有参,无返回值。但只需要一个参数:
Consumer<String> con = str -> { System.out.println("格式四"); };
-
多个参数,多条执行语句,可以有返回值:
Comparator<Integer> comp = (o1, o2) -> { System.out.println("格式五"); return Integer.compare(o1, o2); };
-
只有一条执行语句时,
{ }
和return
都能省略:Comparator<Integer> comp = (o1, o2) -> Integer.compare(o1, o2);
27.2 函数式接口
一个接口中只声明了一个抽象方法,这种接口就被称为函数式接口
可以在一个接口上使用
@FunctionalInterface
注解,以检查其是否是一个函数式接口。
27.2.1 Java 内置的函数式接口
常用的四个函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 | 抽象方法 |
---|---|---|---|---|
Consumer<T> :消费型接口 |
T | void | 对类型 T 的对象进行操作 | void accept(T t) |
Supplier<T> :供给型接口 |
无 | T | 返回类型 T 的对象 | T get() |
Function<T, R> :函数型接口 |
T | R | 对类型 T 的对象进行操作,返回类型 R 的对象 | R apply(T t) |
Predicate<T> :断定型接口 |
T | boolean | 确定类型 T 的对象是否满足约束 | boolean test(T t) |
其他的函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 | 抽象方法 |
---|---|---|---|---|
BiFunction<T, U, R> |
T、U | R | 对类型 T、U 的对象进行操作,返回类型 R 的对象 | R apply(T t, U u) |
UnaryOperator<T> (Function 子接口) |
T | T | 对类型 T 的对象进行一元运算,返回类型 T 的对象 | T apply(T t) |
BinaryOperator<T> (BiFunction 子接口) |
T、T | T | 对类型 T 的对象进行二元运算,返回类型 T 的对象 | T apply(T t1, T t2) |
BiConsumer<T, U> |
T、U | void | 对类型 T、U 的对象进行操作 | void accept(T t, U u) |
BiPredicate<T, U> |
T、U | boolean | 确定类型 T、U 的对象是否满足约束 | boolean test(T t, U u) |
ToIntFunction<T> 、ToLongFunction<T> 、ToDoubleFunction<T> |
T | int / long / double | 计算 int / long /double 值的函数 | int apply(T t) |
IntFunction<T> 、LongFunction<T> 、DoubleFunction<T> |
int / long / double | T | 参数是 int / long /double 的函数 | T applt(int n) |
27.3 方法引用和构造器引用
当要传递给 Lambda 体的操作已经有实现方法了,可以使用方法引用。
方法引用可以看作是 Lambda 表达式深层次的表达。换句话说,方法引用就是 Lambda 表达式,即函数式接口的一个实例。通过方法名指向一个方法。
27.3.1 方法引用
实现接口的抽象方法的参数列表和返回值,与引用方法的参数列表和返回值一致
-
对象::非静态方法
PrintStream out = System.out; Consumer<String> con = out::println;
-
类::静态方法
Supplier<Long> sl = System::currentTimeMillis;
-
类::非静态方法
Comparator<String> bp = String::compareTo;
上面的代码等同于以下写法
Comparator<String> bp = (str1, str2) -> str1.compareTo(str2);
27.3.2 构造器引用
实现接口的抽象方法的参数列表与构造器参数列表一致,返回类型与构造器所在类一致
-
无参构造器
Supplier<String> s = String::new;
等同于以下写法
Supplier<String> s = new Supplier<String>(){ @Override public String get(){ return new String(); } }
-
有参构造器
Function<String, String> function = String::new;
27.3.3 数组引用
把数组看成一个类,则其用法与构造器引用相同
Function<Integer, char[]> f = n -> new char[n];
Function<Integer, char[]> f2 = char[]::new;
27.4 Stream API
Stream API(
java.util.stream
)把真正的函数式编程风格引入到 Java 中。这是目前为止对 Java 类库最好的补充。因为 Stream API 可以极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合进行数据操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream API 和 Collection 的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者主要面向内存,储存于内存。后者主要面向 CPU,通过 CPU 实现计算。
27.4.1 什么是 Stream
Stream 是数据渠道,用于操作数据源所生成的元素序列
- Stream 自己不会储存元素
- Stream 不会改变源对象。他们会返回一个持有结果的新 Stream
- Stream 操作是延迟执行的。这意味着他们会等到需要结果时才执行
27.4.2 Stream 操作步骤
-
创建 Stream:
根据一个数据源,获取一个流
-
中间操作:
一个中间操作链,对数据源的数据进行处理
-
终止操作:
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
#27.4.2.1 创建 Stream
-
通过集合:
List list = new ArrayList(); //某个集合 Stream stream = list.stream(); //返回一个顺序流 Stream stream = list.parallelStream(); //返回一个并行流
-
通过数组:
Arrays 类提供了这个静态方法
int[] ints = new int[2]; //基本数据类型只有这三种 IntStream IS = Arrays.stream(ints); long[] longs = new long[2]; LongStream LS = Arrays.stream(longs); double[] doubles = new double[2]; DoubleStream LS = Arrays.stream(doubles); Object[] objs = new Object[4]; //此处表示一个任意类 Stream<Object> stream = Arrays.stream(objs);
这个方法的重载形式,能够处理对应基本类型的数组
public static IntStream stream(int[] arr)
public static LongStream stream(long[] arr)
public static DoubleStream stream(double[] arr)
-
通过
Stream.of()
方法:Char[] chars = new Char[4]; Stream<Char> CS = Stream.of(chars);
-
创建无限流:
Stream<Integer> iterate = Stream.iterate(1, o -> ++o); Stream<Long> generate = Stream.generate(System::currentTimeMillis);
-
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
-
public static<T> Stream<T> generate(Supplier<T> s)
——看不懂?请看 [ 27.2.1 Java 内置的函数式接口 ]
-
#27.4.2.2 中间操作
多个中间操作连接起一个流水线。该流水线在触发终止操作前不执行任何处理,终止操作触发时一次性进行全部处理。这个流程也称为 “惰性求值”。
-
筛选与切片
方法 描述 filter(Predicate P)
接收一个 Lambda,从流中排除某些元素 distinct()
筛选,通过流生成元素的 hashCode()
和equals()
进行去重linmit(long maxSize)
截断流,使其元素不超过给定数量 skip(long n)
跳过元素,返回一个扔掉了前 n 给元素的流。不足 n 的场合返回空流 -
映射
方法 描述 map(Function f)
接收一个函数,将元素转化成其他形式提取信息 flatMap(Function f)
接收一个函数,将每个元素都转换成另一个流 -
排序
方法 描述 sorted()
产生一个新流,该流按自然顺序排序 sorted(Comparator c)
产生一个新流,该流按比较器顺序排序
#27.4.2.3 终止操作
-
匹配和查找
方法 描述 allMatch(Predicate p)
检查是否匹配全部元素 anyMatch(Predicate p)
检查是否有任意元素匹配 noneMatch(Predicate p)
检查是否任意元素都不匹配 findFirst()
返回第一个元素 findAny()
返回当前流的任意元素 count()
返回当前流的元素个数 max(Comparator c)
返回流中的(比较器顺序下的)最大值 min(Comparator c)
返回流中的(比较器顺序下的)最小值 forEach(Consumer c)
内部迭代 - 关于内部迭代:使用
Collection
接口需要用户去做迭代,称为外部迭代。Stream API 使用内部迭代,不需要用户做迭代。
- 关于内部迭代:使用
-
归约
方法 描述 reduce(T identity, BinaryOperator b)
可以把流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b)
可以把流中元素反复结合起来,得到一个值。返回 Optinal<T>
-
收集
方法 描述 collect(Collector c)
将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法 -
Collector
接口中方法的实现决定了如何对流执行收集的操作另外,
Collectors
实用类提供了很多静态方法,可以方便地创建常见收集器实例。
-
27.5 Optional
类
Optional
类(java.util.Optional
)是一个容器类,它可以保存类型 T 的值,代表这个值存在。也可以仅仅保存 null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好地表达这个概念,而且可以避免空指针异常。
27.5.1 方法
创建方法:
Optional.of(T t)
:创建一个Optional
实例。t
必须非空Optional.empty()
:创建一个空的Optional
实例Optional.ofNullable(T t)
:创建一个Optional
实例。t
可以为 null
判断 Optional
实例是否包含对象:
isPresent()
:判断是否包含对象,返回 booleanifPresent(Consumer c)
:如果有值,则传入该值并执行Consumer
接口的实现代码。
获取 Optional
实例的对象:
get()
:如果有值,返回该值。否则抛出异常。orElse(T other)
:如果有值,返回该值。否则返回指定的 other 对象。orElseGet(Supplier other)
:如果有值,返回该值。否则返回Supplier
接口实现提供的对象。orElseThrow(Supplier excption)
:如果有值,返回该值。否则抛出Supplier
接口实现提供的异常。