《Java 8-21 高频特性实战(上):5 个场景解决 50% 开发问题(附可运行代码)》

Java 8 到 Java 21 核心特性实战:10 个高频场景 + 可运行代码

Java 版本迭代频繁,从 Java 8 到 Java 21 新增了数十个实用特性(如 Lambda、Stream、虚拟线程等),但多数开发者仅停留在 "听过" 层面,未真正落地到项目中。本文聚焦10 个工作高频场景,用 "特性说明 + 问题场景 + 实战代码" 的形式,让你快速掌握核心用法,代码直接复制到 IDE 即可运行,同时适配面试 / 项目双需求。

一、场景 1:集合遍历(告别 for 循环,用 Lambda+Stream 简化)

特性说明

Java 8 引入的 Lambda 表达式 + Stream API,彻底简化集合遍历、过滤、排序操作,代码量减少 50%+,可读性更高。

问题场景

遍历列表筛选出 "年龄> 18 的用户",并按年龄升序排序,传统 for 循环代码冗余。

实战代码

java 复制代码
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

// 用户实体类
class User {
    private String name;
    private int age;

    // 构造器+getter
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}

public class StreamTraversalDemo {
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User("张三", 22));
        userList.add(new User("李四", 17));
        userList.add(new User("王五", 25));
        userList.add(new User("赵六", 19));

        // 1. 筛选年龄>18的用户 + 按年龄升序排序 + 提取姓名列表
        List<String> adultNames = userList.stream()
                .filter(user -> user.getAge() > 18) // 过滤条件
                .sorted(Comparator.comparingInt(User::getAge)) // 排序(方法引用简化)
                .map(User::getName) // 提取姓名
                .collect(Collectors.toList()); // 收集结果

        // 2. 打印结果
        System.out.println("成年用户姓名(按年龄排序):" + adultNames);
        // 输出:成年用户姓名(按年龄排序):[赵六, 张三, 王五]
    }
}

核心亮点

  • filter:按条件筛选元素,替代传统 if 判断;
  • sorted:支持方法引用(User::getAge),比匿名内部类更简洁;
  • map:提取集合中指定字段,避免循环中手动取值。

二、场景 2:空值处理(用 Optional 杜绝空指针异常)

特性说明

Java 8 引入的 Optional 类,专门解决 "空指针异常(NullPointerException)",通过链式调用替代多层 if (obj != null) 判断。

问题场景

获取用户的地址信息(用户可能为 null,地址也可能为 null),传统写法需嵌套 2 层空判断。

实战代码

java 复制代码
import java.util.Optional;

// 地址实体类
class Address {
    private String city;

    public Address(String city) { this.city = city; }
    public String getCity() { return city; }
}

public class OptionalDemo {
    public static void main(String[] args) {
        // 模拟:用户为null / 地址为null / 正常情况
        User nullUser = null;
        User userWithNullAddr = new User("孙七", 23);
        User userWithAddr = new User("周八", 24);
        userWithAddr.setAddress(new Address("上海")); // 给用户设置地址

        // 1. 安全获取用户地址(无空指针风险)
        String city1 = Optional.ofNullable(nullUser) // 允许用户为null
                .map(User::getAddress) // 提取地址(用户为null则跳过)
                .map(Address::getCity) // 提取城市(地址为null则跳过)
                .orElse("默认城市"); // 所有环节为null时返回默认值

        String city2 = Optional.ofNullable(userWithNullAddr)
                .map(User::getAddress)
                .map(Address::getCity)
                .orElse("默认城市");

        String city3 = Optional.ofNullable(userWithAddr)
                .map(User::getAddress)
                .map(Address::getCity)
                .orElse("默认城市");

        System.out.println("城市1:" + city1); // 输出:城市1:默认城市
        System.out.println("城市2:" + city2); // 输出:城市2:默认城市
        System.out.println("城市3:" + city3); // 输出:城市3:上海
    }
}

