java 8 特性
JDK8 新特性
1. Lambda 表达式
Lambda 表达式是 JDK 8 中引入的一项重要特性,它为 Java 引入了函数式编程的概念,使得代码变得更为简洁、灵活。Lambda 表达式主要用于替代匿名内部类的写法,可以将函数作为参数传递给方法,使得代码更加具有表达力。本节将详细解释 Lambda 表达式的语法和使用方法,并提供一些示例来说明其用法。
Lambda 表达式语法
Lambda 表达式的语法比较简洁,主要由以下几个部分组成:
形参列表:Lambda 表达式可以有零个或多个参数,如果没有参数,则使用空括号表示。
箭头符号
->
:箭头符号将形参列表和Lambda 表达式的主体分开。Lambda 表达式的主体:可以是一个表达式或一段代码块。如果是一个表达式,则不需要使用大括号
{}
包裹;如果是一段代码块,则需要使用大括号包裹,并且需要使用return
关键字返回结果。
Lambda 表达式的基本语法如下所示:
(parameters) -> expression
(parameters) -> { statements; }
示例 1:简单的 Lambda 表达式
假设我们有一个接口 MathOperation
,其中定义了一个抽象方法 operate(int a, int b)
,用于执行某种数学操作。我们可以使用 Lambda 表达式来实现该接口的实例,如下所示:
interface MathOperation {
int operate(int a, int b);
}
public class Main {
public static void main(String[] args) {
// Lambda 表达式实现加法操作
MathOperation addition = (a, b) -> a + b;
System.out.println("5 + 3 = " + addition.operate(5, 3));
}
}
示例 2:使用 Lambda 表达式遍历集合
另一个常见的用途是使用 Lambda 表达式遍历集合,如下所示:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// 使用 Lambda 表达式遍历集合
names.forEach(name -> System.out.println(name));
}
}
在这个示例中,我们使用 forEach
方法和 Lambda 表达式来遍历 names
集合,并打印出每个元素的值。
Lambda 表达式的优势
Lambda 表达式的引入使得代码变得更加简洁、易读,特别是在处理集合、事件监听器等场景下更为突出。通过 Lambda 表达式,可以减少冗余的代码,使得程序更加清晰和易于维护。因此,熟练掌握 Lambda 表达式是成为 Java 开发者的一项重要技能。
2. Stream API
Stream API 是 JDK 8 中另一个重要的新特性,它为集合操作提供了一种流式处理的方式。通过 Stream API,可以轻松地进行过滤、映射、排序等操作,而无需编写繁琐的循环结构。
Stream 的主要特性
Stream 是不可变的:每次对 Stream 的操作都会返回一个新的 Stream。
Stream 不存储数据:它们只处理从数据源(如集合、数组等)提供的数据。
Stream 是惰性求值的:它们会延迟到真正需要结果的时候才执行。
Stream 的操作
Stream 操作分为中间操作和终端操作:
中间操作
中间操作返回一个新的 Stream,允许链式调用。这些操作是惰性求值的,仅在终端操作执行时才会实际处理数据。
filter()
: 过滤元素map()
: 映射元素flatMap()
: 扁平化映射distinct()
: 去重sorted()
: 排序peek()
: 对每个元素执行操作并返回一个新的 Stream
终端操作
终端操作触发 Stream 的执行,并生成结果。终端操作之后,Stream 不能再被使用。
forEach()
: 对每个元素执行操作collect()
: 收集结果到集合、数组等count()
: 计算元素数量reduce()
: 规约操作,将元素组合成一个值toArray()
: 将 Stream 转换为数组findFirst()
: 返回第一个元素anyMatch()
,allMatch()
,noneMatch()
: 匹配操作
Stream 的并行处理
Java 8 还引入了并行流,允许利用多核处理器来提高性能。可以使用 parallelStream()
或 parallel()
方法将一个流转换为并行流。
并行处理会出现的问题
1. 线程安全问题
2. 不适合的小任务
3. 顺序依赖
4. 性能不一致
5. 合并操作的复杂性
6. 使用不适合的 Spliterator
7. 并行流的副作用
3. 接口的默认方法
JDK 8 引入了接口的默认方法,使得接口可以包含具有默认实现的方法。这为接口的演化提供了更大的灵活性,可以在不破坏现有实现的情况下向接口中添加新的方法。
4. 函数式接口和新的注解
JDK 8 提供了一些新的函数式接口,如 Function
、Predicate
、Consumer
等,使得在使用 Lambda 表达式时更加方便。此外,还引入了诸如 @FunctionalInterface
、@Repeatable
等新的注解,进一步增强了 Java 语言的表达能力。
@FunctionalInterface
这个注解用于标识一个接口为函数式接口。虽然没有这个注解也可以定义函数式接口,但使用它可以让编译器强制检查该接口是否满足函数式接口的要求(即只有一个抽象方法)。
@Repeatable
@Repeatable
这个注解用于定义可重复注解。在 Java 8 之前,同一个注解不能在同一声明或类型上重复使用。使用 @Repeatable
可以让注解在同一声明或类型上重复使用。
定义可重复注解
首先定义一个容器注解:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedules {
Schedule[] value();
}
然后定义可重复注解,并使用 @Repeatable
指定容器注解:
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Schedules.class)
public @interface Schedule {
String day();
String time();
}
然后定义可重复注解,并使用 @Repeatable
指定容器注解:
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Schedules.class)
public @interface Schedule {
String day();
String time();
}
使用可重复注解
public class RepeatingAnnotations {
@Schedule(day = "Monday", time = "9AM")
@Schedule(day = "Tuesday", time = "10AM")
public void scheduledTask() {
// 方法实现
}
}
通过反射可以访问这些重复注解:
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
try {
Method method = RepeatingAnnotations.class.getMethod("scheduledTask");
// 获取单个 Schedule 注解
Schedule[] schedules = method.getAnnotationsByType(Schedule.class);
for (Schedule schedule : schedules) {
System.out.println(schedule.day() + " at " + schedule.time());
}
// 获取容器注解
Schedules container = method.getAnnotation(Schedules.class);
if (container != null) {
for (Schedule schedule : container.value()) {
System.out.println(schedule.day() + " at " + schedule.time());
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
5. 新的日期和时间 API
JDK 8 引入了全新的日期和时间 API,以取代旧的 Date
和 Calendar
类。新的 API 设计更加清晰、易用,并提供了丰富的操作方法,使得日期和时间的处理变得更加简单和安全。
6. Optional 类
Optional 是一个容器类,表示一个值可能存在也可能不存在。
用于避免 NullPointerException。
语法示例:
Optional<String> optional = Optional.ofNullable(value);