一、函数式编程基础
1.1 什么是函数式编程?
函数式编程是一种编程范式,强调将计算视为函数的求值,避免使用共享状态和可变数据。核心特点:
- 不可变数据:避免副作用,数据一经创建不可修改
- 一等公民函数:函数可作为参数和返回值
- 声明式风格:关注"做什么"而非"怎么做"
- 高阶函数:函数可操作其他函数
1.2 为什么需要函数式编程?
传统编程问题 | 函数式解决方案 |
---|---|
复杂的控制流 | Stream API简化集合操作 |
共享可变状态 | 不可变对象和纯函数 |
冗长的样板代码 | Lambda表达式和方法引用 |
并发编程困难 | 无副作用代码天然支持并行 |
二、Java 8+ 核心新特性
2.1 Lambda表达式
// 传统方式
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// Lambda方式
Runnable r2 = () -> System.out.println("Hello");
// 带参数的Lambda
Comparator<String> comparator = (a, b) -> a.compareTo(b);
2.2 函数式接口
// 自定义函数式接口
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
// 默认方法
default int add(int a, int b) {
return a + b;
}
}
// 使用
Calculator calc = (a, b) -> a * b;
2.3 Stream API
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 过滤长度大于4的名字,转为大写,取前两个
List<String> result = names.stream()
.filter(n -> n.length() > 4)
.map(String::toUpperCase)
.limit(2)
.collect(Collectors.toList());
2.4 方法引用
// 构造器引用
Supplier<List<String>> listSupplier = ArrayList::new;
// 静态方法引用
Function<String, Integer> parser = Integer::parseInt;
// 实例方法引用
List<String> names = Arrays.asList("a", "b", "c");
names.forEach(System.out::println);
三、Stream API 深入解析
3.1 中间操作 vs 终端操作
类型 | 操作示例 | 返回值类型 |
---|---|---|
中间操作 | filter(), map(), sorted() | Stream |
终端操作 | forEach(), collect(), count() | 非Stream类型 |
3.2 常见Stream操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 过滤和映射
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 过滤偶数
.mapToInt(n -> n * 2) // 乘以2
.sum(); // 求和
// 并行流
long count = numbers.parallelStream()
.filter(n -> n > 3)
.count();
四、Optional类
4.1 解决空指针异常
// 创建Optional
Optional<String> optional = Optional.of("value");
Optional<String> emptyOpt = Optional.empty();
Optional<String> nullableOpt = Optional.ofNullable(null);
// 使用示例
String result = nullableOpt
.orElse("default"); // 默认值
// .orElseGet(() -> "default"); // 延迟计算默认值
// .orElseThrow(() -> new IllegalArgumentException()); // 抛出异常
// 链式操作
optional
.map(String::toUpperCase)
.ifPresent(System.out::println);
4.2 实际应用场景
public Optional<User> findUserById(Long id) {
// 从数据库查询用户
User user = userRepository.findById(id);
return Optional.ofNullable(user);
}
// 使用
findUserById(1L)
.map(User::getName)
.orElse("Unknown");
五、日期时间API(Java 8+)
5.1 核心类
类名 | 描述 |
---|---|
LocalDate | 不包含时间的日期(如2023-01-01) |
LocalTime | 不包含日期的时间(如14:30:00) |
LocalDateTime | 包含日期和时间 |
ZonedDateTime | 带时区的日期时间 |
Duration | 表示两个时间点之间的间隔 |
Period | 表示两个日期之间的间隔 |
5.2 常用操作
// 当前日期
LocalDate today = LocalDate.now();
// 指定日期
LocalDate date = LocalDate.of(2023, 5, 1);
// 日期计算
LocalDate tomorrow = today.plusDays(1);
LocalDate previousMonth = today.minusMonths(1);
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = today.format(formatter);
// 解析
LocalDate parsedDate = LocalDate.parse("2023-05-01", formatter);
六、学习要点总结
6.1 核心知识点
- Lambda表达式:简化匿名内部类,提高代码可读性
- 函数式接口:理解
@FunctionalInterface
注解和常用接口 - Stream API:掌握中间操作和终端操作,处理集合数据
- Optional类:有效避免NullPointerException
- 日期时间API:线程安全的新日期时间处理方式
6.2 常见问题解答
-
Q:Lambda表达式和匿名内部类的区别?
A:Lambda使用函数式接口,不创建新的类;匿名内部类会创建新类 -
Q:Stream和Collection的区别?
A:Collection是数据存储,Stream是数据处理;Stream不存储元素 -
Q:如何选择并行流?
A:计算密集型且数据量大时使用,否则可能因线程调度开销降低性能
七、练习题
7.1 基础练习
- 使用Lambda表达式实现
Comparator
对字符串列表排序 - 使用Stream API过滤出年龄大于18的用户,并计算平均年龄
- 使用Optional重构现有代码,消除Null检查
7.2 进阶练习
- 使用Stream API实现单词频率统计
- 实现一个方法,将
List<List<Integer>>
展平为一维列表 - 使用新日期API计算两个日期之间的工作日天数
八、总结
Java 8+ 引入的函数式编程特性和新API,极大地提升了Java的表达能力和开发效率。通过本章学习,你应该掌握了:
- 函数式编程的基本概念和优势
- Lambda表达式和方法引用的使用
- Stream API处理集合数据的方式
- Optional类避免空指针异常的最佳实践
- 新日期时间API的核心类和常用操作