Java 8 特性: Lambda 表达式、Stream API、Optional 类等

646 阅读9分钟

Java 8 引入了很多新的特性,极大地增强了 Java 语言的表达能力和开发效率。以下是一些主要的新特性

1.Lambda 表达式

Lambda 表达式允许以更简洁的方式表示匿名函数,使得代码更加简洁和可读。它支持函数式编程风格,并可以作为参数传递给方法。

语法:

    (parameters) -> expression

示例:

    // 使用Lambda表达式表示Runnable接口
    // Lambda 表达式 无参数:如果没有参数,可以省略括号
    //  一个参数:如果只有一个参数,括号可以省略。 x -> x * x   //传入一个参数并返回其平方
    // 多个参数:多个参数时需要使用括号来包含。(x, y) -> x + y   //传入两个参数并返回它们的和
    // 带有代码块:如果 Lambda 表达式的主体包含多个语句,需要使用大括号 {} 来包裹代码块
    // (x, y) -> {
    // int sum = x + y;
    // return sum;
    // }
Runnable
r = () -> System.out.println("Hello from Lambda");
r.run();

Lambda 表达式的优势

  • 简化代码:Lambda 表达式使得代码更加简洁,减少了冗长的匿名类代码。
  • 提高可读性:通过明确的行为表达式,Lambda 让代码变得更加直观和易于理解。
  • 支持函数式编程:Lambda 表达式使得 Java 更好地支持函数式编程,支持高阶函数、组合函数等。
  • 与 Stream API 结合使用:与 Java 8 引入的 Stream API 结合,Lambda 可以方便地处理集合数据,进行过滤、映射、排序等操作。

Lambda 表达式的实际应用场景

  • 集合框架:通过 Lambda 表达式简化集合元素的操作,特别是在 List、Set、Map 等集合上进行过滤、映射、排序等操作。
  • 事件处理:在 GUI 编程中,Lambda 可以简化事件监听器的实现。
  • 函数式接口:Java 8 中定义了很多函数式接口(如 Runnable、Callable、Comparator 等),可以通过 Lambda 表达式来简化它们的实现。

2.Java 8 java.util.function 包及常见接口详解

Java 8 引入了 java.util.function 包,该包提供了一组核心函数式接口,支持 Lambda 表达式和方法引用。这些接口简化了代码,使其更加简洁、可读和易于维护。


常用的函数式接口一览

接口名描述函数式方法
Predicate<T>条件判断(过滤)boolean test(T)
Function<T, R>类型转换(映射)R apply(T)
Consumer<T>消费型操作(无返回)void accept(T)
Supplier<T>供给型操作(返回数据)T get()
UnaryOperator<T>一元运算符(同类型转换)T apply(T)
BinaryOperator<T>二元运算符(同类型运算)T apply(T, T)

1. Predicate<T>:条件判断

Predicate<T> 用于条件判断,常用于过滤操作。

核心方法:

  • boolean test(T t):对给定的输入执行判断,返回布尔值。

默认方法:

  • and(Predicate other):逻辑与操作。
  • or(Predicate other):逻辑或操作。
  • negate():逻辑非操作。

示例:判断字符串是否为空

import java.util.function.Predicate;

public class PredicateExample {
    public static void main(String[] args) {
        Predicate<String> isEmpty = String::isEmpty;
        Predicate<String> isNotEmpty = isEmpty.negate();
        System.out.println(isEmpty.test(""));   // 输出: true
        System.out.println(isNotEmpty.test("Java")); // 输出: true
    }
}

2. Function<T, R>:类型转换

Function<T, R> 用于将一个类型转换为另一个类型,常用于映射操作。

核心方法:

  • R apply(T t):将输入参数 T 转换为结果 R

默认方法:

  • andThen(Function after):在当前函数执行后执行另一个函数。
  • compose(Function before):在当前函数执行前执行另一个函数。

示例:字符串长度映射与组合

import java.util.function.Function;

public class FunctionExample {
    public static void main(String[] args) {
        Function<String, Integer> strLength = String::length;
        Function<Integer, Integer> doubleValue = x -> x * 2;

        System.out.println(strLength.apply("Java"));  // 输出: 4
        System.out.println(strLength.andThen(doubleValue).apply("Java"));  // 输出: 8
    }
}

3. Consumer<T>:数据消费

Consumer<T> 用于操作数据,没有返回值,常用于打印或存储。

核心方法:

  • void accept(T t):接收参数并执行操作。

默认方法:

  • andThen(Consumer after):组合多个消费者操作。

示例:打印数据并组合操作

import java.util.function.Consumer;

public class ConsumerExample {
    public static void main(String[] args) {
        Consumer<String> printer = System.out::println;
        Consumer<String> greeter = name -> System.out.println("Hello, " + name);

        printer.accept("Java!"); // 输出: Java!
        greeter.andThen(printer).accept("World"); // 输出: Hello, World
        // 输出: World
    }
}

4. Supplier<T>:数据提供

Supplier<T> 提供数据,没有输入参数,常用于生成数据。

核心方法:

  • T get():返回一个结果。

示例:生成随机数与当前时间

