开篇概览:Java 8 的革命性升级
Java 8(2014 年发布)是 Java 历史上最具影响力的版本之一,引入了函数式编程思想,极大提升了代码简洁性与表达力。其核心新特性包括:
- Lambda 表达式:简化匿名内部类,实现函数式编程;
- 函数式接口(
@FunctionalInterface):Lambda 的类型基础; - 方法引用:进一步简化 Lambda 表达式;
- Stream API:声明式、链式处理集合数据;
- Optional:优雅处理
null,避免空指针异常。
这些特性共同推动 Java 向更简洁、更安全、更高效的方向演进,是现代 Java 开发的必备技能。
一、Lambda 表达式:函数式编程的基石
1.1 什么是 Lambda 表达式?
- 一种匿名函数,可作为方法参数传递;
- 语法:
(参数) -> { 表达式或语句 }; - 前提:目标类型必须是函数式接口(仅含一个抽象方法的接口)。
1.2 语法简化规则
| 场景 | 语法 |
|---|---|
| 无参数 | () -> System.out.println("Hello") |
| 单参数 | x -> x * 2(可省略括号) |
| 多参数 | (x, y) -> x + y |
| 单表达式 | x -> x * 2(可省略 {} 和 return) |
| 多语句 | (x) -> { System.out.println(x); return x * 2; } |
1.3 示例:Lambda 替代匿名内部类
import java.util.*;
public class LambdaDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList("张三", "李四", "王五");
// 1. 传统方式:匿名内部类(冗长)
names.forEach(new java.util.function.Consumer<String>() {
@Override
public void accept(String name) {
System.out.println("传统方式: " + name);
}
});
// 2. Lambda 表达式(简洁)
names.forEach(name -> System.out.println("Lambda 方式: " + name));
// 3. 多参数示例:排序
List<Integer> numbers = Arrays.asList(5, 2, 8, 1);
// 使用 Lambda 实现 Comparator
numbers.sort((a, b) -> a - b); // 升序
System.out.println("排序后: " + numbers); // [1, 2, 5, 8]
// 4. 带返回值的 Lambda
java.util.function.Function<Integer, String> intToString =
(num) -> "数字: " + num;
System.out.println(intToString.apply(100)); // 数字: 100
}
}
✅ 优势:
- 代码量减少 50%+;
- 逻辑更聚焦(无样板代码);
- 支持并行处理(结合 Stream API)。
二、函数式接口(@FunctionalInterface)
2.1 定义与规则
- 仅含一个抽象方法的接口;
- 可包含默认方法、静态方法、
Object类方法; - 使用
@FunctionalInterface注解(非必需,但推荐)。
2.2 Java 内置常用函数式接口
| 接口 | 抽象方法 | 用途 |
|---|---|---|
Consumer<T> | void accept(T t) | 消费数据(如 forEach) |
Supplier<T> | T get() | 提供数据(如工厂方法) |
Function<T, R> | R apply(T t) | 转换数据(如映射) |
Predicate<T> | boolean test(T t) | 判断条件(如过滤) |
2.3 示例:自定义函数式接口
// 中文注释:定义一个函数式接口,用于验证用户年龄
@FunctionalInterface
interface AgeValidator {
boolean isValid(int age);
}
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// 使用 Lambda 实现 AgeValidator
AgeValidator adultValidator = age -> age >= 18;
System.out.println("17岁是否成年? " + adultValidator.isValid(17)); // false
System.out.println("20岁是否成年? " + adultValidator.isValid(20)); // true
// 使用内置 Predicate 接口(功能相同)
java.util.function.Predicate<Integer> isAdult = age -> age >= 18;
System.out.println("使用 Predicate: " + isAdult.test(20)); // true
}
}
⚠️ 注意:
- 若接口有多个抽象方法,编译器会报错;
@FunctionalInterface是编译期检查,确保接口符合规范。
三、方法引用(Method Reference)
3.1 什么是方法引用?
- Lambda 表达式的进一步简化;
- 当 Lambda 体仅调用一个已有方法时,可用
::代替。
3.2 四种语法形式
| 类型 | 语法 | 示例 |
|---|---|---|
| 静态方法 | 类名::静态方法名 | Integer::parseInt |
| 实例方法(任意对象) | 类名::实例方法名 | String::length |
| 实例方法(特定对象) | 对象::实例方法名 | System.out::println |
| 构造器 | 类名::new | ArrayList::new |
3.3 示例:方法引用 vs Lambda
import java.util.*;
import java.util.function.*;
public class MethodReferenceDemo {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "Banana", "Cherry");
// 1. 静态方法引用
List<Integer> numbers = Arrays.asList("1", "2", "3");
// Lambda: str -> Integer.parseInt(str)
List<Integer> ints = numbers.stream()
.map(Integer::parseInt) // 方法引用
.collect(Collectors.toList());
System.out.println("字符串转整数: " + ints); // [1, 2, 3]
// 2. 实例方法引用(任意对象)
// Lambda: s -> s.length()
List<Integer> lengths = words.stream()
.map(String::length) // 方法引用
.collect(Collectors.toList());
System.out.println("单词长度: " + lengths); // [5, 6, 6]
// 3. 特定对象方法引用
// Lambda: s -> System.out.println(s)
words.forEach(System.out::println); // 直接打印
// 4. 构造器引用
// Lambda: () -> new ArrayList<String>()
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> newList = listSupplier.get();
newList.add("新列表");
System.out.println("构造器引用创建列表: " + newList);
}
}
✅ 优势:
- 代码更简洁;
- 语义更清晰(直接指向方法);
- 性能略优于 Lambda(避免匿名类创建)。
四、Stream API:声明式数据处理
4.1 核心思想
- 声明式编程:关注“做什么”,而非“怎么做”;
- 链式调用:
stream().filter().map().collect(); - 惰性求值:中间操作不执行,直到终端操作触发;
- 支持并行:
parallelStream()自动多线程处理。
4.2 Stream 操作分类
| 类型 | 特点 | 常用方法 |
|---|---|---|
| 中间操作 | 返回 Stream,可链式调用 | filter, map, sorted, distinct |
| 终端操作 | 触发执行,返回结果 | collect, forEach, count, reduce |
4.3 示例:Stream API 综合应用
import java.util.*;
import java.util.stream.Collectors;
// 中文注释:定义用户类
class User {
private String name;
private int age;
private String city;
public User(String name, int age, String city) {
this.name = name;
this.age = age;
this.city = city;
}
// Getter 方法(Stream 需要)
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + ", city='" + city + "'}";
}
}
public class StreamAPIDemo {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("张三", 25, "北京"),
new User("李四", 30, "上海"),
new User("王五", 22, "北京"),
new User("赵六", 28, "深圳")
);
// 1. 过滤:获取北京用户
List<User> beijingUsers = users.stream()
.filter(user -> "北京".equals(user.getCity())) // 中间操作
.collect(Collectors.toList()); // 终端操作
System.out.println("北京用户: " + beijingUsers);
// 2. 映射:获取所有用户名
List<String> names = users.stream()
.map(User::getName) // 方法引用
.collect(Collectors.toList());
System.out.println("所有用户名: " + names);
// 3. 排序:按年龄升序
List<User> sortedByAge = users.stream()
.sorted(Comparator.comparing(User::getAge))
.collect(Collectors.toList());
System.out.println("按年龄排序: " + sortedByAge);
// 4. 统计:平均年龄
double averageAge = users.stream()
.mapToInt(User::getAge) // 转为 IntStream
.average()
.orElse(0.0);
System.out.println("平均年龄: " + averageAge);
// 5. 分组:按城市分组
Map<String, List<User>> groupedByCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
System.out.println("按城市分组: " + groupedByCity);
// 6. 并行流:处理大数据集(自动多线程)
List<Integer> largeList = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
largeList.add(i);
}
long count = largeList.parallelStream()
.filter(n -> n % 2 == 0)
.count();
System.out.println("偶数个数(并行): " + count);
}
}
✅ Stream 优势:
- 代码可读性强;
- 自动优化(如短路操作);
- 无缝支持并行处理。
五、Optional:优雅处理 null
5.1 为什么需要 Optional?
- 传统
null检查冗长且易遗漏; NullPointerException是最常见运行时异常;Optional显式表达“可能为空”的语义。
5.2 核心方法
| 方法 | 说明 |
|---|---|
Optional.of(T) | 创建非空 Optional(T 不能为 null) |
Optional.ofNullable(T) | 创建可空 Optional(T 可为 null) |
isPresent() | 判断是否有值 |
ifPresent(Consumer) | 有值时执行操作 |
orElse(T) | 无值时返回默认值 |
orElseGet(Supplier) | 无值时通过 Supplier 获取默认值 |
map(Function) | 转换值(链式调用) |
flatMap(Function) | 转换值(避免嵌套 Optional) |
5.3 示例:Optional 使用场景
import java.util.Optional;
// 中文注释:模拟用户服务
class UserService {
// 可能返回 null 的方法
public User findUserById(int id) {
if (id == 1) {
return new User("张三", 25, "北京");
}
return null; // 用户不存在
}
}
public class OptionalDemo {
public static void main(String[] args) {
UserService service = new UserService();
// 1. 传统 null 检查(冗长)
User user1 = service.findUserById(2);
if (user1 != null) {
System.out.println("用户存在: " + user1.getName());
} else {
System.out.println("用户不存在");
}
// 2. 使用 Optional 包装(推荐)
Optional<User> userOpt = Optional.ofNullable(service.findUserById(2));
// 方式一:isPresent() + get()
if (userOpt.isPresent()) {
System.out.println("Optional 用户: " + userOpt.get().getName());
}
// 方式二:ifPresent()(更简洁)
userOpt.ifPresent(user ->
System.out.println("ifPresent: " + user.getName()));
// 方式三:orElse() 提供默认值
User defaultUser = userOpt.orElse(new User("默认用户", 0, "未知"));
System.out.println("orElse 默认用户: " + defaultUser.getName());
// 3. 链式调用:安全获取城市
String city = Optional.ofNullable(service.findUserById(1))
.map(User::getCity) // 转换为 Optional<String>
.orElse("未知城市");
System.out.println("用户城市: " + city); // 北京
// 4. 避免嵌套 null 检查
String city2 = Optional.ofNullable(service.findUserById(2))
.map(User::getCity)
.orElse("无城市信息");
System.out.println("不存在用户的城市: " + city2); // 无城市信息
}
}
✅ 最佳实践:
- 方法返回值:优先返回
Optional<T>而非null;- 避免:
Optional.get()前不检查isPresent();- 不要:将
Optional用作类字段或方法参数。
六、总结:Java 8+ 新特性核心价值
| 特性 | 核心价值 | 典型场景 |
|---|---|---|
| Lambda 表达式 | 简化匿名类,支持函数式编程 | 事件监听、集合操作 |
| 函数式接口 | Lambda 的类型基础 | 自定义行为参数化 |
| 方法引用 | 进一步简化 Lambda | 调用已有方法 |
| Stream API | 声明式、链式数据处理 | 集合过滤、映射、聚合 |
| Optional | 优雅处理 null,避免空指针 | 方法返回值、链式调用 |
📌 核心思想:
“用更少的代码,表达更清晰的意图。”
掌握 Java 8+ 新特性,不仅能提升开发效率,更能写出现代、安全、高性能的 Java 代码,是进阶 Java 工程师的必经之路。
1423

被折叠的 条评论
为什么被折叠?



