李锋镝的博客

  • 首页
  • 时间轴
  • 评论区显眼包🔥
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 另一个网站
    • 我的导航站
    • 网站地图
    • 赞助
  • 留言
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 后端
  3. 正文

从"臃肿冗余"到"优雅简洁":那些让Java开发者顿悟的代码艺术与底层逻辑

2025年10月28日 181点热度 0人点赞 0条评论

每个Java开发者都有过这样的时刻:面对一段复杂业务逻辑,写了几十行嵌套if-else后陷入迷茫;或是调试NullPointerException到深夜,怀疑人生。但当我们第一次看到JDK源码里的Stream流水线、Optional链式判空、CompletableFuture异步组合时,总会忍不住惊叹——"原来Java代码还能这么写!"

本文不只是简单罗列"优雅代码案例",更会深入每段代码背后的设计思想、底层原理和适用场景,从"知其然"到"知其所以然",帮你理解Java语言演进的核心逻辑,真正掌握写出"让人哇塞"的代码的能力。

一、Lambda表达式:不止是语法糖,更是思维方式的革命

Java 8之前,匿名内部类是实现"传递行为"的唯一方式,但冗长的模板代码让逻辑淹没在语法细节中。Lambda表达式的出现,不仅简化了代码,更推动Java从"命令式编程"向"声明式编程"转变。

1. 先搞懂Lambda的本质:函数式接口的实例

很多人以为Lambda是"匿名内部类的简写",但本质上,Lambda表达式是"函数式接口"的实例——它没有类名、没有方法名,直接传递方法体,是一种"行为即数据"的体现。

(1)函数式接口的定义

函数式接口需满足两个条件:

  • 仅包含一个抽象方法(可以包含多个默认方法、静态方法);
  • 标注@FunctionalInterface注解(非强制,但能让编译器校验)。

JDK内置的核心函数式接口:

接口名称 抽象方法 用途 示例场景
Runnable void run() 无参数无返回值的行为 线程任务
Consumer<T> void accept(T t) 接收T类型参数,无返回值 集合遍历、数据消费
Predicate<T> boolean test(T t) 接收T类型参数,返回布尔值 数据过滤
Function<T,R> R apply(T t) 接收T类型参数,返回R类型结果 数据转换(映射)
Supplier<T> T get() 无参数,返回T类型结果 数据生成(如获取配置)

(2)Lambda的语法结构

Lambda表达式的语法可拆解为三部分,用"箭头"分隔:

(参数列表) -> { 方法体 }
  • 参数列表:与函数式接口的抽象方法参数一致,可省略参数类型(编译器自动推断);若只有一个参数,可省略括号;
  • 箭头->:分隔参数与方法体,固定语法;
  • 方法体:若只有一行代码,可省略大括号和return(若有返回值);若多行代码,需用大括号包裹并显式return。

(3)从匿名内部类到Lambda的蜕变

以Comparator排序为例,看Lambda如何简化代码:

// Java 8之前:匿名内部类(23行代码)
List<User> users = Arrays.asList(new User("Alice", 25), new User("Bob", 20));
Collections.sort(users, new Comparator<User>() {
    @Override
    public int compare(User u1, User u2) {
        // 按年龄升序排序
        if (u1.getAge() > u2.getAge()) {
            return 1;
        } else if (u1.getAge() < u2.getAge()) {
            return -1;
        }
        return 0;
    }
});

// Java 8之后:Lambda表达式(3行代码)
users.sort((u1, u2) -> u1.getAge() - u2.getAge());

// 更简洁:方法引用(1行代码)
users.sort(Comparator.comparingInt(User::getAge));

这段代码的进化,本质是从"描述排序的每一步操作"(命令式),到"描述排序的规则"(声明式)——我们只关心"按年龄排序",不关心排序的具体实现。

2. Lambda的核心应用场景

(1)简化线程与异步任务

除了原文章中的Thread示例,更常见的是ExecutorService提交任务:

// 传统写法:匿名内部类
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return "任务完成";
    }
});

// Lambda写法
executor.submit(() -> {
    Thread.sleep(1000);
    return "任务完成";
});