import java.util.function.Supplier;
import java.util.Random;

public class SupplierExample {
    public static void main(String[] args) {
        Supplier<Integer> randomNumber = () -> new Random().nextInt(100);
        Supplier<Long> currentTime = System::currentTimeMillis;

        System.out.println("随机数: " + randomNumber.get());  // 输出: 随机整数
        System.out.println("当前时间: " + currentTime.get()); // 输出: 当前时间戳
    }
}

5. UnaryOperator<T>:一元运算

UnaryOperator<T> 是特殊的 Function<T, T>,用于相同类型的数据转换。

核心方法:

  • T apply(T t):将输入参数 T 转换为 T

示例:将字符串转换为大写

import java.util.function.UnaryOperator;

public class UnaryOperatorExample {
    public static void main(String[] args) {
        UnaryOperator<String> toUpperCase = String::toUpperCase;
        System.out.println(toUpperCase.apply("java"));  // 输出: JAVA
    }
}

6. BinaryOperator<T>:二元运算

BinaryOperator<T> 是特殊的 BiFunction<T, T, T>,用于两个相同类型的数据运算。

核心方法:

  • T apply(T t1, T t2):对两个参数执行操作并返回结果。

示例:两个整数相加与最大值比较

import java.util.function.BinaryOperator;

public class BinaryOperatorExample {
    public static void main(String[] args) {
        BinaryOperator<Integer> add = Integer::sum;
        BinaryOperator<Integer> max = Integer::max;

        System.out.println("求和: " + add.apply(5, 3));  // 输出: 8
        System.out.println("最大值: " + max.apply(5, 3));  // 输出: 5
    }
}

总结

java.util.function 包提供的接口能够帮助我们以更加简洁的方式实现函数式编程。Lambda 表达式和这些接口的结合,使得代码更加简洁和易于理解。熟练掌握这些接口,可以在日常开发中写出更优雅的代码。

Java Stream API 深入解析与实用示例

Java 8 引入了 Stream API,极大简化了对集合数据的处理。通过流操作,开发者可以实现复杂的数据操作,如过滤、排序和聚合,代码更简洁、更具可读性。


3.什么是 Stream API?

Stream API 提供了一种函数式编程风格的数据处理方式,主要用于对集合数据(如 List, Set, Map)进行操作。它支持以下功能:

  • 过滤(Filtering)
  • 映射(Mapping)
  • 排序(Sorting)
  • 统计与聚合(Reduction)
  • 收集结果(Collecting)

Stream 流的生命周期

Stream 的使用包含以下三个步骤:

  1. 创建流 (Stream Source): 从集合、数组等数据源创建流。
  2. 中间操作 (Intermediate Operations): 对数据执行链式转换操作。
  3. 终止操作 (Terminal Operations): 执行终结操作,如收集结果或计算。

3.Stream 的常见操作与示例

1. 创建流 (Stream Creation)

  • 从集合创建流:

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    Stream<String> nameStream = names.stream();
    
  • 从数组创建流:

    int[] numbers = {1, 2, 3, 4, 5};
    IntStream numberStream = Arrays.stream(numbers);
    
  • 使用 Stream.of() 方法:

    Stream<String> stream = Stream.of("Java", "Python", "C++");
    

2. 中间操作 (Intermediate Operations)

1. 过滤 (filter)
  • 过滤出长度大于 3 的字符串:
    List<String> names = Arrays.asList("Java", "C", "Python", "Go");
    names.stream()
        .filter(name -> name.length() > 2)
        .forEach(System.out::println);
    
2. 映射 (map)
  • 将字符串转换为大写:
    List<String> languages = Arrays.asList("java", "python", "go");
    languages.stream()
        .map(String::toUpperCase)
        .forEach(System.out::println);
    
3. 排序 (sorted)
  • 按字母顺序排序:
    List<String> fruits = Arrays.asList("Apple", "Orange", "Banana");
    fruits.stream()
        .sorted()
        .forEach(System.out::println);
    
4. 去重 (distinct)
  • 去除重复的元素:
    List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 4);
    numbers.stream()
        .distinct()
        .forEach(System.out::println);
    
5. 限制和跳过 (limit / skip)
  • 获取前两个元素:

    Stream.of("A", "B", "C", "D")
        .limit(2)
        .forEach(System.out::println);
    
  • 跳过前两个元素:

    Stream.of("A", "B", "C", "D")
        .skip(2)
        .forEach(System.out::println);
    

3. 终结操作 (Terminal Operations)

1. 收集 (collect)
  • 将流转换为集合:
    List<String> filteredNames = names.stream()
        .filter(name -> name.length() > 3)
        .collect(Collectors.toList());
    System.out.println(filteredNames);
    
2. 统计 (count)
  • 统计列表中以 "J" 开头的名字数量:
    long count = names.stream()
        .filter(name -> name.startsWith("J"))
        .count();
    System.out.println("数量: " + count);
    
3. 匹配 (anyMatch / allMatch / noneMatch)
  • 检查是否有任何字符串以 "A" 开头:
    boolean exists = names.stream()
        .anyMatch(name -> name.startsWith("A"));
    System.out.println("存在: " + exists);
    
