目录
8.1 重复注解(Repeated Annotations)
Java 8是一个重要的版本,引入了许多新特性和改进,下面是一些显著的新特性和改进的介绍。
一、Lambda 表达式
Lambda表达式是Java 8引入的一项重要特性,它提供了一种简洁、便利的语法来实现函数式编程。Lambda表达式可以看作是一个匿名函数,它允许将函数作为参数传递给方法,或者在集合操作中以声明性的方式进行处理。
1.1 Lambda表达式的形式
Lambda表达式的一般形式如下:
(parameters) -> expression
或者
(parameters) -> { statements; }
1.2 Lambda表达式的组成部分
Lambda表达式由以下几个部分组成:
- 参数列表:Lambda表达式可以接受零个或多个参数。
- 箭头符号(->):箭头符号将参数列表和Lambda表达式的主体分隔开。
- Lambda主体:可以是一个表达式或者一段代码块。如果是一个表达式,则不需要使用大括号,如果是一段代码块,则需要使用大括号。
1.3 Lambda表达式示例
- Lambda表达式作为函数式接口的实例:
// 定义一个函数式接口
interface MyInterface {
void myMethod(String s);
}
public class Main {
public static void main(String[] args) {
// 使用Lambda表达式实现函数式接口
MyInterface myInterface = (String s) -> {
System.out.println("Hello, " + s);
};
// 调用方法
myInterface.myMethod("world");
}
}
- Lambda表达式作为参数传递给方法:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");
// 使用Lambda表达式遍历集合
list.forEach(item -> System.out.println(item));
}
}
- Lambda表达式简化Comparator的使用:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "orange");
// 使用Lambda表达式排序集合
Collections.sort(list, (String s1, String s2) -> s1.compareTo(s2));
// 输出排序后的集合
list.forEach(item -> System.out.println(item));
}
}
二、Stream API
Stream API 是 Java 8 中引入的一个重要特性,它提供了一种便利和高效的方式来处理集合数据。Stream API 允许以声明性的方式对集合进行各种操作,如过滤、映射、排序、聚合等,而不需要显式地编写循环和条件语句。
2.1 Stream API特点
- 流式操作:Stream API 使用流式操作,允许对数据进行流式处理,逐个处理每个元素而不是一次性处理整个集合。
- 链式调用:Stream API 中的操作可以进行链式调用,使得代码更加简洁和易读。
- 惰性求值:Stream API 中的操作通常是惰性求值的,只有在终端操作被调用时才会触发中间操作的执行,这有助于提高性能。
- 支持并行:Stream API 支持并行操作,可以利用多核处理器进行并行计算,提高程序的性能。
2.2 Stream API 操作类型
Stream API 中的操作可以分为两种类型:中间操作和终端操作。
- 中间操作用于对流中的元素进行处理和转换,如过滤、映射、排序等。
- 终端操作用于触发流的执行,并产生最终的结果,如收集、计数、查找等。
2.3 Stream API 使用示例
- 过滤和映射:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "orange", "grape", "watermelon");
// 过滤长度大于5的字符串
List<String> filteredList = list.stream()
.filter(str -> str.length() > 5)
.collect(Collectors.toList());
System.out.println("Filtered list: " + filteredList);
// 映射转换大写并排序
List<String> upperCaseList = list.stream()
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println("Uppercase and sorted list: " + upperCaseList);
}
}
- 计数和查找:
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "orange", "grape", "watermelon");
// 计算集合中长度为5的字符串数量
long count = list.stream()
.filter(str -> str.length() == 5)
.count();
System.out.println("Number of strings with length 5: " + count);
// 查找第一个以字母"g"开头的字符串
String foundString = list.stream()
.filter(str -> str.startsWith("g"))
.findFirst()
.orElse(null);
System.out.println("First string starts with 'g': " + foundString);
}
}
三、接口的默认方法和静态方法
在Java 8中,接口(Interface)引入了默认方法(Default Method)和静态方法(Static Method)这两个新特性,使得接口具有了更多的灵活性和功能性。这两种方法的介绍如下:
3.1 默认方法(Default Method)
- 默认方法是在接口中提供的具有默认实现的方法。它允许在不破坏现有实现的情况下,向接口添加新的方法。
- 默认方法的声明使用 default 关键字,后面跟着方法的实现。默认方法可以被实现类直接继承或者重写,也可以被实现类选择性地覆盖。
- 默认方法的存在使得接口的修改变得更加容易,可以向现有的接口添加新的功能,而不需要修改所有实现该接口的类。
- 默认方法的典型应用场景包括扩展接口的功能、提供接口的默认行为等。
示例代码如下:
interface MyInterface {
default void defaultMethod() {
System.out.println("This is a default method.");
}
}
3.2 静态方法(Static Method)
- 静态方法是在接口中提供的静态方法实现,它可以直接通过接口名来调用,无需实现类的实例。
- 静态方法的声明使用 static 关键字,后面跟着方法的实现。静态方法不能被实现类覆盖或重写。
- 静态方法的存在使得接口可以提供一些工具方法或者辅助方法,而不需要为此创建实例。
- 静态方法的典型应用场景包括提供工具方法、提供工厂方法等。
示例代码如下:
interface MyInterface {
static void staticMethod() {
System.out.println("This is a static method.");
}
}
四、函数式接口
函数式接口是Java 8引入的一个重要概念,它是一个只包含一个抽象方法的接口。函数式接口可以使用Lambda表达式来实例化,从而实现函数式编程的特性。Java 8为函数式接口引入了一个新的注解 @FunctionalInterface,用于标识函数式接口。
4.1 函数式接口特点
函数式接口的主要特点包括:
- 只包含一个抽象方法:函数式接口只能包含一个抽象方法,但可以包含多个默认方法或静态方法。
- Lambda表达式实例化:函数式接口可以通过Lambda表达式来实例化,从而实现函数作为参数传递、函数式编程等功能。
- 简化代码:函数式接口使得代码更加简洁和可读,特别是在处理回调函数、事件处理、集合操作等场景下。
4.2 函数式接口的示例
下面是一个简单的函数式接口的示例:
@FunctionalInterface
interface MyInterface {
void myMethod();
}
在上面的示例中,MyInterface 是一个函数式接口,它只包含一个抽象方法 myMethod()。使用 @FunctionalInterface 注解标识了该接口是一个函数式接口。
接下来是如何使用Lambda表达式来实例化函数式接口的示例:
public class Main {
public static void main(String[] args) {
// 使用Lambda表达式实例化函数式接口
MyInterface myInterface = () -> System.out.println("Hello, world!");
// 调用函数式接口的抽象方法
myInterface.myMethod();
}
}
在上面的示例中,通过Lambda表达式实例化了 MyInterface 函数式接口,并在 myMethod() 方法中输出了 "Hello, world!"。
五、Optional 类
Optional 类是 Java 8 引入的一个重要类,它的目的是解决空指针异常(NullPointerException)问题。Optional 类可以用来包装一个可能为 null 的值,从而避免直接操作 null 值而导致的空指针异常。通过 Optional 类,我们可以更加优雅和安全地处理可能为 null 的值。
5.1 Optional类特点和用法
① 避免空指针异常
使用 Optional 类可以帮助我们更加安全地处理可能为 null 的值,避免出现空指针异常。通过 Optional 类,我们可以对可能为 null 的值进行包装和处理,从而使得代码更加健壮和可靠。
② 方法链操作
Optional 类提供了丰富的方法来进行操作,如 map、flatMap、filter、orElse、orElseGet 等。这些方法可以通过方法链的方式来组合使用,从而实现对包装值的各种操作和转换。
③ 提倡使用 Optional 类
Java 8 开始,官方提倡在返回值可能为 null 的情况下,使用 Optional 类作为方法的返回值类型,以便提醒调用者可能会得到空值,并且鼓励调用者使用 Optional 类来处理返回值。
④ 不鼓励在字段上使用
虽然 Optional 类对于方法的返回值是非常适用的,但是对于字段(成员变量)的使用却不太合适。官方不推荐在字段上直接使用 Optional 类,因为这样可能会增加额外的复杂性。
5.2 Optional类使用示例
import java.util.Optional;
public class Main {
public static void main(String[] args) {
// 创建一个包装值为空的Optional对象
Optional<String> emptyOptional = Optional.empty();
// 创建一个包装非空值的Optional对象
Optional<String> nonEmptyOptional = Optional.of("Hello, world!");
// 创建一个可能为null的Optional对象
String str = null;
Optional<String> nullableOptional = Optional.ofNullable(str);
// 检查Optional对象是否包含值
System.out.println("Is empty optional? " + emptyOptional.isEmpty()); // true
System.out.println("Is non-empty optional? " + nonEmptyOptional.isPresent()); // true
System.out.println("Is nullable optional? " + nullableOptional.isPresent()); // false
// 获取Optional对象中的值
String value = nonEmptyOptional.get();
System.out.println("Value: " + value); // "Hello, world!"
// 使用orElse方法获取值,如果值为空则返回默认值
String defaultValue = nullableOptional.orElse("Default Value");
System.out.println("Default value: " + defaultValue); // "Default Value"
}
}
通过 Optional 类,可以更加安全和优雅地处理可能为 null 的值,避免了直接操作 null 值而导致的空指针异常。这使得代码更加健壮和可靠,提高了程序的稳定性和可维护性。
六、新的日期和时间 API
Java 8 引入了全新的日期和时间 API(java.time包),用于取代旧的 Date 和 Calendar 类,提供了更加简洁、易用和功能强大的日期和时间处理功能。
6.1 新的日期和时间 API 核心类
- LocalDate:用于表示日期,不包含时间和时区信息。
- LocalTime:用于表示时间,不包含日期和时区信息。
- LocalDateTime:用于表示日期和时间,不包含时区信息。
- Instant:表示时间戳,与 Unix 时间类似,精确到纳秒。
- Duration:表示两个时间点之间的时间间隔。
- Period:表示两个日期之间的时间间隔,以年、月、日为单位。
- ZoneId:用于表示时区。
- ZonedDateTime:带有时区的日期和时间。
6.2 新的日期和时间 API 特点和优势
- 不可变性:日期和时间类都是不可变的(immutable),保证了线程安全性。
- 清晰易用:提供了丰富的方法来进行日期和时间的操作和计算,代码更加清晰易读。
- 丰富的功能:提供了诸如日期加减、格式化、解析、时区转换等丰富的功能,适用于各种日期和时间处理场景。
- 线程安全:新的日期和时间 API 是线程安全的,可以放心在多线程环境中使用。
6.3 新的日期和时间 API 示例
下面是一些使用新的日期和时间 API 的示例:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
// 获取当前日期
LocalDate currentDate = LocalDate.now();
System.out.println("Current Date: " + currentDate);
// 获取当前时间
LocalTime currentTime = LocalTime.now();
System.out.println("Current Time: " + currentTime);
// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current Date and Time: " + currentDateTime);
// 根据指定日期和时间创建对象
LocalDate date = LocalDate.of(2022, Month.FEBRUARY, 14);
LocalTime time = LocalTime.of(12, 30, 0);
LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println("Custom Date and Time: " + dateTime);
// 格式化日期和时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = currentDateTime.format(formatter);
System.out.println("Formatted Date and Time: " + formattedDateTime);
}
}
新的日期和时间 API 提供了更加现代化和强大的日期和时间处理功能,使得日期和时间的操作更加简单、直观和安全。它是 Java 8 中一个重要的新增特性,也是 Java 编程中不可或缺的一部分。
七、CompletableFuture 类
CompletableFuture 类是 Java 8 引入的一个重要的异步编程工具,用于支持异步任务的执行和结果处理。它提供了一种简单、灵活和强大的方式来执行异步任务,并支持依赖关系、异常处理、回调函数等功能。CompletableFuture 类可以用于编写高效的并发程序,提高程序的性能和响应性。
7.1 CompletableFuture 特点和用法
CompletableFuture 类的主要特点和用法包括:
① 异步执行
CompletableFuture 类支持异步执行任务,可以通过 supplyAsync()、runAsync() 等静态方法来创建一个异步任务,并返回一个 CompletableFuture 对象,该对象表示了任务的执行状态和结果。
② 依赖关系
CompletableFuture 类支持任务之间的依赖关系,可以通过 thenApply()、thenAccept()、thenCompose() 等方法来构建任务之间的依赖关系,实现任务的串行或并行执行。
③ 异常处理
CompletableFuture 类提供了丰富的方法来处理异步任务的异常情况,如 exceptionally()、handle() 等方法可以处理任务执行过程中抛出的异常,并返回默认值或执行备选操作。
④ 回调函数
CompletableFuture 类支持回调函数的方式来处理异步任务的结果,如 thenApply()、thenAccept()、thenRun() 等方法可以在任务执行完成后自动调用指定的回调函数。
⑤ 组合操作
CompletableFuture 类支持各种组合操作,如 allOf()、anyOf() 等静态方法可以组合多个 CompletableFuture 对象,实现一组任务的并行执行或等待任意一个任务的完成。
⑥ 支持并行
CompletableFuture 类可以利用多核处理器进行并行计算,提高程序的性能和响应性,从而加速任务的执行和结果的获取。
7.2 CompletableFuture 类使用示例
下面是一个简单的示例,演示了如何使用 CompletableFuture 类来执行异步任务和处理任务的结果:
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
// 异步执行任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Task Result";
});
// 处理任务的结果
future.thenAccept(result -> {
System.out.println("Task Result: " + result);
});
// 主线程等待异步任务完成
try {
future.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过 CompletableFuture 类,可以更加灵活和高效地执行异步任务,并实现对任务结果的处理和管理。可以编写高响应性和高性能的并发程序,提高了程序的可扩展性和并发能力。
八、新的重复注解和类型注解
Java 8 引入了新的重复注解和类型注解,这两个特性为Java的注解提供了更多的灵活性和功能性。
8.1 重复注解(Repeated Annotations)
重复注解允许在同一个元素上多次使用相同的注解,而不需要使用容器注解的方式。在 Java 8 之前,如果要在同一个元素上使用多个相同的注解,必须使用容器注解(Container Annotation),将多个注解包装在一个容器注解中。而在 Java 8 中,可以直接使用相同的注解多次注解同一个元素。
示例代码如下:
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotations {
MyAnnotation[] value();
}
在上面的示例中,MyAnnotation 注解使用 @Repeatable 注解声明可重复,然后通过 MyAnnotations 注解来容纳多个 MyAnnotation 注解。
8.2 类型注解(Type Annotations)
类型注解允许在任何类型使用的地方使用注解,例如类、接口、字段、方法、参数、局部变量等。在 Java 8 之前,注解只能应用于声明的语法结构上,而类型注解允许在任何类型使用的地方使用注解,从而提供了更加精细的注解控制。
示例代码如下:
import java.lang.annotation.*;
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTypeAnnotation {}
class MyClass<@MyTypeAnnotation T> {
public void method(@MyTypeAnnotation String str) {
@MyTypeAnnotation String localVar = "local";
}
}
在上面的示例中,@MyTypeAnnotation 注解被应用于类型参数 T、方法参数 str、局部变量 localVar,从而实现了对这些类型的注解控制。
重复注解和类型注解为Java的注解机制增加了更多的灵活性和功能性,使得注解在代码中的应用更加丰富和广泛。它们为开发者提供了更多的选择,能够更好地满足不同场景下的需求。