(2)集合操作的简化

结合List的forEach方法(接收Consumer),遍历集合无需手动写for循环:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 传统for循环
for (String name : names) {
    System.out.println(name);
}

// Lambda + forEach
names.forEach(name -> System.out.println(name));

// 方法引用简化
names.forEach(System.out::println);

3. 避坑指南:Lambda的常见误区

  • 变量捕获必须是final或effectively final:Lambda中引用的外部变量,不能被修改(否则编译报错),因为Lambda可能在另一个线程执行,变量修改会导致线程安全问题;

    // 错误:变量count被Lambda引用后又修改
    int count = 0;
    names.forEach(name -> {
      System.out.println(name);
      count++; // 编译报错:Variable used in lambda expression should be final or effectively final
    });
  • 不要用Lambda实现复杂逻辑:Lambda的优势是简洁,若方法体超过3行,建议提取为单独方法,用方法引用调用,避免Lambda变成"臃肿的匿名方法";
  • 注意异常处理:Lambda中抛出的受检异常,需在Lambda内部捕获,或函数式接口的抽象方法声明该异常(如Callable声明throws Exception)。

二、Stream API:数据处理的"工业流水线"

如果说Lambda是"传递行为的工具",那Stream API就是"基于行为处理数据的流水线"。它将数据处理抽象为"创建→中间操作→终端操作"的流程,让复杂数据处理逻辑变得清晰优雅。

1. Stream的核心原理:惰性求值与流水线

(1)Stream的生命周期

Stream的处理过程分为三个阶段,缺一不可:

  1. 创建阶段:从集合、数组、生成器等数据源创建Stream(如list.stream());
  2. 中间操作阶段:对Stream中的元素进行过滤、映射、排序等操作(如filter、map),中间操作是惰性的——仅记录操作链,不实际遍历元素;
  3. 终端操作阶段:触发Stream的遍历,执行所有中间操作,并返回结果(如collect、count、forEach),终端操作后Stream不可再使用。

(2)惰性求值的底层逻辑

为什么中间操作是惰性的?因为Stream会优化操作链,避免不必要的计算。例如:

// 案例:查找年龄大于18的第一个用户的名字
String userName = users.stream()
    .filter(user -> {
        System.out.println("过滤用户:" + user.getName()); // 中间操作
        return user.getAge() > 18;
    })
    .map(User::getName) // 中间操作
    .findFirst() // 终端操作
    .orElse("未知");

执行结果只会打印"过滤用户:Alice"(假设第一个用户年龄>18),后续用户不会被过滤——因为findFirst是短路终端操作,找到第一个符合条件的元素后就停止遍历。这种优化在大数据量场景下能大幅提升性能。

2. 复杂业务场景实战:从20行到5行

原文章展示了订单分组统计,我们再看一个更贴近电商的场景:计算"未取消的订单中,每个用户的平均订单金额,只保留平均金额>100的用户,按平均金额降序排序"。

(1)传统写法(约25行)

// 1. 过滤未取消的订单
List<Order> validOrders = new ArrayList<>();
for (Order order : allOrders) {
    if (order.getStatus() != OrderStatus.CANCELLED) {
        validOrders.add(order);
    }
}

// 2. 按用户分组,计算每个用户的订单总金额和数量
Map<Long, Double> userTotalAmount = new HashMap<>();
Map<Long, Integer> userOrderCount = new HashMap<>();
for (Order order : validOrders) {
    Long userId = order.getUserId();
    // 累加总金额
    userTotalAmount.put(userId, userTotalAmount.getOrDefault(userId, 0.0) + order.getTotalAmount());
    // 累加订单数量
    userOrderCount.put(userId, userOrderCount.getOrDefault(userId, 0) + 1);
}

// 3. 计算平均金额,过滤并排序
List<Map.Entry<Long, Double>> resultList = new ArrayList<>();
for (Map.Entry<Long, Double> entry : userTotalAmount.entrySet()) {
    Long userId = entry.getKey();
    Double total = entry.getValue();
    Integer count = userOrderCount.get(userId);
    Double avg = total / count;
    if (avg > 100) {
        resultList.add(new AbstractMap.SimpleEntry<>(userId, avg));
    }
}

