Java 8 核心特性详解与代码示例
Java 8 是 Java 发展历程中的一个里程碑版本,引入了许多革命性的特性,极大地提升了开发效率和代码可读性。本文将详细解析 Java 8 的核心特性,包括 Lambda 表达式、函数式接口、Stream API、默认方法、Optional 类、新的日期时间 API 等,并通过丰富的代码示例展示这些特性的实际应用。
一、Lambda 表达式
Lambda 表达式是 Java 8 最重要的特性之一,它允许我们将函数作为方法参数或代码块作为数据传递,极大地简化了代码。
1.1 基本语法
Lambda 表达式的基本语法如下:
(parameters) -> expression
或
(parameters) -> { statements; }1.2 代码示例
// 传统方式创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running...");
}
}).start();
// 使用Lambda表达式创建线程
new Thread(() -> System.out.println("Thread is running...")).start();排序操作
List<String> names = Arrays.asList("Tom", "Jerry", "Alice", "Bob");
// 传统方式排序
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
// Lambda方式排序
names.sort((o1, o2) -> o1.compareTo(o2));
// 方法引用方式
names.sort(String::compareTo);Lambda 表达式通过简化匿名内部类的写法,使代码更加简洁易读。
二、函数式接口
函数式接口是只包含一个抽象方法的接口,Lambda 表达式需要与函数式接口一起使用。
2.1 内置函数式接口
Java 8 提供了一些常用的函数式接口:
| 接口 | 抽象方法 | 用途 |
|---|---|---|
| Predicate | boolean test(T t) | 条件判断(如过滤) |
| Function<T, R> | R apply(T t) | 数据转换(如映射) |
| Consumer | void accept(T t) | 消费数据(如打印) |
| Supplier | T get() | 生产数据(如工厂方法) |
| BiFunction<T, U, R> | R apply(T t, U u) | 双参数转换 |
| UnaryOperator | T apply(T t) | 一元运算(如平方) |
2.2 代码示例
Predicate 示例
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 定义两个Predicate
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> greaterThan3 = n -> n > 3;
// 组合使用Predicate
List<Integer> result = numbers.stream()
.filter(isEven.and(greaterThan3))
.collect(Collectors.toList());
System.out.println(result); // 输出自定义函数式接口
@FunctionalInterface
interface TriFunction<A, B, C, R> {
R apply(A a, B b, C c);
}
// 使用自定义函数式接口
TriFunction<Integer, Integer, Integer, Integer> sumThree = (a, b, c) -> a + b + c;
System.out.println(sumThree.apply(1, 2, 3)); // 输出 6函数式接口为 Lambda 表达式提供了类型支持,使得函数式编程在 Java 中成为可能。
三、Stream API
Stream API 是 Java 8 引入的全新抽象层,用于简化集合操作,支持声明式数据处理。
3.1 Stream 特点
- 声明性:描述做什么而非怎么做
- 链式操作:可组合多个操作形成流水线
- 函数式编程:鼓励使用无副作用函数
- 并行处理:支持并行流提高性能
- 延迟执行:中间操作延迟执行,终端操作触发计算
3.2 常用操作
中间操作
| 操作 | 说明 | 示例 |
|---|---|---|
| filter | 过滤元素 | .filter(s -> s.length() > 3) |
| map | 元素转换 | .map(String::toUpperCase) |
| flatMap | 扁平化流 | .flatMap(list -> list.stream()) |
| distinct | 去重 | .distinct() |
| sorted | 排序 | .sorted(Comparator.reverseOrder()) |
| peek | 调试输出 | .peek(System.out::println) |
终端操作
| 操作 | 说明 | 示例 |
|---|---|---|
| forEach | 遍历 | .forEach(System.out::println) |
| collect | 收集 | .collect(Collectors.toList()) |
| reduce | 归约 | .reduce(0, Integer::sum) |
| count | 计数 | .count() |
| anyMatch | 任意匹配 | .anyMatch(s -> s.startsWith("A")) |
| allMatch | 全部匹配 | .allMatch(s -> s.length() > 2) |
| noneMatch | 无匹配 | .noneMatch(s -> s.isEmpty()) |
3.3 代码示例
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 传统的处理方式
List<String> result = new ArrayList<>();
for (String word : words) {
if (word.length() > 5) {
String upperCase = word.toUpperCase();
result.add(upperCase);
}
}
Collections.sort(result);
// 使用 Stream 的方式
List<String> result = words.stream()
.filter(word -> word.length() > 5) // 过滤长度大于 5 的单词
.map(String::toUpperCase) // 转换为大写
.sorted() // 排序
.collect(Collectors.toList()); // 收集结果
Stream API 通过声明式编程风格,简化了集合操作,提高了代码的可读性和可维护性。
四、接口的默认方法和静态方法
Java 8 允许在接口中定义默认方法和静态方法,解决了接口演化问题。
4.1 默认方法
public interface Vehicle {
// 传统抽象方法
void start();
// 默认方法
default void honk() {
System.out.println("按喇叭:滴滴!");
}
// 静态方法
static void checkEngine() {
System.out.println("发动机状态检查");
}
}
class Car implements Vehicle {
@Override
public void start() {
System.out.println("汽车启动");
}
// 可选择是否重写honk()方法
}
public class Demo {
public static void main(String[] args) {
Car car = new Car();
car.start(); // 必须实现的抽象方法
car.honk(); // 直接继承默认实现 → 输出"按喇叭:滴滴!"
Vehicle.checkEngine(); // 静态方法直接调用 → 输出"发动机状态检查"
}
}4.2 多继承冲突解决
当实现多个包含相同默认方法的接口时:
- 类或父类中显式声明的方法优先级最高
- 选择提供最具体实现的默认方法
- 必须明确指定使用哪个接口的方法
interface A {
default void show() {
System.out.println("A的默认方法");
}
}
interface B {
default void show() {
System.out.println("B的默认方法");
}
}
class MyClass implements A, B {
// 必须明确指定使用哪个接口的方法
@Override
public void show() {
A.super.show(); // 显式调用A的默认方法
}
}默认方法使得接口可以平滑升级,而不会破坏现有实现。
五、Optional 类
Optional 类用于解决空指针异常问题,提供了一种更优雅的方式来处理可能为 null 的值。
5.1 常用方法
| 方法 | 说明 |
|---|---|
| Optional.of(value) | 创建非空Optional |
| Optional.ofNullable(value) | 创建可能为空的Optional |
| isPresent() | 检查值是否存在 |
| ifPresent(Consumer) | 值存在时执行操作 |
| orElse(other) | 值不存在时返回默认值 |
| orElseGet(Supplier) | 值不存在时通过Supplier提供值 |
| orElseThrow() | 值不存在时抛出异常 |
5.2 代码示例
// 创建Optional
Optional<String> optional = Optional.of("Hello");
Optional<String> nullableOptional = Optional.ofNullable(null);
// 检查值是否存在
if (optional.isPresent()) {
System.out.println(optional.get());
}
// 函数式风格处理
optional.ifPresent(System.out::println);
// 提供默认值
String result = nullableOptional.orElse("Default Value");
System.out.println(result); // 输出 "Default Value"
// 链式操作
Optional<String> transformed = optional
.map(s -> s + " World")
.filter(s -> s.length() > 5);
transformed.ifPresent(System.out::println); // 输出 "Hello World"
// 嵌套Optional处理
Optional<Optional<String>> nestedOptional = Optional.of(Optional.of("Nested"));
String flatResult = nestedOptional.flatMap(o -> o).orElse("Empty");
System.out.println(flatResult); // 输出 "Nested"Optional 类鼓励开发者显式处理 null 值,减少空指针异常的发生。
六、新的日期时间 API
Java 8 引入了全新的日期时间 API (JSR 310),解决了旧 java.util.Date 和 java.util.Calendar 的问题。
6.1 核心类
| 类 | 说明 |
|---|---|
| LocalDate | 日期(年月日) |
| LocalTime | 时间(时分秒) |
| LocalDateTime | 日期和时间 |
| ZonedDateTime | 带时区的日期和时间 |
| Period | 日期之间的间隔 |
| Duration | 时间之间的间隔 |
| DateTimeFormatter | 日期时间格式化 |
6.2 代码示例
// 获取当前日期和时间
LocalDate today = LocalDate.now();
LocalTime now = LocalTime.now();
LocalDateTime currentDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("Today: " + today);
System.out.println("Now: " + now);
System.out.println("Current DateTime: " + currentDateTime);
System.out.println("Zoned DateTime: " + zonedDateTime);
// 创建特定日期
LocalDate independenceDay = LocalDate.of(1949, 10, 1);
System.out.println("Independence Day: " + independenceDay);
// 日期运算
LocalDate nextWeek = today.plusDays(7);
LocalDate nextMonth = today.plusMonths(1);
System.out.println("Next Week: " + nextWeek);
System.out.println("Next Month: " + nextMonth);
// 日期比较
boolean isAfter = nextWeek.isAfter(today);
System.out.println("Is next week after today? " + isAfter);
// 格式化日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = currentDateTime.format(formatter);
System.out.println("Formatted DateTime: " + formattedDateTime);
// 解析日期字符串
LocalDateTime parsedDateTime = LocalDateTime.parse("2025-09-09 12:00:00", formatter);
System.out.println("Parsed DateTime: " + parsedDateTime);
// 计算两个日期之间的间隔
Period period = Period.between(today, independenceDay);
System.out.println("Period: " + period.getYears() + " years, "
+ period.getMonths() + " months, " + period.getDays() + " days");
// 计算时间间隔
LocalTime startTime = LocalTime.of(9, 0);
LocalTime endTime = LocalTime.of(17, 30);
Duration duration = Duration.between(startTime, endTime);
System.out.println("Duration: " + duration.toHours() + " hours");相比旧的日期时间API的设计混乱(年份从1900开始、月份从0计数)、可变性(线程不安全)、时区处理隐式依赖系统设置等问题,新的日期时间 API 设计更加合理,线程安全且易于使用。
七、方法引用
方法引用是 Lambda 表达式的一种简化写法,用于直接调用已有方法。
7.1 四种方法引用类型
| 类型 | 语法 | 示例 |
|---|---|---|
| 静态方法引用 | ClassName::staticMethod | Math::sqrt |
| 实例方法引用 | instance::method | System.out::println |
| 任意对象的实例方法引用 | ClassName::instanceMethod | String::length |
| 构造方法引用 | ClassName::new | ArrayList::new |
7.2 代码示例
// 静态方法引用
Function<Double, Double> sqrt = Math::sqrt;
System.out.println(sqrt.apply(16.0)); // 输出 4.0
// 实例方法引用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);
// 任意对象的实例方法引用
Function<String, Integer> length = String::length;
List<Integer> lengths = names.stream()
.map(length)
.collect(Collectors.toList());
System.out.println(lengths); // 输出 [5, 3, 7]
// 构造方法引用
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> newList = listSupplier.get();
// 排序示例
names.sort(String::compareToIgnoreCase);
System.out.println(names); // 输出 [Alice, Bob, Charlie]方法引用使代码更加简洁,特别是在与 Stream API 结合使用时。
八、其他特性
8.1 Base64 编码解码
Java 8 提供了 Base64 编码解码的内置支持:
// 编码
String original = "Hello, World!";
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);8.2 并行数组操作
int[] array = {3, 1, 4, 1, 5, 9, 2, 6};
// 并行排序
Arrays.parallelSort(array);
System.out.println("Sorted: " + Arrays.toString(array));
// 并行前缀计算
Arrays.parallelPrefix(array, (left, right) -> left + right);
System.out.println("Prefix Sum: " + Arrays.toString(array));8.3 Nashorn JavaScript 引擎
Java 8 引入了新的 JavaScript 引擎 Nashorn:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
try {
engine.eval("function greet(name) { print('Hello, ' + name); }");
engine.eval("greet('World');");
} catch (ScriptException e) {
e.printStackTrace();
}总结
Java 8 通过引入 Lambda 表达式 和 函数式接口 实现了函数式编程范式,大幅简化了代码结构;Stream API 以声明式操作集合数据,结合并行流提升处理效率;默认方法 允许接口平滑扩展而不破坏现有实现;Optional 类 以容器化方式优雅处理空值,减少 NullPointerException;全新的 日期时间 API(java.time)解决了旧 API 的线程安全和设计混乱问题;方法引用 进一步精简 Lambda 表达式的语法。
这些特性至今仍是现代 Java 开发的基础,值得每个 Java 开发者深入掌握。
评论0
暂时没有评论