jdk8 新特性方法详解

----------------------------------------------------------------------------------

JDK 8(Java 8)是 Java 历史上极具里程碑意义的版本,引入了多项革命性特性,极大提升了代码简洁性、可读性和开发效率。以下是 JDK 8 的核心新特性及方法详解,包含用法、示例和应用场景:

一、Lambda 表达式(闭包)

核心作用:用简洁的语法实现 “函数式编程”,可作为参数传递给方法,替代匿名内部类,简化代码。

语法

java
 
运行
 
 
 
 
(参数列表) -> { 代码块 }
// 简化规则:
// 1. 若参数只有一个,可省略括号:param -> { ... }
// 2. 若代码块只有一行,可省略大括号和return:(a,b) -> a + b
 

关键前提:函数式接口

Lambda 表达式只能用于函数式接口(仅含一个抽象方法的接口,可加@FunctionalInterface注解校验)。JDK 8 内置了大量函数式接口(如java.util.function包下的PredicateFunction等)。

示例:替代匿名内部类

java
 
运行
 
 
 
 
// 传统匿名内部类(Runnable是函数式接口)
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("传统方式");
    }
}).start();

// Lambda表达式简化
new Thread(() -> System.out.println("Lambda方式")).start();
 

实际应用:集合排序

java
 
运行
 
 
 
 
List<String> list = Arrays.asList("apple", "banana", "cherry");

// 传统排序(匿名内部类)
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

// Lambda简化
Collections.sort(list, (a, b) -> a.length() - b.length());
 

二、Stream API(集合处理)

核心作用:以 “声明式” 方式处理集合(类似 SQL 查询),支持链式操作,简化过滤、排序、转换等操作,且可并行处理提升效率。

核心流程

  1. 创建流:从集合、数组等数据源获取流(stream() 串行流 / parallelStream() 并行流)。
  2. 中间操作:对元素进行处理(过滤、映射、排序等,返回新流,可链式调用)。
  3. 终端操作:生成最终结果(如收集为集合、计算总和等,触发实际处理)。

常用方法及示例

java
 
运行
 
 
 
 
List<User> users = Arrays.asList(
    new User("张三", 20, "男"),
    new User("李四", 25, "女"),
    new User("王五", 18, "男")
);

// 1. 过滤(年龄>20)+ 提取姓名 + 收集为List
List<String> names = users.stream()
    .filter(u -> u.getAge() > 20) // 中间操作:过滤
    .map(User::getName)           // 中间操作:提取姓名(方法引用)
    .collect(Collectors.toList()); // 终端操作:收集结果

// 2. 排序(按年龄升序)+ 统计数量
long count = users.stream()
    .sorted(Comparator.comparingInt(User::getAge)) // 按年龄排序
    .count(); // 统计元素数

// 3. 并行流(多线程处理):计算年龄总和
int totalAge = users.parallelStream()
    .mapToInt(User::getAge)
    .sum();
 

核心优势

  • 替代繁琐的for循环,代码更简洁;
  • 并行流自动利用多核 CPU,无需手动处理多线程;
  • 中间操作 “惰性执行”,仅在终端操作时才实际处理,提升效率。

三、Optional 类

核心作用:封装 “可能为null” 的对象,通过链式方法安全处理空值,避免NullPointerException(NPE)。

核心方法

方法作用示例
ofNullable(T) 包装可能为null的值 Optional.ofNullable(user)
map(Function) 若值存在,对其转换(如获取属性) opt.map(User::getAge)
ifPresent(Consumer) 若值存在,执行操作 opt.ifPresent(u -> System.out.println(u))
orElse(T) 若值不存在,返回默认值 opt.orElse(new User())
orElseThrow(Supplier) 若值不存在,抛出指定异常 opt.orElseThrow(() -> new RuntimeException())

示例:安全获取嵌套属性

java
 
运行
 
 
 
 
// 避免多层null判断:获取用户的地址城市
String city = Optional.ofNullable(user)       // 包装user(可能为null)
    .map(User::getAddress)                    // 若user非null,获取address
    .map(Address::getCity)                    // 若address非null,获取city
    .orElse("未知城市");                       // 任意环节为null,返回默认值
 

四、接口的默认方法(Default Methods)与静态方法

核心作用:允许接口中定义 “带实现的方法”,解决接口升级时的兼容性问题(无需所有实现类都重写新方法)。

1. 默认方法(default关键字)

java
 
运行
 
 
 
 
public interface MyInterface {
    // 抽象方法(必须实现)
    void abstractMethod();
    
    // 默认方法(可直接调用,实现类可重写)
    default void defaultMethod() {
        System.out.println("接口的默认方法");
    }
}

// 实现类
public class MyImpl implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println("实现抽象方法");
    }
    
    // 可选:重写默认方法
    @Override
    public void defaultMethod() {
        System.out.println("重写默认方法");
    }
}
 

2. 静态方法(static关键字)

接口中可定义静态方法,直接通过接口名调用:
java
 
运行
 
 
 
 
public interface MathUtils {
    static int add(int a, int b) {
        return a + b;
    }
}

// 调用
int sum = MathUtils.add(1, 2); // 3
 

应用场景

  • 接口升级(如Collection接口新增stream()默认方法,所有集合实现类无需修改即可使用);
  • 提供通用工具方法(如Comparator.comparing()静态方法)。

五、方法引用(Method References)

核心作用:简化 Lambda 表达式,当 Lambda 体仅调用一个已存在的方法时,可直接引用该方法。

四种引用方式