// 4. 按平均金额降序排序
Collections.sort(resultList, new Comparator<Map.Entry<Long, Double>>() {
    @Override
    public int compare(Map.Entry<Long, Double> e1, Map.Entry<Long, Double> e2) {
        return e2.getValue().compareTo(e1.getValue());
    }
});

(2)Stream API写法(5行)

List<Map.Entry<Long, Double>> result = allOrders.stream()
    // 1. 过滤未取消的订单
    .filter(order -> order.getStatus() != OrderStatus.CANCELLED)
    // 2. 按用户分组,计算总金额和数量(用简单对象存储)
    .collect(Collectors.groupingBy(
        Order::getUserId,
        Collectors.mapping(
            Order::getTotalAmount,
            Collectors.summarizingDouble(Double::doubleValue) // 返回DoubleSummaryStatistics,包含总和、数量、平均等
        )
    ))
    // 3. 转换为Entry流,过滤平均金额>100的用户
    .entrySet().stream()
    .filter(entry -> entry.getValue().getAverage() > 100)
    // 4. 按平均金额降序排序
    .sorted((e1, e2) -> Double.compare(e2.getValue().getAverage(), e1.getValue().getAverage()))
    .collect(Collectors.toList());

这段代码的优势不仅是行数减少,更重要的是逻辑与代码结构完全对齐——每个中间操作对应一个业务步骤,可读性远超嵌套循环。

3. 进阶技巧:并行Stream与性能优化

Stream支持并行处理(parallelStream()),底层使用Fork/Join框架,能利用多核CPU提升大数据量处理性能:

// 并行处理:注意线程安全问题
double totalAmount = allOrders.parallelStream()
    .filter(order -> order.getStatus() != OrderStatus.CANCELLED)
    .mapToDouble(Order::getTotalAmount)
    .sum();

并行Stream的避坑点:

  • 线程安全:若中间操作修改外部非线程安全集合(如ArrayList),会导致数据错乱,需使用线程安全集合或collect而非forEach;
  • 性能阈值:数据量小时(如<1000元素),并行Stream的线程调度开销可能超过性能提升,反而更慢;
  • 避免状态依赖:中间操作不能依赖前一个元素的处理结果(如map中修改外部变量),并行时元素处理顺序不确定。

三、Optional:从"层层判空"到"链式调用",彻底告别NPE

每个Java开发者都经历过NullPointerException的折磨——为了获取"订单→用户→地址→城市",写了三层嵌套if-else。Optional的出现,不是为了"消灭null",而是强迫开发者"正视null",用更优雅的方式处理null场景。

1. Optional的核心设计:封装"可能为null的值"

Optional本质是一个"容器",它包含两种状态:

  • 存在值(Present):容器内有非null的值;
  • 空(Empty):容器内没有值(对应原有的null)。

通过这种封装,开发者必须显式处理"值不存在"的场景,避免无意识忽略null导致NPE。

2. Optional的正确使用:从创建到链式调用

(1)创建Optional的三种方式

方法 用途 注意事项
Optional.of(T t) 封装非null的值 若t为null,直接抛NullPointerException,适合确定值非null的场景
Optional.ofNullable(T t) 封装可能为null的值 若t为null,返回Optional.empty(),最常用
Optional.empty() 创建空的Optional 不直接传入值,适合返回"无结果"的场景

错误示例:用Optional.of(null)创建,直接抛NPE:

// 错误:user可能为null,用of会抛NPE
Optional<User> optionalUser = Optional.of(getUserById(1L)); // 若getUserById返回null,报错

// 正确:用ofNullable
Optional<User> optionalUser = Optional.ofNullable(getUserById(1L));

(2)常用方法与链式调用

Optional的核心价值在于链式调用,通过map、flatMap等方法避免嵌套判空,常用方法如下:

