<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

  1. 无参,无返回值:

    Runnable r1 = () -> {
        System.out.println("格式一");
    };
  2. 有参,无返回值:

    Consumer<String> con = (String str) -> {
        System.out.println("格式二");
    };
  3. 有参,无返回值。但参数的数据类型可由编译器推断得出,即 “类型推断”:

    Consumer<String> con = (str) -> {
        System.out.println("格式三");
    };
  4. 有参,无返回值。但只需要一个参数:

    Consumer<String> con = str -> {
        System.out.println("格式四");
    };
  5. 多个参数,多条执行语句,可以有返回值:

    Comparator<Integer> comp = (o1, o2) -> {
        System.out.println("格式五");
        return Integer.compare(o1, o2);
    };
  6. 只有一条执行语句时,{ }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 方法引用

实现接口的抽象方法的参数列表和返回值,与引用方法的参数列表和返回值一致

  1. 对象::非静态方法

    PrintStream out = System.out;
    Consumer<String> con = out::println;
  2. 类::静态方法

    Supplier<Long> sl = System::currentTimeMillis;
  3. 类::非静态方法

    Comparator<String> bp = String::compareTo;

    上面的代码等同于以下写法

    Comparator<String> bp = (str1, str2) -> str1.compareTo(str2);

27.3.2 构造器引用

实现接口的抽象方法的参数列表与构造器参数列表一致,返回类型与构造器所在类一致

  1. 无参构造器

    Supplier<String> s = String::new;

    等同于以下写法

    Supplier<String> s = new Supplier<String>(){
        @Override
        public String get(){
            return new String();
        }
    }
  2. 有参构造器

    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 操作步骤

  1. 创建 Stream:

    根据一个数据源,获取一个流

  2. 中间操作:

    一个中间操作链,对数据源的数据进行处理

  3. 终止操作:

    一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

#27.4.2.1 创建 Stream

  1. 通过集合:

    List list = new ArrayList();				//某个集合
    Stream stream = list.stream();				//返回一个顺序流
    Stream stream = list.parallelStream();		//返回一个并行流
  2. 通过数组:

    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)
  3. 通过 Stream.of() 方法:

    Char[] chars = new Char[4];
    Stream<Char> CS = Stream.of(chars);
  4. 创建无限流:

    Stream<Integer> iterate = Stream.iterate(1, o -> ++o);
    Stream<Long> generate = Stream.generate(System::currentTimeMillis);

#27.4.2.2 中间操作

多个中间操作连接起一个流水线。该流水线在触发终止操作前不执行任何处理,终止操作触发时一次性进行全部处理。这个流程也称为 “惰性求值”。

  1. 筛选与切片

    方法 描述
    filter(Predicate P) 接收一个 Lambda,从流中排除某些元素
    distinct() 筛选,通过流生成元素的 hashCode()equals() 进行去重
    linmit(long maxSize) 截断流,使其元素不超过给定数量
    skip(long n) 跳过元素,返回一个扔掉了前 n 给元素的流。不足 n 的场合返回空流
  2. 映射

    方法 描述
    map(Function f) 接收一个函数,将元素转化成其他形式提取信息
    flatMap(Function f) 接收一个函数,将每个元素都转换成另一个流
  3. 排序

    方法 描述
    sorted() 产生一个新流,该流按自然顺序排序
    sorted(Comparator c) 产生一个新流,该流按比较器顺序排序

#27.4.2.3 终止操作

  1. 匹配和查找

    方法 描述
    allMatch(Predicate p) 检查是否匹配全部元素
    anyMatch(Predicate p) 检查是否有任意元素匹配
    noneMatch(Predicate p) 检查是否任意元素都不匹配
    findFirst() 返回第一个元素
    findAny() 返回当前流的任意元素
    count() 返回当前流的元素个数
    max(Comparator c) 返回流中的(比较器顺序下的)最大值
    min(Comparator c) 返回流中的(比较器顺序下的)最小值
    forEach(Consumer c) 内部迭代
    • 关于内部迭代:使用 Collection 接口需要用户去做迭代,称为外部迭代。Stream API 使用内部迭代,不需要用户做迭代。
  2. 归约

    方法 描述
    reduce(T identity, BinaryOperator b) 可以把流中元素反复结合起来,得到一个值。返回 T
    reduce(BinaryOperator b) 可以把流中元素反复结合起来,得到一个值。返回 Optinal<T>
  3. 收集

    方法 描述
    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():判断是否包含对象,返回 boolean
  • ifPresent(Consumer c):如果有值,则传入该值并执行 Consumer 接口的实现代码。

获取 Optional 实例的对象:

  • get():如果有值,返回该值。否则抛出异常。
  • orElse(T other):如果有值,返回该值。否则返回指定的 other 对象。
  • orElseGet(Supplier other):如果有值,返回该值。否则返回 Supplier 接口实现提供的对象。
  • orElseThrow(Supplier excption):如果有值,返回该值。否则抛出 Supplier 接口实现提供的异常。

<Java>27 Java 8 新特性
https://i-melody.github.io/2022/03/09/Java/入门阶段/27 Java 8 新特性/
作者
Melody
发布于
2022年3月9日
许可协议