0%

浅谈 Java 8

Java 8 是 Java 发展历程中的一个里程碑式版本,它引入了大量令人激动的新特性,极大地提升了开发效率。

Java 8 核心新特性

Java 8 最重要的特性主要围绕着 函数式编程更高效的 API 设计。

1. Lambda 表达式 (Lambda Expressions)

这是 Java 8 最核心的特性之一,它允许你以更简洁的方式表示只有一个抽象方法的接口(即 函数式接口)的实例。

  • 语法: (parameters) -> expression(parameters) -> { statements; }

  • 作用: 极大地简化了匿名内部类的使用,使得代码更加紧凑和易读,特别是在处理集合和事件时。

1
2
3
4
5
6
7
8
9
//Java 7 或之前
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from a thread!");
}
}).start();
//Java 8 Lambda
new Thread(() -> System.out.println("Hello from a thread!")).start();

2. 函数式接口 (Functional Interfaces)

Lambda 表达式的出现,使得 Java 需要一种新的方式来表示可以作为参数传递的代码块。函数式接口就是只有一个抽象方法的接口,它们是 Lambda 表达式的类型。

  • @FunctionalInterface 注解: 用于强制编译器检查该接口是否符合函数式接口的定义,如果不是,则会报错。

  • Java 8 内置的函数式接口:

    • Consumer<T>: 接收一个输入参数但不返回任何结果。
    • Predicate<T>: 接收一个输入参数,返回一个布尔值。
    • Function<T, R>: 接收一个输入参数,返回一个结果。
    • Supplier<T>: 不接收任何参数,返回一个结果。
    • UnaryOperator<T>: 接收一个输入参数,返回一个相同类型的结果(Function<T, T> 的特化)。
    • BinaryOperator<T>: 接收两个相同类型的输入参数,返回一个相同类型的结果(BiFunction<T, T, T> 的特化)。

3. Stream API

Stream API 是 Java 8 中处理集合(以及数组、I/O 渠道等)数据的新方式,它提供了一种声明式、函数式的数据处理能力。Stream API 可以将复杂的集合操作(如过滤、映射、查找、排序等)连接起来形成一个管道,从而实现高效的链式操作。

  • 特性:
    • 非破坏性: Stream 不会改变源数据结构。
    • 惰性执行: 只有当终端操作被调用时,中间操作才会执行。
    • 内部迭代: Stream API 在内部处理迭代,你无需编写显式的 for 循环。
    • 支持并行处理: 可以轻松地将顺序流转换为并行流,从而利用多核 CPU 优势。
  • 操作分类:
    • 中间操作 (Intermediate Operations): 返回另一个 Stream,可以链式调用。例如 filter(), map(), sorted(), distinct()
    • 终端操作 (Terminal Operations): 产生一个非 Stream 的结果(如集合、值、副作用),标志着 Stream 操作的结束。例如 forEach(), collect(), reduce(), count(), min(), max(), findFirst()
1
2
3
4
5
6
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int sumOfDoubledEvens = numbers.stream()
.filter(n -> n % 2 == 0) // 中间操作:过滤偶数
.map(n -> n * 2) // 中间操作:加倍
.reduce(0, Integer::sum); // 终端操作:求和
System.out.println(sumOfDoubledEvens); // 输出 24 (2*2 + 4*2 + 6*2 = 4 + 8 + 12 = 24)

4. 默认方法 (Default Methods) 或 接口的扩展方法 (Extension Methods)

Java 8 允许在接口中定义带有方法体的方法,用 default 关键字修饰。这解决了接口演进的问题,在不破坏现有实现类的前提下,向接口添加新的方法。避免了“打破所有现有实现”的问题,允许向已发布的接口添加新功能,同时保持向后兼容性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface MyInterface {
void abstractMethod();

default void defaultMethod() {
System.out.println("This is a default method.");
}
}

class MyClass implements MyInterface {
@Override
public void abstractMethod() {
System.out.println("Implementing abstract method.");
}
}

// MyClass 实例可以直接调用 defaultMethod() 而无需实现
MyClass obj = new MyClass();
obj.defaultMethod(); // 输出 "This is a default method."

5. Optional 类

java.util.Optional<T> 是一个容器对象,可能包含也可能不包含非 null 值。它被设计用来消除代码中的 NullPointerException,并提升代码的健壮性和可读性。

  • 作用: 鼓励开发者显式地处理值可能不存在的情况,而不是依赖于 null 检查。

  • 常用方法:

    • Optional.of(value): 创建一个包含非空值的 Optional。
    • Optional.ofNullable(value): 如果值非空则创建 Optional,否则创建空的 Optional。
    • isPresent(): 判断 Optional 是否包含值。
    • ifPresent(Consumer): 如果值存在,则执行给定的 Consumer 操作。
    • get(): 获取值,如果 Optional 为空则抛出 NoSuchElementException (不推荐直接使用)。
    • orElse(other): 如果值存在则返回,否则返回提供的默认值。
    • orElseGet(Supplier): 如果值存在则返回,否则调用 Supplier 获取默认值。
    • orElseThrow(Supplier): 如果值存在则返回,否则抛出由 Supplier 创建的异常。
1
2
3
4
5
6
7
8
9
10
11
String name = null;
Optional<String> optionalName = Optional.ofNullable(name);

// 传统方式:
// if (name != null) { System.out.println(name.toUpperCase()); }

// 使用 Optional:
optionalName.ifPresent(n -> System.out.println(n.toUpperCase())); // 不会执行

String result = optionalName.orElse("Default Name");
System.out.println(result); // 输出 "Default Name"

6. 日期与时间 API

Java 8 引入了全新的、现代化的日期和时间 API ( java.time 包),解决了旧 java.util.Datejava.util.Calendar API 中存在的线程安全、设计缺陷、时区处理复杂等问题。

  • 核心类:

    • LocalDate: 不带时间的日期,例如 2023-10-26。
    • LocalTime: 不带日期的时分秒,例如 10:30:45。
    • LocalDateTime: 结合了日期和时间,不带时区。
    • ZonedDateTime: 带有时区的日期和时间。
    • Instant: 时间线上的一个瞬时点(UTC 时间)。
    • Duration: 表示时间量(秒和纳秒)。
    • Period: 表示日历时间量(年、月、日)。
    • DateTimeFormatter: 用于日期和时间的格式化与解析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LocalDate today = LocalDate.now();
System.out.println("今天的日期: " + today);

LocalTime now = LocalTime.now();
System.out.println("现在的时间: " + now.withNano(0)); // 去掉纳秒

LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("当前日期时间: " + currentDateTime);

LocalDate specificDate = LocalDate.of(2025, 1, 1);
System.out.println("特定日期: " + specificDate);

// 计算两个日期之间的天数
long daysBetween = ChronoUnit.DAYS.between(LocalDate.of(2024, 1, 1), today);
System.out.println("距离2024年1月1日的天数: " + daysBetween);

参考

https://github.com/winterbe/java8-tutorial