方法 用途 返回值类型
map(Function<T,R>) 若值存在,对值执行映射操作 Optional<R>
flatMap(Function<T,Optional<R>>) 若值存在,对值执行映射(返回Optional) Optional<R>
orElse(T other) 若值不存在,返回other(other需提前创建) T
orElseGet(Supplier<T>) 若值不存在,执行Supplier获取值 T
orElseThrow(Supplier<Exception>) 若值不存在,抛指定异常 T
ifPresent(Consumer<T>) 若值存在,执行Consumer操作 void

实战案例:获取"订单中的用户的收货地址的城市",处理所有null场景:

// 传统写法:三层if-else(12行)
public String getOrderUserCity(Order order) {
    if (order != null) {
        User user = order.getUser();
        if (user != null) {
            Address address = user.getShippingAddress();
            if (address != null) {
                String city = address.getCity();
                if (city != null) {
                    return city;
                }
            }
        }
    }
    return "未知城市";
}

// Optional写法:链式调用(5行)
public String getOrderUserCity(Order order) {
    return Optional.ofNullable(order)
        .map(Order::getUser) // 映射到User,返回Optional<User>
        .map(User::getShippingAddress) // 映射到Address,返回Optional<Address>
        .map(Address::getCity) // 映射到City,返回Optional<String>
        .orElse("未知城市"); // 无值时返回默认值
}

(3)map与flatMap的区别

当映射操作的返回值本身是Optional时,需用flatMap避免嵌套Optional(如Optional<Optional<String>>):

// 示例:用户的邮箱可能为null,getEmail返回Optional<String>
public Optional<String> getUserEmail(User user) {
    return Optional.ofNullable(user.getEmail());
}

// 若用map,会得到Optional<Optional<String>>
Optional<Optional<String>> nestedEmail = Optional.ofNullable(user)
    .map(this::getUserEmail);

// 用flatMap,会"扁平化"为Optional<String>
Optional<String> email = Optional.ofNullable(user)
    .flatMap(this::getUserEmail);

3. 避坑指南:Optional的反模式

  • 不要用get()直接获取值:get()在值不存在时会抛NoSuchElementException,相当于回到了NPE的问题,应优先用orElse、orElseGet等方法;

    // 错误:直接get(),可能抛异常
    User user = optionalUser.get();
    
    // 正确:用orElseGet
    User user = optionalUser.orElseGet(() -> new User("默认用户", 0));
  • 不要用Optional封装集合:Optional<List<User>>是反模式,集合本身可以为空(new ArrayList<>()),无需用Optional封装,应直接返回空集合而非Optional.empty();
  • 不要在方法参数中使用Optional:方法参数用Optional会增加调用者的复杂度(需手动创建Optional),应直接接收参数,在方法内部用ofNullable处理。

四、设计模式:从"if-else地狱"到"优雅扩展"

设计模式的核心是"解耦",让代码符合开闭原则(对扩展开放,对修改关闭)。原文章提到的策略模式和建造者模式,是Java开发中最常用的两种模式,我们深入其底层逻辑和实际应用。

1. 策略模式:彻底消灭if-else地狱

(1)为什么需要策略模式?

当业务逻辑中存在多个"分支判断"(如不同用户类型的折扣计算、不同支付方式的处理),用if-else会导致:

  • 代码冗长,可读性差;
  • 新增分支需修改原有代码,违反开闭原则;
  • 测试困难,需覆盖所有分支。

(2)策略模式的核心结构

策略模式包含三个角色:

  1. 策略接口(Strategy):定义统一的行为方法(如折扣计算);
  2. 具体策略(Concrete Strategy):实现策略接口,封装具体逻辑(如VIP折扣、新用户折扣);
  3. 上下文(Context):持有策略接口的引用,负责选择并执行具体策略。

(3)实战:电商折扣计算系统

以"根据用户类型计算商品折扣"为例,对比if-else和策略模式:

① 传统if-else写法(15行)
public double calculateDiscountPrice(String userType, double originalPrice) {
    if ("VIP".equals(userType)) {
        // VIP用户8折
        return originalPrice * 0.8;
    } else if ("MEMBER".equals(userType)) {
        // 会员用户9折
        return originalPrice * 0.9;
    } else if ("NEW".equals(userType)) {
        // 新用户95折
        return originalPrice * 0.95;
    } else if ("EMPLOYEE".equals(userType)) {
        // 员工7折
        return originalPrice * 0.7;
    }
    // 默认无折扣
    return originalPrice;
}

