Java 8 新特性详解
Java 8 是 Java 语言的一个重要版本,于 2014 年 3 月发布。它引入了许多新特性,这些特性不仅改变了 Java 代码的编写方式,也解决了之前版本中存在的一些问题。本文将详细介绍 Java 8 的主要新特性,包括它们的概念、解决的问题、代码示例以及应用场景。
一、Lambda 表达式
概念
Lambda 表达式是一种匿名函数,它允许我们将函数作为参数传递给方法,或者将代码作为数据处理。Lambda 表达式的语法简洁明了,使代码更加紧凑。
解决的问题
- 代码冗余:之前的匿名内部类代码冗长,Lambda 表达式提供了更简洁的语法。
- 函数式编程:Java 8 之前,Java 是一种面向对象的语言,不支持函数式编程。Lambda 表达式引入了函数式编程的思想。
- 可读性差:匿名内部类的代码可读性差,Lambda 表达式使代码更加清晰。
语法
1 2 3 4 5
| (parameters) -> expression
(parameters) -> { statements; }
|
代码示例
基本用法
1 2 3 4 5 6 7 8 9 10 11
| Runnable runnable = () -> System.out.println("Hello Lambda!"); runnable.run();
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); names.sort((s1, s2) -> s1.compareTo(s2)); System.out.println(names);
names.forEach(name -> System.out.println(name));
|
类型推断
1 2 3 4 5 6 7
| BinaryOperator<Integer> add = (a, b) -> a + b; System.out.println(add.apply(1, 2));
BinaryOperator<Integer> subtract = (Integer a, Integer b) -> a - b; System.out.println(subtract.apply(5, 3));
|
多行 Lambda 表达式
1 2 3 4 5 6
| BinaryOperator<Integer> multiply = (a, b) -> { System.out.println("Multiplying " + a + " and " + b); return a * b; }; System.out.println(multiply.apply(3, 4));
|
应用场景
- 函数式接口:实现只包含一个抽象方法的接口,如 Runnable、Comparator 等。
- 集合操作:使用 forEach、sort 等方法操作集合。
- 事件处理:处理 GUI 事件或其他回调函数。
- Stream API:与 Stream API 结合使用,进行数据处理。
二、Stream API
概念
Stream API 是 Java 8 引入的一个新的抽象,它允许我们以声明式的方式处理数据集合。Stream API 提供了丰富的操作,如过滤、映射、排序、聚合等。
解决的问题
- 代码冗余:之前的集合处理代码冗长,Stream API 提供了更简洁的语法。
- 并行处理:Stream API 内置了并行处理能力,简化了并行代码的编写。
- 可读性差:传统的集合处理代码可读性差,Stream API 使代码更加清晰。
- 性能优化:Stream API 可以自动优化处理过程,提高性能。
基本操作
- 中间操作:返回一个新的 Stream,如 filter、map、sorted 等。
- 终端操作:返回一个结果或副作用,如 forEach、collect、reduce 等。
代码示例
基本操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
List<String> result = names.stream() .filter(name -> name.length() > 3) .map(String::toUpperCase) .collect(Collectors.toList()); System.out.println(result);
int totalLength = names.stream() .mapToInt(String::length) .sum(); System.out.println(totalLength);
Optional<String> first = names.stream() .filter(name -> name.length() > 3) .findFirst(); System.out.println(first.orElse("Not found"));
|
并行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| List<Integer> numbers = IntStream.rangeClosed(1, 1000000) .boxed() .collect(Collectors.toList());
long start = System.currentTimeMillis(); int sum1 = numbers.stream() .filter(n -> n % 2 == 0) .mapToInt(Integer::intValue) .sum(); long end = System.currentTimeMillis(); System.out.println("串行处理结果: " + sum1 + ", 用时: " + (end - start) + "ms");
start = System.currentTimeMillis(); int sum2 = numbers.parallelStream() .filter(n -> n % 2 == 0) .mapToInt(Integer::intValue) .sum(); end = System.currentTimeMillis(); System.out.println("并行处理结果: " + sum2 + ", 用时: " + (end - start) + "ms");
|
高级操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Map<Integer, List<String>> groupByLength = names.stream() .collect(Collectors.groupingBy(String::length)); System.out.println(groupByLength);
Map<Boolean, List<String>> partitionByLength = names.stream() .collect(Collectors.partitioningBy(name -> name.length() > 3)); System.out.println(partitionByLength);
Optional<String> concatenated = names.stream() .reduce((s1, s2) -> s1 + ", " + s2); System.out.println(concatenated.orElse(""));
|
应用场景
- 数据过滤和转换:过滤集合中的元素,转换元素类型。
- 数据聚合:计算总和、平均值、最大值、最小值等。
- 数据分组和分区:根据条件对数据进行分组或分区。
- 并行处理:处理大型数据集,提高性能。
- 链式操作:将多个操作链接在一起,形成一个处理管道。
三、Optional 类
概念
Optional 类是 Java 8 引入的一个容器类,它可以包含一个非空值,或者表示一个空值。Optional 类的目的是解决空指针异常(NullPointerException)的问题。
解决的问题
- 空指针异常:Java 中最常见的异常之一,Optional 类提供了一种优雅的方式来处理可能为 null 的值。
- 代码可读性:Optional 类的方法使代码更加清晰,表达了值可能不存在的意图。
- 防御性编程:Optional 类鼓励程序员编写更加健壮的代码,避免空指针异常。
主要方法
| 方法签名 |
描述 |
| of(T value) |
创建一个包含非空值的 Optional 对象 |
| ofNullable(T value) |
创建一个可能包含 null 值的 Optional 对象 |
| empty() |
创建一个空的 Optional 对象 |
| isPresent() |
检查 Optional 对象是否包含值 |
| ifPresent(Consumer<? super T> consumer) |
如果 Optional 对象包含值,则执行消费者操作 |
| get() |
获取 Optional 对象中的值,如果值为 null,则抛出异常 |
| orElse(T other) |
获取 Optional 对象中的值,如果值为 null,则返回默认值 |
| orElseGet(Supplier<? extends T> other) |
获取 Optional 对象中的值,如果值为 null,则通过供应商函数获取默认值 |
| orElseThrow(Supplier<? extends X> exceptionSupplier) |
获取 Optional 对象中的值,如果值为 null,则抛出供应商函数提供的异常 |
| map(Function<? super T, ? extends U> mapper) |
如果 Optional 对象包含值,则对值应用映射函数 |
| flatMap(Function<? super T, Optional> mapper) |
如果 Optional 对象包含值,则对值应用映射函数,返回映射后的 Optional 对象 |
| filter(Predicate<? super T> predicate) |
如果 Optional 对象包含值,并且值满足谓词条件,则返回该 Optional 对象,否则返回空的 Optional 对象 |
代码示例
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| Optional<String> optional1 = Optional.of("Hello"); Optional<String> optional2 = Optional.ofNullable(null); Optional<String> optional3 = Optional.empty();
System.out.println(optional1.isPresent()); System.out.println(optional2.isPresent()); System.out.println(optional3.isPresent());
System.out.println(optional1.get());
System.out.println(optional1.orElse("Default")); System.out.println(optional2.orElse("Default"));
System.out.println(optional1.orElseGet(() -> "Default")); System.out.println(optional2.orElseGet(() -> "Default"));
System.out.println(optional1.orElseThrow(() -> new IllegalArgumentException("Value is null")));
|
函数式操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Optional<String> optional = Optional.of("Hello");
Optional<Integer> lengthOptional = optional.map(String::length); System.out.println(lengthOptional.orElse(0));
Optional<String> flatMapped = optional.flatMap(s -> Optional.of(s.toUpperCase())); System.out.println(flatMapped.orElse(""));
Optional<String> filtered = optional.filter(s -> s.length() > 3); System.out.println(filtered.orElse("Not found"));
Optional<String> filtered2 = optional.filter(s -> s.length() > 10); System.out.println(filtered2.orElse("Not found"));
|
实际应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public String getUserName(User user) { if (user != null) { return user.getName(); } return "Unknown"; }
public String getUserNameWithOptional(User user) { return Optional.ofNullable(user) .map(User::getName) .orElse("Unknown"); }
public String getAddressCity(User user) { return Optional.ofNullable(user) .map(User::getAddress) .map(Address::getCity) .orElse("Unknown City"); }
|
应用场景
- 避免空指针异常:处理可能为 null 的值,避免空指针异常。
- 方法返回值:当方法可能返回 null 时,使用 Optional 作为返回类型,明确表达值可能不存在的意图。
- 链式调用:通过 Optional 的 map 和 flatMap 方法,实现安全的链式调用。
- 配置值处理:处理可能不存在的配置值,提供默认值。
- API 设计:设计更加健壮的 API,避免 null 返回值。
四、默认方法
概念
默认方法是 Java 8 引入的一个新特性,它允许在接口中定义带有默认实现的方法。默认方法的目的是为了向后兼容,使得接口可以在不破坏现有实现的情况下添加新方法。
解决的问题
- 向后兼容:在 Java 8 之前,接口不能添加新方法,否则会破坏所有现有实现。默认方法解决了这个问题。
- 代码复用:默认方法允许在接口中提供方法实现,实现了接口级别的代码复用。
- 函数式接口:默认方法使得接口可以包含多个方法,同时仍然保持函数式接口的特性(只需要一个抽象方法)。
语法
1 2 3 4 5 6 7 8 9
| interface MyInterface { void abstractMethod(); default void defaultMethod() { System.out.println("Default method implementation"); } }
|
代码示例
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| interface Vehicle { void start(); default void honk() { System.out.println("Beep beep!"); } }
class Car implements Vehicle { @Override public void start() { System.out.println("Car started"); } @Override public void honk() { System.out.println("Car honks: Beep beep!"); } }
class Bicycle implements Vehicle { @Override public void start() { System.out.println("Bicycle started"); } }
public class Main { public static void main(String[] args) { Vehicle car = new Car(); car.start(); car.honk(); Vehicle bicycle = new Bicycle(); bicycle.start(); bicycle.honk(); } }
|
接口冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| interface A { default void method() { System.out.println("A.method()"); } }
interface B { default void method() { System.out.println("B.method()"); } }
class C implements A, B { @Override public void method() { A.super.method(); System.out.println("C.method()"); } }
public class Main { public static void main(String[] args) { C c = new C(); c.method(); } }
|
实际应用
1 2 3 4 5 6 7 8 9 10 11 12 13
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
names.removeIf(name -> name.length() > 3); System.out.println(names);
names.replaceAll(String::toUpperCase); System.out.println(names);
|
应用场景
- 接口扩展:在不破坏现有实现的情况下,为接口添加新方法。
- 代码复用:在接口中提供通用的方法实现,实现接口级别的代码复用。
- 函数式接口:创建包含多个默认方法的函数式接口,只需要一个抽象方法。
- 向后兼容:为老接口添加新功能,同时保持向后兼容。
五、方法引用
概念
方法引用是 Java 8 引入的一个新特性,它允许我们直接引用已有的方法,而不需要创建 Lambda 表达式。方法引用的语法简洁明了,使代码更加紧凑。
解决的问题
- 代码冗余:当 Lambda 表达式只是调用一个已有的方法时,方法引用提供了更简洁的语法。
- 可读性差:方法引用使代码更加清晰,直接表达了要调用的方法。
类型
- 静态方法引用:
ClassName::staticMethod
- 实例方法引用:
instance::instanceMethod
- 类的实例方法引用:
ClassName::instanceMethod
- 构造方法引用:
ClassName::new
代码示例
静态方法引用
1 2 3 4 5 6 7 8 9 10 11 12
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
names.forEach(System.out::println);
Function<Integer, String> converter = String::valueOf; System.out.println(converter.apply(42));
|
实例方法引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
StringFormatter formatter = new StringFormatter();
names.forEach(name -> formatter.format(name));
names.forEach(formatter::format);
class StringFormatter { public void format(String str) { System.out.println("Formatted: " + str); } }
|
类的实例方法引用
1 2 3 4 5 6 7 8 9
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.sort((s1, s2) -> s1.compareTo(s2));
names.sort(String::compareTo); System.out.println(names);
|
构造方法引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<User> users = names.stream() .map(name -> new User(name)) .collect(Collectors.toList());
List<User> users2 = names.stream() .map(User::new) .collect(Collectors.toList());
class User { private String name; public User(String name) { this.name = name; } @Override public String toString() { return "User{name='" + name + "'}"; } }
|
应用场景
- 简化 Lambda 表达式:当 Lambda 表达式只是调用一个已有的方法时,使用方法引用使代码更加简洁。
- 提高可读性:方法引用直接表达了要调用的方法,使代码更加清晰。
- 与 Stream API 结合:在 Stream API 的操作中,使用方法引用可以使代码更加紧凑。
六、日期时间 API
概念
日期时间 API 是 Java 8 引入的一个新的日期时间处理库,它位于 java.time 包中。新的日期时间 API 解决了旧的 java.util.Date 和 java.util.Calendar 类存在的问题,提供了更加清晰、更加易用的日期时间处理功能。
解决的问题
- 可变性:旧的日期时间类是可变的,容易导致并发问题。新的日期时间类是不可变的,更加安全。
- 设计混乱:旧的日期时间类设计混乱,
Date 类同时包含日期和时间,Calendar 类用于日期时间的计算。新的日期时间 API 有明确的职责划分。
- 时区处理:旧的日期时间类时区处理复杂。新的日期时间 API 提供了更好的时区支持。
- 格式化和解析:旧的日期时间类的格式化和解析功能有限。新的日期时间 API 提供了更强大的格式化和解析功能。
主要类
- LocalDate:表示日期,不包含时间和时区。
- LocalTime:表示时间,不包含日期和时区。
- LocalDateTime:表示日期和时间,不包含时区。
- ZonedDateTime:表示日期、时间和时区。
- Instant:表示时间戳,从 UTC 1970-01-01 00:00:00 开始的秒数。
- Duration:表示时间间隔。
- Period:表示日期间隔。
- DateTimeFormatter:用于日期时间的格式化和解析。
代码示例
基本操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| LocalDate today = LocalDate.now(); System.out.println("Today: " + today);
LocalTime now = LocalTime.now(); System.out.println("Now: " + now);
LocalDateTime current = LocalDateTime.now(); System.out.println("Current: " + current);
Instant instant = Instant.now(); System.out.println("Instant: " + instant);
LocalDate date = LocalDate.of(2026, 1, 23); System.out.println("Date: " + date);
LocalTime time = LocalTime.of(10, 30, 45); System.out.println("Time: " + time);
LocalDateTime dateTime = LocalDateTime.of(2026, 1, 23, 10, 30, 45); System.out.println("DateTime: " + dateTime);
|
日期时间计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| LocalDate today = LocalDate.now(); LocalDate tomorrow = today.plusDays(1); System.out.println("Tomorrow: " + tomorrow);
LocalDate nextWeek = today.plusWeeks(1); System.out.println("Next week: " + nextWeek);
LocalDate nextMonth = today.plusMonths(1); System.out.println("Next month: " + nextMonth);
LocalTime now = LocalTime.now(); LocalTime later = now.plusHours(1).plusMinutes(30); System.out.println("Later: " + later);
LocalDateTime current = LocalDateTime.now(); LocalDateTime future = current.plusDays(1).plusHours(2); System.out.println("Future: " + future);
Duration duration = Duration.between(LocalTime.of(10, 0), LocalTime.of(12, 30)); System.out.println("Duration in hours: " + duration.toHours()); System.out.println("Duration in minutes: " + duration.toMinutes());
Period period = Period.between(LocalDate.of(2026, 1, 1), LocalDate.of(2026, 12, 31)); System.out.println("Period in months: " + period.getMonths()); System.out.println("Period in days: " + period.getDays());
|
格式化和解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| LocalDateTime dateTime = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formattedDateTime = dateTime.format(formatter); System.out.println("Formatted: " + formattedDateTime);
String dateTimeStr = "2026-01-23 10:30:45"; LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter); System.out.println("Parsed: " + parsedDateTime);
LocalDate date = LocalDate.now(); String isoDate = date.format(DateTimeFormatter.ISO_DATE); System.out.println("ISO date: " + isoDate);
LocalDateTime dateTime2 = LocalDateTime.now(); String isoDateTime = dateTime2.format(DateTimeFormatter.ISO_DATE_TIME); System.out.println("ISO date time: " + isoDateTime);
|
时区处理
1 2 3 4 5 6 7 8 9 10 11
| ZonedDateTime zonedDateTime = ZonedDateTime.now(); System.out.println("Current zoned date time: " + zonedDateTime);
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York")); System.out.println("New York time: " + newYorkTime);
ZonedDateTime tokyoTime = newYorkTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo")); System.out.println("Tokyo time: " + tokyoTime);
|
应用场景
- 日期时间处理:处理日期、时间、日期时间等。
- 日期时间计算:计算日期时间的差值、添加或减去时间间隔等。
- 日期时间格式化:将日期时间格式化为字符串,或者将字符串解析为日期时间。
- 时区处理:处理不同时区的日期时间,进行时区转换。
- 时间戳处理:处理时间戳,进行时间戳和日期时间的转换。
七、其他新特性
1. Base64 编码
Java 8 引入了内置的 Base64 编码和解码功能,位于 java.util.Base64 类中。
1 2 3 4 5 6 7 8 9
| String original = "Hello, Base64!"; String encoded = Base64.getEncoder().encodeToString(original.getBytes()); System.out.println("Encoded: " + encoded);
byte[] decodedBytes = Base64.getDecoder().decode(encoded); String decoded = new String(decodedBytes); System.out.println("Decoded: " + decoded);
|
2. 并行数组操作
Java 8 为数组添加了并行操作方法,位于 java.util.Arrays 类中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int[] numbers = {5, 3, 8, 1, 2, 9, 4, 7, 6}; Arrays.parallelSort(numbers); System.out.println(Arrays.toString(numbers));
int[] arr = {1, 2, 3, 4, 5}; Arrays.parallelPrefix(arr, (a, b) -> a + b); System.out.println(Arrays.toString(arr));
int[] arr2 = new int[5]; Arrays.parallelSetAll(arr2, i -> i * 2); System.out.println(Arrays.toString(arr2));
|
3. 重复注解
Java 8 允许在同一位置重复使用相同的注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Repeatable(Filters.class) @interface Filter { String value(); }
@interface Filters { Filter[] value(); }
class MyClass { @Filter("filter1") @Filter("filter2") public void doSomething() { } }
|
4. 类型注解
Java 8 允许在更多的位置使用注解,包括类型参数、类型变量、数组元素等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class MyClass { public <@NonNull T> void method1(T t) { } public void method2(List<@NonNull String> list) { } public void method3(@NonNull String[] array) { } }
|
八、Java 8 新特性的实际应用
1. Web 开发
- Stream API:处理 HTTP 请求和响应中的数据。
- Lambda 表达式:简化事件处理和回调函数。
- Optional:处理可能不存在的请求参数和响应数据。
- 日期时间 API:处理时间戳和日期时间数据。
2. 大数据处理
- Stream API:处理大型数据集,支持并行处理。
- Lambda 表达式:简化数据处理逻辑。
- 方法引用:简化代码,提高可读性。
3. 移动应用开发
- Lambda 表达式:简化事件处理和异步操作。
- Optional:处理可能不存在的数据,避免空指针异常。
- 日期时间 API:处理时间和日期数据。
4. 企业应用开发
- Stream API:处理业务数据,支持并行处理。
- Lambda 表达式:简化业务逻辑代码。
- Optional:处理可能不存在的业务数据,避免空指针异常。
- 默认方法:为接口添加新功能,保持向后兼容。
- 日期时间 API:处理业务时间和日期数据。
九、总结
Java 8 引入了许多重要的新特性,这些特性不仅改变了 Java 代码的编写方式,也解决了之前版本中存在的一些问题:
- Lambda 表达式:引入了函数式编程的思想,简化了代码。
- Stream API:提供了一种声明式的数据处理方式,支持并行处理。
- Optional 类:解决了空指针异常的问题,使代码更加健壮。
- 默认方法:允许在接口中定义默认实现,实现了向后兼容。
- 方法引用:简化了 Lambda 表达式,使代码更加清晰。
- 日期时间 API:提供了更加清晰、更加易用的日期时间处理功能。
这些新特性的引入,使得 Java 8 成为了 Java 语言发展的一个重要里程碑。它们不仅提高了开发效率,也使代码更加清晰、更加健壮。在实际开发中,我们应该充分利用这些新特性,编写更加优雅、更加高效的 Java 代码。
Java 8 的新特性为我们打开了一扇新的大门,让我们能够以一种更加现代、更加函数式的方式编写 Java 代码。无论是处理集合数据、处理日期时间,还是避免空指针异常,Java 8 都提供了更加优雅的解决方案。让我们拥抱这些新特性,享受 Java 8 带来的编程乐趣!