类型语法示例
静态方法引用 类名::静态方法 Integer::parseInt(替代s -> Integer.parseInt(s)
实例方法引用(对象) 对象::实例方法 user::getName(替代u -> u.getName()
实例方法引用(类) 类名::实例方法 String::length(替代s -> s.length()
构造方法引用 类名::new User::new(替代() -> new User()

示例:配合 Stream 使用

java
 
运行
 
 
 
 
List<String> strList = Arrays.asList("1", "2", "3");

// 静态方法引用:将字符串转为整数
List<Integer> intList = strList.stream()
    .map(Integer::parseInt) // 等价于 s -> Integer.parseInt(s)
    .collect(Collectors.toList());

// 构造方法引用:创建User对象
List<User> userList = strList.stream()
    .map(User::new) // 假设User有单参数构造方法:User(String name)
    .collect(Collectors.toList());
 

六、新日期时间 API(java.time 包)

核心作用:替代线程不安全的DateCalendar,提供 immutable(不可变)、线程安全的日期时间处理类。

核心类及用法

类名作用示例
LocalDate 处理日期(年 / 月 / 日) LocalDate.now() → 2023-10-01
LocalTime 处理时间(时 / 分 / 秒) LocalTime.of(14, 30) → 14:30
LocalDateTime 处理日期 + 时间 LocalDateTime.now() → 2023-10-01T14:30:00
Instant 时间戳(UTC 时区) Instant.now() → 2023-10-01T06:30:00Z
Duration 计算时间间隔 Duration.between(time1, time2)
Period 计算日期间隔 Period.between(date1, date2)
DateTimeFormatter 日期格式化(线程安全) DateTimeFormatter.ofPattern("yyyy-MM-dd")

示例:日期格式化与计算

java
 
运行
 
 
 
 
// 1. 获取当前日期并格式化
LocalDate today = LocalDate.now();
String formatted = today.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
System.out.println(formatted); // 2023年10月01日

// 2. 计算3天后的日期
LocalDate future = today.plusDays(3); // 2023-10-04

// 3. 计算两个日期的间隔
LocalDate birthDate = LocalDate.of(1990, 1, 1);
Period period = Period.between(birthDate, today);
System.out.println("年龄:" + period.getYears() + "岁"); // 33岁(假设当前2023年)
 

七、CompletableFuture(异步编程)

核心作用:替代Future,支持异步任务的链式操作、异常处理和结果组合,更灵活地处理多线程异步任务。

核心优势

  • 支持链式调用(thenApplythenAccept等);
  • 内置异常处理(exceptionally);
  • 可组合多个异步任务(thenCombineallOf等)。

示例:异步任务链式执行

java
 
运行
 
 
 
 
// 异步执行任务1:计算结果
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1执行中...");
    return 10; // 模拟耗时计算
});

// 任务1完成后,异步执行任务2(处理结果)
CompletableFuture<String> future2 = future1.thenApplyAsync(result -> {
    System.out.println("任务2处理任务1的结果:" + result);
    return "结果:" + (result * 2); // 任务1结果*2
});

// 获取最终结果(阻塞等待,实际开发中可配合回调)
String result = future2.get();
System.out.println(result); // 结果:20
 

异常处理

java
 
运行
 
 
 
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    if (true) {
        throw new RuntimeException("任务执行失败");
    }
    return 100;
})
.exceptionally(ex -> { // 捕获异常并返回默认值
    System.out.println("捕获异常:" + ex.getMessage());
    return 0; // 异常时返回0
});

System.out.println(future.get()); // 0
 

八、其他重要特性

  1. 重复注解(Repeatable Annotations):允许同一注解在同一位置重复使用(需定义容器注解)。
    java
     
    运行
     
     
     
     
    @Repeatable(Roles.class)
    public @interface Role {
        String value();
    }
    
    public @interface Roles {
        Role[] value();
    }
    
    // 使用
    @Role("admin")
    @Role("user")
    public class MyClass {}
    
     
     
  2. 类型注解(Type Annotations):注解可用于类型声明(如泛型、参数类型),配合 Checker Framework 可进行编译时类型检查。
    java
     
    运行
     
     
     
     
    List<@NonNull String> list; // 声明集合元素不能为null
    
     
     
  3. Nashorn JavaScript 引擎:支持在 Java 中运行 JavaScript 代码(Java 15 后已废弃,推荐使用 GraalVM)。

总结

JDK 8 的新特性从根本上改变了 Java 的编程风格:
  • Lambda+Stream 简化了集合处理和函数式编程;
  • Optional 解决了空指针痛点;
  • 新日期 API 提供了安全易用的时间处理;
  • CompletableFuture 提升了异步编程效率。
这些特性不仅让代码更简洁,还大幅提升了开发效率和程序性能,是 Java 开发者必须掌握的核心内容。

----------------------------------------------------------------------------------

jdk8 Stream API 详解
 
JDK 8 的 Stream API 是处理集合的革命性特性,它以 “声明式” 方式对集合进行操作(类似 SQL 查询),支持链式调用、并行处理,大幅简化了集合的过滤、转换、聚合等操作。Stream 不是集合本身,而是对集合元素的 “计算管道”,其核心价值是简洁代码 + 高效处理。

一、Stream 核心概念

  • 定义:Stream 是数据元素的序列,支持对元素进行连续、聚合的操作。它不存储数据,也不改变源数据,所有操作都返回新的流(或最终结果)。
  • 特点:
    • 惰性执行:中间操作不会立即执行,只有终端操作触发时才会一次性处理(优化性能)。
    • 一次性:流只能被消费一次,处理完毕后自动关闭,再次使用会抛出异常。
    • 并行支持:通过 parallelStream() 可轻松实现多线程并行处理,无需手动管理线程。

二、Stream 操作流程

Stream 操作分为 3 个阶段,形成一个 “管道流”:
  1. 创建流:从数据源(集合、数组等)生成流。
  2. 中间操作:对元素进行处理(过滤、映射等),返回新的流(可链式调用)。
  3. 终端操作:生成最终结果(如集合、数值等),触发实际处理(流至此关闭)。
java
 
运行
 
 
 
 
// 完整流程示例:从集合创建流 → 中间操作 → 终端操作
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
List<String> result = list.stream()          // 1. 创建流(串行流)
    .filter(s -> s.length() > 5)             // 2. 中间操作:过滤长度>5的元素
    .map(String::toUpperCase)                // 2. 中间操作:转为大写
    .sorted()                                // 2. 中间操作:排序
    .collect(Collectors.toList());           // 3. 终端操作:收集为List

// 结果:[BANANA, CHERRY]
 

三、阶段 1:创建流(6 种常见方式)

从不同数据源创建 Stream:
数据源创建方式示例
集合 stream()(串行)/ parallelStream()(并行) list.stream() / set.parallelStream()
数组 Arrays.stream(数组) Arrays.stream(new int[]{1,2,3})
单个值 Stream.of(值1, 值2...) Stream.of("a", "b", "c")
空流 Stream.empty() Stream<String> emptyStream = Stream.empty();
无限流(迭代) Stream.iterate(初始值, 迭代函数) Stream.iterate(0, n -> n+2).limit(5) → 0,2,4,6,8
无限流(生成) Stream.generate(生成函数) Stream.generate(Math::random).limit(3) → 3 个随机数

四、阶段 2:中间操作(10 个核心方法)