问题:新增"老用户折扣"时,需新增else if分支,修改原有方法。

② 策略模式写法(Spring环境)

在Spring中,可通过自动注入收集所有策略,实现"零配置扩展":

  1. 定义策略接口:
@FunctionalInterface // 函数式接口,简化实现
public interface DiscountStrategy {
    /**
     * 计算折扣后价格
     * @param originalPrice 原价
     * @return 折扣价
     */
    double calculate(double originalPrice);

    /**
     * 获取策略对应的用户类型(如"VIP")
     */
    String getUserType();
}
  1. 实现具体策略:
// VIP用户策略
@Component
public class VipDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double originalPrice) {
        return originalPrice * 0.8;
    }

    @Override
    public String getUserType() {
        return "VIP";
    }
}

// 新用户策略
@Component
public class NewUserDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double originalPrice) {
        return originalPrice * 0.95;
    }

    @Override
    public String getUserType() {
        return "NEW";
    }
}

// 更多策略...
  1. 上下文类(自动注入所有策略):
@Service
public class DiscountContext {
    // 自动注入所有实现DiscountStrategy的Bean,按用户类型分组
    private final Map<String, DiscountStrategy> strategyMap;

    // Spring构造函数注入:自动收集所有DiscountStrategy实例
    public DiscountContext(List<DiscountStrategy> strategyList) {
        this.strategyMap = strategyList.stream()
            .collect(Collectors.toMap(
                DiscountStrategy::getUserType, // Key:用户类型
                Function.identity() // Value:策略实例
            ));
    }

    // 计算折扣价
    public double calculateDiscountPrice(String userType, double originalPrice) {
        // 查找对应策略,无则返回原价
        return Optional.ofNullable(strategyMap.get(userType))
            .map(strategy -> strategy.calculate(originalPrice))
            .orElse(originalPrice);
    }
}
  1. 使用方式:
@Autowired
private DiscountContext discountContext;

public void test() {
    double vipPrice = discountContext.calculateDiscountPrice("VIP", 1000); // 800.0
    double newUserPrice = discountContext.calculateDiscountPrice("NEW", 1000); // 950.0
}
③ 策略模式的优势
  • 开闭原则:新增"老用户折扣"时,只需新增OldUserDiscountStrategy类,无需修改DiscountContext;
  • 单一职责:每个策略类只负责一种折扣逻辑,代码清晰,易于测试;
  • 灵活扩展:支持动态切换策略(如根据活动临时调整折扣)。

2. 建造者模式:解决"对象属性爆炸"问题

当一个类有多个属性(尤其是可选属性)时,传统构造函数会出现"参数爆炸",建造者模式通过链式调用,让对象创建更优雅。

(1)传统构造函数的问题

以Order类为例,包含订单号(必填)、用户ID(必填)、金额(必填)、备注(可选)、支付方式(可选)、收货地址(可选)等属性:

// 传统写法:重载构造函数(冗余)
public class Order {
    private String orderNo; // 必填
    private Long userId; // 必填
    private double amount; // 必填
    private String remark; // 可选
    private String payType; // 可选
    private String address; // 可选

    // 全参构造函数(参数太多,顺序易混淆)
    public Order(String orderNo, Long userId, double amount, String remark, String payType, String address) {
        this.orderNo = orderNo;
        this.userId = userId;
        this.amount = amount;
        this.remark = remark;
        this.payType = payType;
        this.address = address;
    }

    // 省略其他重载构造函数...
}

// 使用时:参数顺序易写错,可选参数需传null
Order order = new Order("20240520001", 1L, 99.99, null, "ALIPAY", "北京市朝阳区");

(2)建造者模式的实现(手动编写)

public class Order {
    // 成员变量(不变,用final修饰必填参数)
    private final String orderNo; // 必填
    private final Long userId; // 必填
    private final double amount; // 必填
    private String remark; // 可选
    private String payType; // 可选
    private String address; // 可选