// 补充 User 类的 address 字段和 getter/setter
class User {
    private String name;
    private int age;
    private Address address;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getter/setter
    public Address getAddress() { return address; }
    public void setAddress(Address address) { this.address = address; }
    public String getName() { return name; }
    public int getAge() { return age; }
}

核心亮点

  • ofNullable:支持传入 null 值,避免 of 方法的空指针风险;
  • 链式 map:连续提取嵌套字段,无需手动判断每层是否为 null;
  • orElse:设置默认值,替代 else 分支。

三、场景 3:日期时间处理(用 LocalDateTime 替代 Date)

特性说明

Java 8 引入的 java.time 包(JSR 310),解决了传统 Date 类 "线程不安全、API 混乱" 的问题,提供 LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期时间)等核心类。

问题场景

计算 "当前时间 3 天后的日期""两个日期相差的天数""日期格式化",传统 Date+SimpleDateFormat 需处理时区和线程安全问题。

实战代码

java 复制代码
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;

public class DateTimeDemo {
    public static void main(String[] args) {
        // 1. 获取当前日期时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前日期时间:" + now); // 输出:2025-10-20T15:30:45.123

        // 2. 日期计算:当前时间 +3 天
        LocalDateTime threeDaysLater = now.plusDays(3);
        System.out.println("3天后日期时间:" + threeDaysLater); // 输出:2025-10-23T15:30:45.123

        // 3. 日期格式化(线程安全,无需手动同步)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedNow = now.format(formatter);
        System.out.println("格式化后当前时间:" + formattedNow); // 输出:2025-10-20 15:30:45

        // 4. 两个日期相差天数
        LocalDate date1 = LocalDate.of(2025, 1, 1);
        LocalDate date2 = LocalDate.of(2025, 12, 31);
        Period period = Period.between(date1, date2);
        System.out.println("两个日期相差天数:" + period.getDays()); // 输出:304(实际差值按年/月/日拆分)
    }
}

核心亮点

  • 线程安全:DateTimeFormatter 替代 SimpleDateFormat,无需担心多线程问题;
  • 链式 API:plusDays/minusMonths 等方法直接计算,无需手动处理毫秒值;
  • 清晰区分日期 / 时间:LocalDate 仅存日期,LocalTime 仅存时间,避免 Date 类的混淆。

四、场景 4:接口增强(用 default 方法避免接口升级兼容问题)

特性说明

Java 8 允许接口中定义 default 方法(带实现体)和 static 方法,解决了 "接口升级导致所有实现类必须修改" 的痛点。

问题场景

原有接口 Payment 只有 pay() 方法,现在需要新增 refund() 方法,若直接添加抽象方法,所有实现类(如 WechatPayAlipay)都会报错。

实战代码

java 复制代码
// 支付接口(含 default 方法,支持升级)
interface Payment {
    // 抽象方法(必须由实现类实现)
    void pay(double amount);

    // default 方法(带默认实现,实现类可选择性重写)
    default void refund(double amount) {
        System.out.println("默认退款逻辑:退款金额" + amount + "元(未指定支付渠道)");
    }

    // static 方法(接口工具方法,直接通过接口调用)
    static void validateAmount(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("金额必须大于0!");
        }
    }
}

// 微信支付实现类
class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        Payment.validateAmount(amount); // 调用接口静态方法校验
        System.out.println("微信支付:" + amount + "元");
    }

    // 可选:重写 default 方法,自定义微信退款逻辑
    @Override
    public void refund(double amount) {
        Payment.validateAmount(amount);
        System.out.println("微信退款:" + amount + "元(原路退回微信余额)");
    }
}

// 支付宝实现类(不重写 refund 方法,使用默认实现)
class Alipay implements Payment {
    @Override
    public void pay(double amount) {
        Payment.validateAmount(amount);
        System.out.println("支付宝支付:" + amount + "元");
    }
}