中间操作返回新的流,支持链式调用,不立即执行,仅记录操作逻辑。

1. 过滤(filter

保留满足条件的元素(参数为 Predicate<T> 函数式接口)。
java
 
运行
 
 
 
 
// 过滤偶数
List<Integer> nums = Arrays.asList(1,2,3,4,5);
nums.stream()
    .filter(n -> n % 2 == 0) // 保留偶数
    .forEach(System.out::println); // 终端操作:打印 2,4
 

2. 映射(map / flatMap

  • map:将元素转换为另一种类型(参数为 Function<T, R>)。
  • flatMap:将元素转换为流,再合并为一个流(解决 “流中流” 问题)。
java
 
运行
 
 
 
 
// map:提取字符串长度
List<String> words = Arrays.asList("a", "bb", "ccc");
words.stream()
    .map(String::length) // 每个字符串→长度
    .forEach(System.out::println); // 1,2,3

// flatMap:将列表中的列表展平(如 [[1,2], [3,4]] → [1,2,3,4])
List<List<Integer>> listOfLists = Arrays.asList(
    Arrays.asList(1,2), 
    Arrays.asList(3,4)
);
listOfLists.stream()
    .flatMap(List::stream) // 每个子列表→流,再合并
    .forEach(System.out::println); // 1,2,3,4
 

3. 排序(sorted

  • 自然排序(元素实现 Comparable):sorted()
  • 自定义排序(参数为 Comparator<T>):sorted(comparator)
java
 
运行
 
 
 
 
// 自然排序(字符串按字典序)
List<String> strs = Arrays.asList("banana", "apple", "cherry");
strs.stream()
    .sorted()
    .forEach(System.out::println); // apple, banana, cherry

// 自定义排序(按长度倒序)
strs.stream()
    .sorted((s1, s2) -> s2.length() - s1.length())
    .forEach(System.out::println); // banana(6), cherry(6), apple(5)
 

4. 去重(distinct

根据元素的 equals() 方法去重(类似 HashSet)。
java
 
运行
 
 
 
 
List<Integer> nums = Arrays.asList(1,2,2,3,3,3);
nums.stream()
    .distinct()
    .forEach(System.out::println); // 1,2,3
 

5. 限制(limit(n)

保留前 n 个元素(若流长度 < n,保留所有)。
java
 
运行
 
 
 
 
// 取前2个元素
Stream.iterate(1, n -> n+1) // 无限流:1,2,3,4...
    .limit(2)
    .forEach(System.out::println); // 1,2
 

6. 跳过(skip(n)

跳过前 n 个元素(若流长度 < n,返回空流)。
java
 
运行
 
 
 
 
List<Integer> nums = Arrays.asList(1,2,3,4);
nums.stream()
    .skip(2) // 跳过前2个
    .forEach(System.out::println); // 3,4
 

7. 消费(peek

对元素执行操作(如打印、日志),但不改变元素(用于调试中间结果)。
java
 
运行
 
 
 
 
List<String> strs = Arrays.asList("a", "b");
strs.stream()
    .peek(s -> System.out.println("处理前:" + s)) // 调试
    .map(String::toUpperCase)
    .peek(s -> System.out.println("处理后:" + s))
    .collect(Collectors.toList());

// 输出:
// 处理前:a
// 处理后:A
// 处理前:b
// 处理后:B
 

五、阶段 3:终端操作(12 个核心方法)

终端操作触发流的实际处理,返回非流结果(如集合、数值、布尔值等),且流至此关闭。

1. 收集(collect

将流转换为集合或其他数据结构(最常用,依赖 Collectors 工具类)。
常用收集器(Collectors作用示例
toList() 收集为 List stream.collect(Collectors.toList())
toSet() 收集为 Set(去重) stream.collect(Collectors.toSet())
toMap(keyMapper, valueMapper) 收集为 Map users.stream().collect(Collectors.toMap(User::getId, User::getName))
joining(分隔符) 字符串拼接 stream.collect(Collectors.joining(",")) → "a,b,c"
groupingBy(分类函数) 按条件分组(返回 Map<K, List<V>> users.stream().collect(Collectors.groupingBy(User::getGender))(按性别分组)
partitioningBy( predicate) 按布尔条件分区(返回 Map<Boolean, List<V>> nums.stream().collect(Collectors.partitioningBy(n -> n%2==0))(分为偶数 / 奇数两组)

2. 计数(count

返回流中元素的数量(long 类型)。
java
 
运行
 
 
 
 
long evenCount = Arrays.asList(1,2,3,4).stream()
    .filter(n -> n%2 ==0)
    .count(); // 2
 

3. 查找(findFirst / findAny

  • findFirst():返回流中第一个元素(Optional<T>),适合有序流。
  • findAny():返回流中任意一个元素(Optional<T>),适合并行流(效率更高)。
java
 
运行
 
 
 
 
// 查找第一个偶数
Optional<Integer> firstEven = Arrays.asList(1,2,3,4).stream()
    .filter(n -> n%2 ==0)
    .findFirst(); // Optional[2]

// 并行流中查找任意偶数(可能是2或4)
Optional<Integer> anyEven = Arrays.asList(1,2,3,4).parallelStream()
    .filter(n -> n%2 ==0)
    .findAny();
 

4. 匹配(anyMatch / allMatch / noneMatch

  • anyMatch(predicate):是否存在至少一个元素满足条件(boolean)。
  • allMatch(predicate):是否所有元素都满足条件(boolean)。
  • noneMatch(predicate):是否所有元素都不满足条件(boolean)。
java
 
运行
 
 
 
 
List<Integer> nums = Arrays.asList(2,4,6);
boolean hasEven = nums.stream().anyMatch(n -> n%2 ==0); // true(存在偶数)
boolean allEven = nums.stream().allMatch(n -> n%2 ==0); // true(全是偶数)
boolean noOdd = nums.stream().noneMatch(n -> n%2 !=0);  // true(没有奇数)
 

5. 归约(reduce

将流中元素聚合为一个值(如求和、求最大值)。
  • 无初始值:reduce(BinaryOperator<T>) → 返回 Optional<T>(避免流为空时的问题)。
  • 有初始值:reduce(初始值, BinaryOperator<T>) → 返回 T(流为空时返回初始值)。
java
 
运行
 
 
 
 
// 求和(有初始值0)
int sum = Arrays.asList(1,2,3).stream()
    .reduce(0, Integer::sum); // 6(0+1+2+3)

// 求最大值(无初始值)
Optional<Integer> max = Arrays.asList(1,3,2).stream()
    .reduce(Integer::max); // Optional[3]
 

6. 遍历(forEach

对流中每个元素执行操作(参数为 Consumer<T>),无返回值。
java
 
运行
 
 
 
 
Arrays.asList("a", "b").stream()
    .forEach(System.out::println); // 打印 a, b
 

7. 极值(min / max

根据比较器返回流中最小 / 最大值(Optional<T>)。
java
 
运行
 
 
 
 
// 求字符串长度最小的元素
Optional<String> minStr = Arrays.asList("apple", "banana", "pear").stream()
    .min(Comparator.comparingInt(String::length)); // Optional[pear](长度4)
 

六、并行流(parallelStream

并行流利用多核 CPU 并行处理元素,适合大数据量场景(自动拆分任务到多个线程)。
java
 
运行
 
 
 
 
// 并行计算1-1000万的和(效率高于串行)
long sum = IntStream.rangeClosed(1, 10_000_000)
    .parallel() // 转为并行流
    .sum();
 
注意:
  • 并行流基于 ForkJoinPool,默认线程数为 CPU 核心数。
  • 线程安全问题:若中间操作涉及共享变量,需手动保证线程安全(如用 AtomicInteger)。
  • 并非所有场景都快:小数据量场景下,并行流的线程开销可能超过收益。

七、Stream 与传统集合操作对比

传统方式(for循环)Stream API
命令式编程(需手动控制循环逻辑) 声明式编程(只需描述 “做什么”)
代码冗长,嵌套深(如多层过滤 / 转换) 链式调用,代码简洁易读
并行处理需手动创建线程池,复杂 只需 parallelStream(),自动并行
立即执行(每步操作都处理数据) 惰性执行(终端操作时一次性处理)

八、核心总结

Stream API 是处理集合的 “瑞士军刀”,其核心价值在于:
  1. 简洁性:用链式调用替代繁琐的 for 循环和条件判断。
  2. 高效性:惰性执行 + 并行处理,优化大数据量场景的性能。
  3. 可读性:代码意图更清晰(如 filter 明确是过滤,map 明确是转换)。
掌握 Stream API 是 JDK 8 学习的核心,它彻底改变了 Java 处理集合的方式,是现代 Java 开发的必备技能。

----------------------------------------------------------------------------------

jdk8 Optional 详解
 
java.util.Optional<T> 是 JDK 8 引入的一个 “容器类”,专门用于封装可能为 null 的对象,其核心目标是优雅地避免空指针异常(NPE),替代传统的 null 检查逻辑,使代码更简洁、可读。

一、为什么需要 Optional?

在 Java 中,null 通常表示 “值不存在”,但直接操作可能为 null 的对象时,若未显式检查,极易触发 NullPointerException。例如:
java
 
运行
 
 
 
 
String str = null;
System.out.println(str.length()); // 直接抛出 NPE
 
传统解决方案是层层嵌套 if (obj != null) 判断,导致代码臃肿(被称为 “null 地狱”):
java
 
运行
 
 
 
 
// 传统 null 检查:获取用户的地址城市
String city = null;
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        city = address.getCity();
    }
}
 
Optional 通过封装可能为 null 的值,并提供一系列方法安全处理值的存在性,彻底避免了显式 null 检查。

二、Optional 核心概念

  • 本质:Optional 是一个 “容器”,内部可能包含一个非 null 的值,或为空(empty)。
  • 设计原则:
    • 不可变:创建后无法修改其包含的值(若有)。
    • 无 null 存储:Optional 自身永远不为 null,其内部要么有值(非 null),要么为空。
    • 明确表达 “值可能不存在” 的语义:比直接返回 null 更清晰地告知调用者 “需要处理空值情况”。

三、创建 Optional 实例(3 种方式)

通过 Optional 的静态方法创建实例,明确值的存在性:
方法作用适用场景示例
Optional.of(T value) 包装一个非 null 的值 明确知道值不可能为 null 时 Optional.of("hello")(若传 null 会抛 NPE)
Optional.ofNullable(T value) 包装一个可能为 null 的值 不确定值是否为 null 时(最常用) Optional.ofNullable(getUser())
Optional.empty() 创建一个空 Optional(无值) 明确表示 “值不存在” 时 return Optional.empty();

四、核心方法详解(按使用场景分类)

1. 判断值是否存在

  • isPresent():返回 boolean,表示 Optional 中是否有值(非空)。
    java
     
    运行
     
     
     
     
    Optional<String> opt = Optional.ofNullable("test");
    if (opt.isPresent()) {
        System.out.println("值存在:" + opt.get());
    }
    
     
     
  • isEmpty()(JDK 11+):与 isPresent() 相反,判断值是否不存在。
    java
     
    运行
     
     
     
     
    if (opt.isEmpty()) {
        System.out.println("值不存在");
    }
    
     
     

2. 若值存在则执行操作(避免 isPresent() + get()

  • ifPresent(Consumer<? super T> action):若值存在,则执行传入的消费操作(如打印、赋值);若不存在,不做任何事。
    java
     
    运行
     
     
     
     
    // 传统写法:if (str != null) { ... }
    // Optional 写法:
    Optional<String> opt = Optional.ofNullable(getName());
    opt.ifPresent(name -> System.out.println("姓名:" + name)); // 仅当 name 非 null 时执行
    
     
     
  • ifPresentOrElse(Consumer, Runnable)(JDK 9+):若值存在执行消费操作,否则执行另一个操作。
    java
     
    运行
     
     
     
     
    opt.ifPresentOrElse(
        name -> System.out.println("姓名:" + name),
        () -> System.out.println("姓名不存在")
    );
    
     
     

3. 获取值(或处理 “无值” 情况)

  • get():直接获取值,但值不存在时会抛出 NoSuchElementException(不推荐直接使用,相当于 “换一种方式抛异常”)。
    java
     
    运行
     
     
     
     
    try {
        String value = opt.get(); // 若 opt 为空,抛异常
    } catch (NoSuchElementException e) {
        // 处理无值情况
    }
    
     
     
  • orElse(T other):若值存在则返回该值,否则返回默认值 otherother 无论是否用到都会被创建)。
    java
     
    运行
     
     
     
     
    // 若 user 为 null,返回默认用户
    User user = Optional.ofNullable(getUser()).orElse(new User("默认用户"));
    
     
     
  • orElseGet(Supplier<? extends T> supplier):若值存在则返回该值,否则通过 supplier 生成默认值(延迟创建,仅当需要时才执行)。
    java
     
    运行
     
     
     
     
    // 仅当 getUser() 返回 null 时,才调用 createDefaultUser()
    User user = Optional.ofNullable(getUser()).orElseGet(() -> createDefaultUser());
    
     
     
  • orElseThrow(Supplier<? extends X> exceptionSupplier):若值存在则返回该值,否则抛出 supplier 生成的异常(推荐用于 “值必须存在” 的场景)。
    java
     
    运行
     
     
     
     
    // 若用户不存在,抛出自定义异常
    User user = Optional.ofNullable(getUser())
        .orElseThrow(() -> new UserNotFoundException("用户不存在"));
    
     
     

4. 转换值(安全处理嵌套对象)

  • map(Function<? super T, ? extends U> mapper):若值存在,对其应用 mapper 转换,返回新的 Optional(包含转换后的值);若值不存在,返回空 Optional。
    java
     
    运行
     
     
     
     
    // 获取用户的年龄(若用户存在)
    Optional<User> userOpt = Optional.ofNullable(getUser());
    Optional<Integer> ageOpt = userOpt.map(User::getAge); // 若 user 存在,返回 age 的 Optional
    
     
     
  • flatMap(Function<? super T, Optional<U>> mapper):若值存在,对其应用 mapper 转换(mapper 直接返回 Optional),避免嵌套 Optional(Optional<Optional<U>>)。
    java
     
    运行
     
     
     
     
    // 若用户存在,获取其地址的 Optional(假设 getUserAddress() 返回 Optional<Address>)
    Optional<Address> addressOpt = userOpt.flatMap(User::getUserAddress);
    
     
     

5. 过滤值

  • filter(Predicate<? super T> predicate):若值存在且满足 predicate,返回当前 Optional;否则返回空 Optional。
    java
     
    运行
     
     
     
     
    // 过滤出年龄 > 18 的用户
    Optional<User> adultUser = userOpt.filter(user -> user.getAge() > 18);
    
     
     

五、典型应用场景

场景 1:安全获取嵌套对象的属性(彻底消除多层 null 检查)

java
 
运行
 
 
 
 
// 需求:获取用户的地址城市,若任意环节为 null,返回 "未知城市"
String city = Optional.ofNullable(user)       // 包装可能为 null 的 user
    .map(User::getAddress)                    // 若 user 非 null,获取 address(可能为 null)
    .map(Address::getCity)                    // 若 address 非 null,获取 city(可能为 null)
    .orElse("未知城市");                       // 任意环节为 null,返回默认值
 

场景 2:方法返回值明确 “可能为空”(替代 null 返回)

java
 
运行
 
 
 
 
// 传统方法:返回 null 表示“未找到”,调用者可能忘记检查
public User findUserById(Long id) {
    return userRepository.findById(id); // 可能返回 null
}

// 优化:返回 Optional,明确告知调用者“可能无值”
public Optional<User> findUserById(Long id) {
    return Optional.ofNullable(userRepository.findById(id));
}

// 调用者必须处理“无值”情况(更安全)
Optional<User> userOpt = userService.findUserById(1L);
User user = userOpt.orElseThrow(() -> new UserNotFoundException("用户不存在"));
 

场景 3:结合 Stream 处理集合中的空值

java
 
运行
 
 
 
 
List<User> users = Arrays.asList(
    new User("张三", 20), 
    null, // 集合中可能存在 null
    new User("李四", 25)
);

// 过滤 null 并提取姓名
List<String> names = users.stream()
    .map(Optional::ofNullable) // 将每个元素包装为 Optional
    .filter(Optional::isPresent) // 保留有值的 Optional
    .map(Optional::get) // 提取值(此时已非 null)
    .map(User::getName)
    .collect(Collectors.toList()); // [张三, 李四]
 

六、注意事项(避免滥用)

  1. 不推荐作为类的字段或方法参数:Optional 设计用于方法返回值,作为字段或参数会增加代码复杂度(如序列化问题)。
  2. 避免过度嵌套:Optional<Optional<User>> 这种嵌套结构是错误的,应使用 flatMap 扁平化。
  3. 慎用 get() 方法:get() 在值不存在时抛异常,违背 Optional 设计初衷,优先用 orElseorElseThrow 等方法。
  4. 不要替代所有 null 检查:简单场景下(如单一层级的 null 判断),传统 if (obj != null) 可能更直观。

七、总结

Optional 是处理 “潜在 null 值” 的优雅解决方案,其核心价值在于:
  • 明确语义:通过返回 Optional 清晰告知调用者 “值可能不存在”,强制处理空值场景。
  • 消除嵌套 if:用 map 链式调用替代多层 null 检查,代码更简洁。
  • 减少 NPE:从根源上避免因忘记 null 检查导致的空指针异常。
合理使用 Optional 能显著提升代码的健壮性和可读性,是现代 Java 开发的必备技能。

----------------------------------------------------------------------------------

jdk8 新日期 API 详解
 
JDK 8 引入了全新的日期时间 API(位于 java.time 包下),彻底解决了旧 API(java.util.Datejava.util.Calendar)的线程不安全、设计混乱(如月份从 0 开始)、API 命名不直观等问题。新 API 基于不可变对象设计,线程安全,且职责划分清晰,是处理日期时间的最佳实践。

一、新日期 API 的核心优势

  1. 不可变性:所有类(如 LocalDateLocalTime)都是不可变的,修改操作会返回新对象,避免线程安全问题。
  2. 清晰的职责划分:不同类处理不同场景(如日期、时间、日期 + 时间、时间戳等),API 命名直观。
  3. 线程安全:无需额外同步,可在多线程环境下安全使用。
  4. 统一的格式化:DateTimeFormatter 替代线程不安全的 SimpleDateFormat,支持多种格式化方式。

二、核心类详解(按场景分类)

1. 本地日期时间(无时区信息)

适用于不需要时区的场景(如 “生日”“会议时间”,默认基于本地时区)。
类名作用示例(当前值)
LocalDate 仅包含日期(年 / 月 / 日) 2023-10-01
LocalTime 仅包含时间(时 / 分 / 秒 / 纳秒) 14:30:25.123456789
LocalDateTime 包含日期 + 时间 2023-10-01T14:30:25.123456789
(1)LocalDate:处理日期
创建实例:
java
 
运行
 
 
 
 
// 获取当前日期
LocalDate today = LocalDate.now(); // 2023-10-01

// 创建指定日期(年, 月, 日)
LocalDate birthday = LocalDate.of(1990, 1, 1); // 1990-01-01
LocalDate christmas = LocalDate.of(2023, Month.DECEMBER, 25); // 用枚举指定月份
 
常用方法:
java
 
运行
 
 
 
 
LocalDate date = LocalDate.of(2023, 10, 1);

// 增减日期
LocalDate tomorrow = date.plusDays(1); // 2023-10-02(加1天)
LocalDate lastMonth = date.minusMonths(1); // 2023-09-01(减1个月)

// 获取字段
int year = date.getYear(); // 2023
Month month = date.getMonth(); // OCTOBER(枚举)
int day = date.getDayOfMonth(); // 1
DayOfWeek dayOfWeek = date.getDayOfWeek(); // SUNDAY(周日)

// 比较
boolean isAfter = date.isAfter(LocalDate.of(2023, 9, 30)); // true(是否在之后)
boolean isLeapYear = date.isLeapYear(); // false(是否闰年)
 
(2)LocalTime:处理时间
创建实例:
java
 
运行
 
 
 
 
// 获取当前时间
LocalTime now = LocalTime.now(); // 14:30:25.123456789

// 创建指定时间(时, 分, 秒, 纳秒)
LocalTime morning = LocalTime.of(8, 30); // 08:30:00
LocalTime preciseTime = LocalTime.of(14, 20, 30, 123456789); // 14:20:30.123456789
 
常用方法:
java
 
运行
 
 
 
 
LocalTime time = LocalTime.of(14, 30);

// 增减时间
LocalTime later = time.plusHours(2); // 16:30(加2小时)
LocalTime earlier = time.minusMinutes(15); // 14:15(减15分钟)

// 获取字段
int hour = time.getHour(); // 14
int minute = time.getMinute(); // 30
 
(3)LocalDateTime:处理日期 + 时间
创建实例:
java
 
运行
 
 
 
 
// 获取当前日期时间
LocalDateTime now = LocalDateTime.now(); // 2023-10-01T14:30:25

// 创建指定日期时间
LocalDateTime meeting = LocalDateTime.of(2023, 10, 1, 15, 0); // 2023-10-01T15:00
// 也可通过 LocalDate + LocalTime 组合
LocalDateTime combo = LocalDate.now().atTime(LocalTime.now());
 
常用方法:
java
 
运行
 
 
 
 
LocalDateTime dt = LocalDateTime.of(2023, 10, 1, 14, 30);

// 转换为 LocalDate / LocalTime
LocalDate date = dt.toLocalDate(); // 2023-10-01
LocalTime time = dt.toLocalTime(); // 14:30

// 增减操作(支持年月日时分秒)
LocalDateTime nextWeek = dt.plusWeeks(1); // 2023-10-08T14:30
 

2. 时间戳(Instant

Instant 表示UTC 时区的时间戳(从 1970-01-01T00:00:00Z 开始的毫秒数),类似旧 API 的 System.currentTimeMillis(),但精度更高(纳秒级)。
创建与操作:
java
 
运行
 
 
 
 
// 获取当前时间戳(UTC)
Instant now = Instant.now(); // 2023-10-01T06:30:25.123456789Z(UTC时间,比北京时间晚8小时)

// 从毫秒数创建
Instant fromMillis = Instant.ofEpochMilli(System.currentTimeMillis());

// 增减时间(基于UTC)
Instant later = now.plus(1, ChronoUnit.HOURS); // 加1小时
 
与旧 API 转换:
java
 
运行
 
 
 
 
// Instant → Date
Date date = Date.from(Instant.now());

// Date → Instant
Instant instant = new Date().toInstant();
 

3. 时间间隔(Duration)与日期间隔(Period

  • Duration:计算两个时间(LocalTime/Instant)之间的间隔(秒、纳秒)。
  • Period:计算两个日期(LocalDate)之间的间隔(年、月、日)。
(1)Duration:时间间隔
java
 
运行
 
 
 
 
LocalTime start = LocalTime.of(10, 0);
LocalTime end = LocalTime.of(12, 30);

// 计算间隔
Duration duration = Duration.between(start, end);

// 获取间隔值
long hours = duration.toHours(); // 2(小时)
long minutes = duration.toMinutes(); // 150(分钟)
long seconds = duration.getSeconds(); // 9000(秒)
 
(2)Period:日期间隔
java
 
运行
 
 
 
 
LocalDate startDate = LocalDate.of(2020, 1, 1);
LocalDate endDate = LocalDate.of(2023, 10, 1);

// 计算间隔
Period period = Period.between(startDate, endDate);

// 获取间隔值
int years = period.getYears(); // 3(年)
int months = period.getMonths(); // 9(月)
int days = period.getDays(); // 0(日)
 

4. 日期格式化与解析(DateTimeFormatter

DateTimeFormatter 替代了旧 API 中线程不安全的 SimpleDateFormat,支持预定义格式和自定义格式,且线程安全。
常用格式:
java
 
运行
 
 
 
 
// 预定义格式(ISO标准)
DateTimeFormatter isoDate = DateTimeFormatter.ISO_LOCAL_DATE; // yyyy-MM-dd
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME; // yyyy-MM-ddTHH:mm:ss

// 自定义格式(模式字母与 SimpleDateFormat 类似)
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
 
格式化(日期→字符串):
java
 
运行
 
 
 
 
LocalDateTime dt = LocalDateTime.of(2023, 10, 1, 14, 30);
String formatted = dt.format(customFormatter); // "2023年10月01日 14:30:00"
 
解析(字符串→日期):
java
 
运行
 
 
 
 
String str = "2023年10月01日 14:30:00";
LocalDateTime parsed = LocalDateTime.parse(str, customFormatter);
 

5. 时区处理(ZonedDateTime 与 ZoneId

涉及跨时区场景时,使用 ZonedDateTime(带时区的日期时间)和 ZoneId(时区 ID)。
常用时区操作:
java
 
运行
 
 
 
 
// 获取所有可用时区ID(如 Asia/Shanghai, UTC)
Set<String> zoneIds = ZoneId.getAvailableZoneIds();

// 上海时区的当前时间
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

// UTC时区的当前时间
ZonedDateTime utcTime = ZonedDateTime.now(ZoneId.of("UTC"));

// 时区转换(将上海时间转为纽约时间)
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
 

三、与旧 API 的对比(为什么弃用旧 API?)

旧 API(Date/Calendar新 API(java.time
可变对象(线程不安全) 不可变对象(线程安全)
类职责混乱(Date 既含日期又含时间) 职责清晰(LocalDate/LocalTime 等)
月份从 0 开始(1 月 = 0) 月份从 1 开始(符合直觉)
格式化依赖 SimpleDateFormat(线程不安全) 用 DateTimeFormatter(线程安全)
API 命名不直观(如 getYear() 返回 1900 年起的偏移量) 命名直观(getYear() 直接返回年份)

四、最佳实践

  1. 优先使用不可变类:LocalDateLocalTime 等都是不可变的,避免修改原始对象,而是通过 plusXxx()/minusXxx() 生成新对象。
  2. 明确时区:若涉及跨时区,使用 ZonedDateTime 而非 LocalDateTime,避免时区混淆。
  3. 格式化用 DateTimeFormatter:彻底替代 SimpleDateFormat,尤其在多线程环境。
  4. 避免旧 API:新项目直接使用 java.time 包,旧项目逐步迁移(可通过 toInstant()/from() 转换)。

总结

JDK 8 新日期时间 API 解决了旧 API 的所有痛点,通过清晰的类设计、不可变性和线程安全,让日期时间处理变得简单直观。核心类 LocalDateLocalTimeLocalDateTime 覆盖了绝大多数本地场景,Instant 处理时间戳,Duration/Period 计算间隔,DateTimeFormatter 负责格式化,掌握这些类即可优雅地处理各类日期时间需求。

----------------------------------------------------------------------------------

jdk8 CompletableFuture 详解
 
CompletableFuture 是 JDK 8 引入的异步编程工具类,基于 Future 接口扩展,解决了传统 Future 的局限性(如必须阻塞或轮询获取结果、无法链式处理多个异步任务、缺乏异常处理机制等)。它支持链式操作、异常处理、任务组合等高级特性,是 Java 异步编程的核心工具。

一、核心优势

相比传统 FutureCompletableFuture 的关键改进:
  • 非阻塞回调:无需阻塞等待结果,可通过回调函数处理异步结果。
  • 链式操作:多个异步任务可按依赖关系串联,形成流水线。
  • 任务组合:支持合并多个异步任务的结果(如 “等待所有任务完成” 或 “任一任务完成”)。
  • 内置异常处理:提供专门的方法捕获和处理异步任务中的异常。
  • 灵活性:可自定义线程池,避免默认线程池资源耗尽问题。

二、创建 CompletableFuture 实例

CompletableFuture 提供了多种创建方式,覆盖 “有返回值”“无返回值”“已完成任务” 等场景:
方法作用适用场景
supplyAsync(Supplier<T>) 异步执行有返回值的任务(用默认线程池) 需要返回结果的异步操作
supplyAsync(Supplier<T>, Executor) 异步执行有返回值的任务(用自定义线程池) 需控制线程资源的场景
runAsync(Runnable) 异步执行无返回值的任务(用默认线程池) 无需返回结果的异步操作(如日志)
runAsync(Runnable, Executor) 异步执行无返回值的任务(用自定义线程池) 同上,需控制线程资源
completedFuture(T value) 创建已完成的任务(直接返回指定值) 模拟异步结果或立即返回的场景
CompletableFuture<Void> empty() 创建空的未完成任务 需手动完成的场景(配合 complete()

示例:创建基础异步任务

java
 
运行
 
 
 
 
// 1. 有返回值的异步任务(默认线程池:ForkJoinPool.commonPool())
CompletableFuture<String> supplyTask = CompletableFuture.supplyAsync(() -> {
    // 模拟耗时操作(如RPC调用、数据库查询)
    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
    return "任务结果";
});

// 2. 无返回值的异步任务(自定义线程池)
ExecutorService executor = Executors.newFixedThreadPool(3); // 自定义线程池(推荐)
CompletableFuture<Void> runTask = CompletableFuture.runAsync(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
    System.out.println("无返回值任务执行完毕");
}, executor);

// 3. 已完成的任务(立即获取结果)
CompletableFuture<String> completedTask = CompletableFuture.completedFuture("已完成的结果");
System.out.println(completedTask.get()); // 直接返回:已完成的结果
 

三、核心操作:链式处理异步结果

CompletableFuture 最强大的特性是链式操作:一个任务完成后,自动触发后续任务,形成 “流水线”。核心方法分为三类(按是否返回新结果划分):

1. 处理结果并返回新值(thenApply 系列)

  • thenApply(Function<? super T,? extends U>):当前任务完成后,用 Function 处理结果,返回新的 CompletableFuture<U>(同步执行后续任务,使用当前线程)。
  • thenApplyAsync(Function...):后续任务异步执行(使用默认线程池或自定义线程池)。
java
 
运行
 
 
 
 
// 示例:任务1返回数字,任务2将其转为字符串(链式处理)
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 100);

// 同步处理:任务1完成后,当前线程执行转换
CompletableFuture<String> task2 = task1.thenApply(num -> "结果:" + num);

// 异步处理:任务1完成后,线程池执行转换(推荐,避免阻塞当前线程)
CompletableFuture<String> task3 = task1.thenApplyAsync(num -> "异步结果:" + num, executor);

// 获取最终结果(阻塞等待,实际开发中常用回调替代)
System.out.println(task2.get()); // 结果:100
 

2. 消费结果不返回新值(thenAccept 系列)

  • thenAccept(Consumer<? super T>):当前任务完成后,用 Consumer 消费结果(无返回值,CompletableFuture<Void>),同步执行。
  • thenAcceptAsync(Consumer...):异步消费结果。
java
 
运行
 
 
 
 
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Hello");

// 消费结果(打印)
task.thenAcceptAsync(result -> System.out.println("消费结果:" + result), executor);
// 输出:消费结果:Hello
 

3. 任务完成后执行动作(thenRun 系列)

  • thenRun(Runnable):当前任务完成后,执行 Runnable(不关心前序结果),同步执行。
  • thenRunAsync(Runnable...):异步执行。
java
 
运行
 
 
 
 
CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
});

// 任务完成后执行后续动作
task.thenRunAsync(() -> System.out.println("前序任务已完成!"), executor);
// 输出:前序任务已完成!
 

四、异常处理

异步任务可能抛出异常,CompletableFuture 提供了专门的异常处理方法,避免异常被默默吞噬:

1. exceptionally(Function<Throwable, ? extends T>)

捕获前序任务的异常,返回默认值(类似 catch)。
java
 
运行
 
 
 
 
CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {
    // 模拟异常
    if (true) throw new RuntimeException("任务执行失败");
    return 100;
}).exceptionally(ex -> {
    // 处理异常,返回默认值
    System.out.println("捕获异常:" + ex.getMessage());
    return 0; // 异常时返回0
});

System.out.println(task.get()); // 输出:0
 

2. handle(BiFunction<? super T, Throwable, ? extends U>)

无论前序任务成功或失败,都执行处理(类似 try-finally)。
java
 
运行
 
 
 
 
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("失败");
    return "成功结果";
}).handle((result, ex) -> {
    if (ex != null) {
        return "处理异常:" + ex.getMessage();
    } else {
        return "处理成功:" + result;
    }
});

System.out.println(task.get()); // 输出:处理异常:失败
 

五、组合多个异步任务

CompletableFuture 支持将多个独立的异步任务按逻辑组合(如 “等待所有完成”“等待任一完成”“依赖多个结果”)。

1. 等待两个任务完成后组合结果(thenCombine 系列)

  • thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V>):当前任务和 other 任务都完成后,用 BiFunction 组合结果。
  • thenAcceptBoth(...):组合结果并消费(无返回值)。
  • runAfterBoth(...):两个任务完成后执行动作(不关心结果)。
java
 
运行
 
 
 
 
// 任务1:计算a=10
CompletableFuture<Integer> taskA = CompletableFuture.supplyAsync(() -> 10);
// 任务2:计算b=20
CompletableFuture<Integer> taskB = CompletableFuture.supplyAsync(() -> 20);

// 组合结果:a + b
CompletableFuture<Integer> sumTask = taskA.thenCombine(taskB, (a, b) -> a + b);
System.out.println(sumTask.get()); // 30
 

2. 等待任一任务完成(applyToEither 系列)

  • applyToEither(CompletionStage<? extends T> other, Function<? super T,? extends U>):当前任务或 other 任务中先完成的一个,用 Function 处理其结果。
  • acceptEither(...):消费先完成的结果。
  • runAfterEither(...):任一任务完成后执行动作。
java
 
运行
 
 
 
 
// 任务1:2秒后返回"任务A"
CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {
    try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
    return "任务A";
});

// 任务2:1秒后返回"任务B"
CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
    return "任务B";
});

// 取先完成的任务结果
CompletableFuture<String> firstTask = taskA.applyToEither(taskB, result -> "先完成的是:" + result);
System.out.println(firstTask.get()); // 1秒后输出:先完成的是:任务B
 

3. 等待多个任务全部完成(allOf)与任一完成(anyOf

  • allOf(CompletableFuture<?>... cfs):等待所有任务完成(返回 CompletableFuture<Void>,无返回值,需手动获取每个任务结果)。
  • anyOf(CompletableFuture<?>... cfs):等待任一任务完成(返回 CompletableFuture<Object>,结果为第一个完成的任务的结果)。
java
 
运行
 
 
 
 
// 多个任务
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "结果1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "结果2");
CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "结果3");

// 1. 等待所有任务完成
CompletableFuture<Void> allTask = CompletableFuture.allOf(task1, task2, task3);
allTask.thenRun(() -> {
    // 所有任务完成后,手动获取每个结果
    try {
        System.out.println(task1.get() + "," + task2.get() + "," + task3.get()); // 结果1,结果2,结果3
    } catch (Exception e) { e.printStackTrace(); }
}).get();

// 2. 等待任一任务完成
CompletableFuture<Object> anyTask = CompletableFuture.anyOf(task1, task2, task3);
System.out.println(anyTask.get()); // 可能是结果1、结果2或结果3(取决于哪个先完成)
 

六、手动完成任务(complete 系列)

可手动触发任务完成(或异常),适用于 “外部事件驱动” 场景(如监听回调结果)。
方法作用
complete(T value) 手动将任务标记为 “完成”,返回指定值
completeExceptionally(Throwable ex) 手动将任务标记为 “异常完成”,抛出指定异常
java
 
运行
 
 
 
 
CompletableFuture<String> manualTask = new CompletableFuture<>();

// 模拟外部事件触发完成(如异步回调)
new Thread(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
    manualTask.complete("手动完成的结果"); // 手动设置结果
}).start();

System.out.println(manualTask.get()); // 1秒后输出:手动完成的结果
 

七、线程池选择(重要!)

  • 默认线程池:supplyAsync/runAsync 不指定线程池时,使用 ForkJoinPool.commonPool(),其线程数默认等于 CPU 核心数(适合计算密集型任务)。
  • 自定义线程池:IO 密集型任务(如网络请求、数据库操作)建议使用自定义线程池(如 Executors.newFixedThreadPool(n)),避免默认线程池被耗尽。
java
 
运行
 
 
 
 
// 自定义线程池(IO密集型任务,线程数可设为CPU核心数*2)
ExecutorService ioExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

// 使用自定义线程池执行任务
CompletableFuture.supplyAsync(() -> {
    // 执行IO操作(如HTTP请求)
    return "IO结果";
}, ioExecutor);
 

八、与传统 Future 的对比

特性传统 FutureCompletableFuture
获取结果 需阻塞(get())或轮询 支持回调(非阻塞)
链式操作 不支持(需手动嵌套) 支持(thenApply 等方法)
异常处理 需在任务内部捕获,外部难处理 提供 exceptionally/handle 等方法
任务组合 不支持 支持 thenCombine/allOf 等
灵活性 低(仅能获取结果) 高(支持手动完成、线程池自定义)

总结

CompletableFuture 是 Java 异步编程的 “瑞士军刀”,其核心价值在于:
  • 用链式操作简化异步任务的依赖关系,代码更清晰;
  • 提供完善的异常处理,避免异步任务中的异常被忽略;
  • 支持多任务组合,轻松实现 “等待所有”“等待任一” 等逻辑;
  • 可自定义线程池,灵活控制资源,避免性能问题。
掌握 CompletableFuture 能显著提升异步代码的可读性和可维护性,是处理并发任务(如微服务调用、批量处理)的必备技能。

----------------------------------------------------------------------------------

 

----------------------------------------------------------------------------------

----------------------------------------------------------------------------------

----------------------------------------------------------------------------------

----------------------------------------------------------------------------------

posted @ 2025-10-15 15:00  hanease  阅读(68)  评论(0)    收藏  举报