    // 私有构造函数:仅允许Builder调用
    private Order(Builder builder) {
        // 校验必填参数
        if (builder.orderNo == null || builder.orderNo.isEmpty()) {
            throw new IllegalArgumentException("订单号不能为空");
        }
        if (builder.userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (builder.amount <= 0) {
            throw new IllegalArgumentException("金额必须大于0");
        }

        this.orderNo = builder.orderNo;
        this.userId = builder.userId;
        this.amount = builder.amount;
        this.remark = builder.remark;
        this.payType = builder.payType;
        this.address = builder.address;
    }

    // 静态内部Builder类
    public static class Builder {
        // 复制Order的成员变量
        private String orderNo;
        private Long userId;
        private double amount;
        private String remark;
        private String payType;
        private String address;

        // Builder构造函数:传入必填参数
        public Builder(String orderNo, Long userId, double amount) {
            this.orderNo = orderNo;
            this.userId = userId;
            this.amount = amount;
        }

        // 可选参数的setter方法,返回Builder自身(链式调用)
        public Builder remark(String remark) {
            this.remark = remark;
            return this;
        }

        public Builder payType(String payType) {
            this.payType = payType;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        // 构建Order对象
        public Order build() {
            return new Order(this);
        }
    }

    // Getter方法(无Setter,保证对象不可变)
    // ...省略...
}

// 使用方式:链式调用,清晰优雅
Order order = new Order.Builder("20240520001", 1L, 99.99)
    .remark("加急订单")
    .payType("ALIPAY")
    .address("北京市朝阳区")
    .build();

(3)Lombok简化建造者模式

手动编写Builder类繁琐,Lombok的@Builder注解可自动生成Builder,大幅简化代码:

import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;

@Getter // 自动生成Getter
@Builder // 自动生成Builder类
public class Order {
    @NonNull // 标记必填参数,Builder会自动校验非null
    private String orderNo;
    @NonNull
    private Long userId;
    @NonNull
    private double amount;
    // 可选参数(无@NonNull)
    private String remark;
    private String payType;
    private String address;
}

// 使用方式:与手动编写一致
Order order = Order.builder()
    .orderNo("20240520001") // 必填参数,不填编译报错
    .userId(1L)
    .amount(99.99)
    .remark("加急订单")
    .build();

(4)建造者模式的适用场景

  • 多属性对象:当类的属性超过4个,尤其是存在多个可选属性时;
  • 不可变对象:通过私有构造函数和Builder,保证对象创建后不可修改(线程安全);
  • 复杂对象创建:对象创建需多步逻辑(如校验、默认值设置),可在Builder中封装。

五、并发编程:从"线程安全"到"性能优化"的艺术

Java并发编程一直是难点,但JDK提供的CompletableFuture、ConcurrentHashMap等工具,让异步和线程安全变得优雅。

1. CompletableFuture:异步编程的"瑞士军刀"

传统Future的get()方法会阻塞线程,而CompletableFuture支持异步链式调用,能组合多个异步任务,实现真正的非阻塞编程。

(1)CompletableFuture的核心能力

  • 异步执行任务:通过supplyAsync(有返回值)、runAsync(无返回值)提交异步任务;
  • 任务组合:支持thenApply(同步处理结果)、thenCompose(异步处理结果)、thenCombine(合并两个任务结果);
  • 异常处理:通过exceptionally、handle优雅处理异步任务的异常;
  • 多任务协调:支持allOf(等待所有任务完成)、anyOf(等待任意任务完成)。

(2)实战:电商下单后的异步流程

电商下单后需执行三个异步任务:

  1. 扣减库存;
  2. 生成支付单;
  3. 发送下单成功短信。

传统Future需要手动管理线程和阻塞等待,CompletableFuture可优雅实现:

@Service
public class OrderService {
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private SmsService smsService;
    // 自定义线程池,避免使用默认线程池
    private final ExecutorService executor = Executors.newFixedThreadPool(5);

    public void createOrder(Order order) {
        // 1. 异步扣减库存(有返回值:是否扣减成功)
        CompletableFuture<Boolean> inventoryFuture = CompletableFuture.supplyAsync(() -> 
            inventoryService.deductStock(order.getProductId(), order.getQuantity()), executor
        );

        // 2. 异步生成支付单(有返回值:支付单号)
        CompletableFuture<String> paymentFuture = CompletableFuture.supplyAsync(() -> 
            paymentService.createPayment(order.getOrderNo(), order.getAmount()), executor
        );

        // 3. 库存扣减成功后,异步发送短信(无返回值)
        CompletableFuture<Void> smsFuture = inventoryFuture.thenAcceptAsync((deductSuccess) -> {
            if (deductSuccess) {
                smsService.sendOrderSuccessSms(order.getUserId(), order.getOrderNo());
            }
        }, executor);

        // 4. 等待所有任务完成,处理结果(非阻塞)
        CompletableFuture.allOf(inventoryFuture, paymentFuture, smsFuture)
            .whenComplete((unused, throwable) -> {
                if (throwable != null) {
                    // 处理异常(如回滚库存)
                    log.error("下单异步任务失败", throwable);
                    inventoryService.restoreStock(order.getProductId(), order.getQuantity());
                } else {
                    // 所有任务成功,更新订单状态
                    try {
                        Boolean deductSuccess = inventoryFuture.get();
                        String paymentNo = paymentFuture.get();
                        order.setStatus(deductSuccess ? OrderStatus.PENDING_PAY : OrderStatus.FAILED);
                        order.setPaymentNo(paymentNo);
                        orderRepository.save(order);
                    } catch (Exception e) {
                        log.error("获取异步任务结果失败", e);
                    }
                }
            });
    }
}

(3)CompletableFuture的避坑点

  • 自定义线程池:避免使用ForkJoinPool.commonPool()(默认线程池),高并发下会导致线程耗尽,应创建自定义线程池并指定拒绝策略;
  • 异常处理:每个异步任务都可能抛异常,需通过exceptionally或whenComplete处理,避免异常被吞噬;
  • 结果获取:尽量避免用get()阻塞获取结果,优先用whenComplete、thenAccept等非阻塞方式。

2. 并发集合:线程安全与性能的平衡

JDK并发包中的集合(ConcurrentHashMap、CopyOnWriteArrayList等),通过精妙的设计实现了"线程安全"与"高性能"的平衡。

(1)ConcurrentHashMap:从分段锁到CAS+synchronized

ConcurrentHashMap是线程安全的HashMap,底层实现经历了两次重大演进:

  • Java 7:采用"分段锁(Segment)",将数组分为16个Segment,每个Segment是一个独立的锁,支持16个线程同时写操作;
  • Java 8:放弃分段锁,采用"CAS+synchronized",对数组的每个节点(Node)加锁,粒度更细,性能更高。

核心优势:支持并发读写,无需外部同步,常用方法如下:

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// 1. 原子性put(不存在则添加,存在则不操作)
map.putIfAbsent("key1", 1);

// 2. 原子性计算(避免先get再put的线程安全问题)
map.compute("key1", (k, v) -> v == null ? 1 : v + 1); // 结果:2

// 3. 原子性删除(仅当值匹配时删除)
map.remove("key1", 2); // 删除成功,返回true

(2)CopyOnWriteArrayList:读多写少场景的神器

CopyOnWriteArrayList的核心思想是"写时复制"——写操作(add、remove)会复制底层数组,读操作(get)直接访问原数组,因此读操作无锁,性能极高。

适用场景:读操作远多于写操作(如配置列表、日志列表),代码示例:

CopyOnWriteArrayList<String> configs = new CopyOnWriteArrayList<>();

// 写操作:复制数组(线程安全)
configs.add("timeout=3000");
configs.remove("oldConfig");

// 读操作:无锁,直接访问(性能极高)
for (String config : configs) {
    System.out.println(config);
}

// 迭代器:基于快照,不抛出ConcurrentModificationException
Iterator<String> iterator = configs.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
    configs.add("newConfig"); // 迭代时修改,不会报错
}

注意事项:写操作会复制数组,因此写多的场景性能差,且内存占用高(复制期间存在两个数组)。

六、结语:代码的修行,是"优雅"与"实用"的平衡

看完这些代码案例,你可能会惊叹"原来Java还能这么写",但真正的代码艺术,不是追求"最炫的语法",而是在"可读性、可维护性、性能"之间找到平衡。

最后,分享几个写出优雅Java代码的习惯:

  1. 多读源码:JDK源码(如ArrayList、ConcurrentHashMap)、Spring源码中的优秀实现,是最好的老师;
  2. 善用工具:Lambda、Stream、Optional、Lombok等,让代码更简洁,但不要为了"炫技"而过度使用;
  3. 关注设计:遇到复杂逻辑时,先思考设计模式(如策略模式解耦if-else),再动手写代码;
  4. 持续重构:代码写完不是结束,而是开始,定期重构冗余代码,让它"越用越顺手"。

记住:代码是写给人看的,顺便能在机器上运行。十年后再看自己今天写的代码,如果还能轻松理解,甚至觉得"当时考虑得真周到",那你就真正掌握了代码的艺术。

除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/hou-duan/4542

相关文章

  • 配置Jackson使用字段而不是getter/setter来序列化和反序列化
  • JDK25模块级导入深度解析:Java导入机制的革命性进化
  • 数据库更新如何实现乐观锁
  • try...catch性能深度剖析:从JVM原理到实战优化,打破技术迷思
  • Spring WebFlux深度解析:异步非阻塞架构与实战落地指南
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: JAVA Lambda Stream
最后更新:2025年10月28日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >

文章评论

1 2 3 4 5 6 7 8 9 11 12 13 14 15 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 42 43 44 46 47 48 49 50 51 52 53 54 55 57 58 60 61 62 63 64 65 66 67 69 72 74 76 77 78 79 80 81 82 85 86 87 90 92 93 94 95 96 97 98 99
取消回复

我是人间惆怅客,知君何事泪纵横,断肠声里忆平生。

那年今日(04月14日)