4. 查找 (findFirst / findAny)
  • 获取第一个元素:
    Optional<String> first = names.stream().findFirst();
    first.ifPresent(System.out::println);
    
5. 归约 (reduce)
  • 计算总和:
    int sum = IntStream.range(1, 6)
        .reduce(0, Integer::sum);
    System.out.println("总和: " + sum);
    

示例:完整案例演示

示例:获取学生平均成绩

import java.util.*;
import java.util.stream.*;

public class StreamExample {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Alice", 85),
                new Student("Bob", 90),
                new Student("Charlie", 75)
        );

        double averageScore = students.stream()
                .mapToInt(Student::getScore)
                .average()
                .orElse(0);

        System.out.println("平均成绩: " + averageScore);
    }
}

class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }
}

总结

Java 8 Stream API 提供了强大的数据操作功能,支持链式操作和多种聚合操作。熟练掌握 Stream API,可以大幅提高数据处理的效率和代码的简洁性。

4.Java Optional 类详解与使用示例

Java 8 引入了 Optional 类,旨在减少 NullPointerException 的出现,优雅地处理可能为空的值。它是一个容器类,表示一个可能包含值或为空的对象。


什么是 Optional

Optional<T> 是一个容器对象,它可能包含一个非空值,也可能为空。Optional 提供了多种方法来避免显式的 null 检查,从而减少冗余代码,提高代码的可读性和健壮性。


创建 Optional 实例的方式

1. Optional.of(T value)

  • 创建一个包含非空值的 Optional 对象。
  • 如果传入值为 null,则抛出 NullPointerException
Optional<String> optional = Optional.of("Java");

2. Optional.ofNullable(T value)

  • 创建一个可能包含 nullOptional 对象。
  • 如果传入值为 null,则返回一个空的 Optional
Optional<String> optional = Optional.ofNullable(null);  // 空的 Optional
Optional<String> optional2 = Optional.ofNullable("Hello");  // 非空的 Optional

3. Optional.empty()

  • 创建一个空的 Optional 对象。
Optional<String> emptyOptional = Optional.empty();

常用方法与示例

1. isPresent()ifPresent()

  • isPresent(): 检查值是否存在,返回布尔值。
  • ifPresent(Consumer): 如果值存在,执行传入的操作。
Optional<String> optional = Optional.of("Java");
if(optional.

isPresent()){
        System.out.

println("存在值: "+optional.get());
        }

        optional.

ifPresent(value ->System.out.

println("值存在: "+value));

2. get()

  • 获取 Optional 中的值,如果为空则抛出 NoSuchElementException
Optional<String> optional = Optional.of("Java");
System.out.

println(optional.get());  // 输出: Java

3. orElse(T other)

  • 如果值存在则返回该值,否则返回默认值。
Optional<String> optional = Optional.ofNullable(null);
System.out.

println(optional.orElse("默认值"));  // 输出: 默认值

4. orElseGet(Supplier)

  • 如果值存在则返回该值,否则执行 Supplier 函数生成默认值。
Optional<String> optional = Optional.ofNullable(null);
System.out.

println(optional.orElseGet(() ->"动态生成默认值"));

5. orElseThrow(Supplier)

  • 如果值存在则返回该值,否则抛出由 Supplier 提供的异常。
Optional<String> optional = Optional.ofNullable(null);
optional.

orElseThrow(() ->new

IllegalArgumentException("值不存在"));

6. filter(Predicate)

  • 根据条件过滤 Optional 值,如果不满足条件则返回空的 Optional
Optional<String> optional = Optional.of("Java");
optional.

filter(value ->value.

startsWith("J"))
        .

ifPresent(System.out::println);  // 输出: Java

7. map(Function)

  • Optional 中的值执行转换操作,返回新的 Optional
Optional<String> optional = Optional.of("Java");
Optional<Integer> length = optional.map(String::length);
System.out.

println("长度: "+length.orElse(0));  // 输出: 长度: 4

8. flatMap(Function)

  • map 类似,但要求返回值必须是 Optional
Optional<String> optional = Optional.of("Java");
Optional<String> upper = optional.flatMap(value -> Optional.of(value.toUpperCase()));
System.out.

println(upper.orElse("空值"));  // 输出: JAVA

完整示例:用户信息管理

import java.util.Optional;

public class OptionalExample {
    public static void main(String[] args) {
        User user = new User("Alice", "alice@example.com");

        // 使用 Optional 处理可能为空的字段
        String email = Optional.ofNullable(user)
                .map(User::getEmail)
                .orElse("未提供邮箱");

        System.out.println("用户邮箱: " + email);
    }
}

class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}

何时使用 Optional

  • 适用场景:

    • 方法返回值可能为空。
    • 需要避免 null 检查,增强代码的可读性。
  • 不适用场景:

    • 在类的字段中使用(如实体类中的属性)。
    • 不应滥用,以避免不必要的性能开销。

总结

Optional 是处理可能为空的对象的强大工具,提供了灵活的空值处理方式,避免了繁琐的 null 检查。熟练掌握 Optional 的方法,可以编写更简洁、更健壮的 Java 代码。