public class DefaultMethodDemo {
    public static void main(String[] args) {
        Payment wechatPay = new WechatPay();
        wechatPay.pay(100); // 输出:微信支付:100.0元
        wechatPay.refund(100); // 输出:微信退款:100.0元(原路退回微信余额)

        Payment alipay = new Alipay();
        alipay.pay(200); // 输出:支付宝支付:200.0元
        alipay.refund(200); // 输出:默认退款逻辑:退款金额200.0元(未指定支付渠道)
    }
}

核心亮点

  • 兼容性:接口新增 default 方法,原有实现类无需修改,直接使用默认逻辑;
  • 灵活性:实现类可根据自身需求重写 default 方法;
  • 工具化:static 方法可作为接口通用工具,避免创建额外工具类。

五、场景 5:虚拟线程(Java 21 新特性,轻量级并发编程)

特性说明

Java 21 正式引入虚拟线程(Virtual Thread),属于 "用户态线程",无需绑定操作系统内核线程,创建成本极低(可创建百万级线程),彻底解决传统线程 "资源占用高、切换成本高" 的问题。

问题场景

需要并发处理 1000 个 HTTP 请求(或 IO 密集型任务),传统线程池(ThreadPoolExecutor)受限于核心线程数,并发效率低;虚拟线程可轻松应对百万级 IO 密集型任务。

实战代码

java 复制代码
import java.util.concurrent.Executors;

public class VirtualThreadDemo {
    public static void main(String[] args) {
        // 1. 创建虚拟线程池(Java 21 新增 Executors.newVirtualThreadPerTaskExecutor())
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 2. 提交 1000 个任务(每个任务模拟 1 秒 IO 等待)
            for (int i = 0; i < 1000; i++) {
                int taskId = i;
                executor.submit(() -> {
                    // 模拟 IO 任务(如 HTTP 请求、数据库查询)
                    try {
                        Thread.sleep(1000); // 睡眠 1 秒,模拟 IO 等待
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                    System.out.println("虚拟线程完成任务:" + taskId + ",线程名:" + Thread.currentThread().getName());
                });
            }
        } // 自动关闭线程池,无需手动 shutdown()

        System.out.println("所有任务提交完成,等待执行...");
        // 输出:1 秒后批量打印 1000 个任务完成信息(虚拟线程并发执行,无阻塞)
    }
}

核心亮点

  • 低资源消耗:虚拟线程创建成本仅为传统线程的千分之一,支持百万级并发;
  • 简化 API:newVirtualThreadPerTaskExecutor() 一键创建虚拟线程池,无需配置核心线程数;
  • 自动管理:try-with-resources 语法自动关闭线程池,避免资源泄漏;
  • 适配 IO 密集型:虚拟线程在 IO 等待时会自动 "挂起",释放资源给其他线程,大幅提升并发效率。
相关推荐
资生算法程序员_畅想家_剑魔2 小时前
算法-回溯-14
java·开发语言·算法
w_zero_one2 小时前
Java的Vert.x框架结合Thymeleaf(TH)模板语言
java·开发语言·idea
2301_800050992 小时前
ceph分布式存储
笔记·分布式·ceph
咸鱼2.02 小时前
【java入门到放弃】网络
java·开发语言·网络
YJlio2 小时前
Contig 学习笔记(13.5):整理现有文件碎片的策略与批量实战
笔记·学习·stable diffusion
阿里嘎多学长2 小时前
2025-12-28 GitHub 热点项目精选
开发语言·程序员·github·代码托管
Roye_ack2 小时前
【微服务 Day2】SpringCloud实战开发(微服务拆分步骤 + Nacos注册中心 + OpenFeign + 微服务拆分作业)
java·spring cloud·微服务·nacos·openfeign
wniuniu_2 小时前
blob是啥
java·服务器·网络
.生产的驴2 小时前
DockerCompoe 部署注册中心Nacos 一键部署 单机+Mysql8
java·linux·运维·spring boot·缓存·docker·doc