  • 2010年:中国青海玉树大地震
  • 1894年:托马斯·爱迪生展示了其新发明活动电影放映机
  • 1629年:荷兰物理学家克里斯蒂安·惠更斯出生
  • 1578年:西班牙国王腓力三世出生
  • 605年:隋炀帝下令开凿大运河
  • 更多历史事件
最新 热点 随机
最新 热点 随机
Everything Claude Code 详细使用文档 配置Jackson使用字段而不是getter/setter来序列化和反序列化 这个域名注册整整十年了,十年时间,真快啊 Claude Code全维度实战指南:从入门到精通,解锁AI编程新范式 Apollo配置中心中的protalDB的作用是什么 org.apache.ibatis.plugin.Interceptor类详细介绍及使用
AI时代,个人技术博客的出路在哪里?使用WireGuard在Ubuntu 24.04系统搭建VPN这个域名注册整整十年了,十年时间,真快啊WordPress实现用户评论等级排行榜插件WordPress网站换了个字体,差点儿把样式换崩了做了一个WordPress文章热力图插件
开发者必懂的 AI 向量入门:从数学基础到实战应用 分代ZGC这么牛?底层原理是什么? 图解 | 原来这就是网络 使用springboot结合AI生成视频 Java枚举梳理总结一 Excel2016右键新建工作表,打开时提示“因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。”的解决办法
标签聚合
设计模式 ElasticSearch docker 多线程 SpringBoot JAVA AI 分布式 MySQL JVM Spring SQL 架构 K8s IDEA WordPress 数据库 AI编程 Redis 日常
友情链接
  • Blogs·CN
  • Honesty
  • Mr.Sun的博客
  • 临窗旋墨
  • 哥斯拉
  • 彬红茶日记
  • 志文工作室
  • 懋和道人
  • 拾趣博客导航
  • 搬砖日记
  • 旧时繁华
  • 林羽凡
  • 瓦匠个人小站
  • 皮皮社
  • 知向前端
  • 蜗牛工作室
  • 韩小韩博客
  • 风渡言

COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

域名年龄

Theme Kratos Made By Dylan

津ICP备2024022503号-3

京公网安备11011502039375号