1.你知道 Java 8 有什么新特性吗?

lambda 表达式,stream 流,异步调用 接口可以写默认实现

Java 8 是 Java 历史上一个非常重要的版本,引入了很多革新特性。最核心的有以下几点:

  1. Lambda 表达式:它极大地简化了匿名内部类的写法,让我们能用函数式编程的思想来写代码,代码更紧凑。
// 旧写法
new Thread(new Runnable() {
    @Override
    public void run() { System.out.println("Old"); }
}).start();
 
// Lambda 写法
new Thread(() -> System.out.println("New")).start();
  1. Stream API:这是我平时用得最多的。它让集合的操作变得像写 SQL 一样流畅,支持链式处理,比如 filter 过滤、map 转换,还能通过 parallelStream 轻松利用多核 CPU 进行并行计算。

  2. Optional:用来优雅地解决空指针异常,避免了代码里到处写 if (obj != null)

  3. CompletableFuture:提供了强大的异步编程能力,支持任务编排和回调,比以前的 Future 好用很多。

  4. 接口默认方法:允许在接口里写默认实现,这增强了接口的扩展性。

  5. 还有像新的日期时间 API,它是线程安全的,解决了旧版 Date 的很多坑。

2.Lambda 表达式了解吗?

1. 什么是 Lambda 表达式?

一句话总结: Lambda 表达式本质上是一个 “匿名函数”(没有名字的函数)。

  • 核心目的: 它可以把“一段代码”像数据一样传递给方法。

  • 直观感受: 它是对 匿名内部类 的“语法糖”优化,让代码变得极其简洁。

2. 为什么要用它?(核心优势)

  1. 代码简洁: 告别了啰嗦的 new Runnable() { ... } 这种样板代码。

  2. 行为参数化: 可以把“逻辑”当作参数传进方法里(比如传一个排序逻辑给 sort 方法)。

  3. 支持并行处理: 它是 Java 8 另一大杀器 Stream API 的基础。

3. 使用前提:函数式接口 (Functional Interface)

面试必考点: Lambda 表达式只能用于实现 “函数式接口”

  • 定义: 只有一个抽象方法的接口。

  • 注解: 通常用 @FunctionalInterface 标记(不是必须的,但加上了编译器会帮你检查)。

  • 常见例子: RunnableCallableComparator

接口名抽象方法参数返回值作用描述记忆口诀
Consumer<T>void accept(T t)消费型: 给你个东西,你把它吃了(打印、写入数据库)。只进不出
Supplier<T>T get()供给型: 没参数,你得给我变个东西出来(工厂方法)。只出不进
Function<T, R>R apply(T t)函数型: 给你 T,你把它变成 R(数据转换,如 String 转 int)。有进有出
Predicate<T>boolean test(T t)boolean断言型: 给你个东西,你告诉我它是对是错(过滤数据)。判官
  • 调试困难

3.Java中stream的API介绍一下

如果说 Collection(集合) 是用来数据的,那么 Stream(流) 就是用来数据的。

1. 什么是 Stream 流?

一句话总结: Stream 就像一条**“流水线”。 它不是数据结构,不保存数据,而是负责对数据进行加工处理**(过滤、映射、排序等)。

  • 特性:

    1. 不存储数据: 它只是搬运工,数据源还是 List 或 Set。

    2. 不改变源数据: 所有的操作(比如过滤)都会产生一个新的流,原集合不受影响。

    3. 延迟执行 (Lazy): 只有当你要结果(调用终端操作)的时候,流水线才会真正开始转动。

    4. 一次性消耗: 流水线走完了就没了,不能重复使用(想再操作必须重新生成一个流)。

2. Stream 的操作流程 (三步走)

面试官常问:“怎么用 Stream?” 回答这三步即可:

第一步:创建流 (Source)

  • 从集合创建:list.stream()

  • 从数组创建:Arrays.stream(arr)

  • 直接创建:Stream.of("a", "b", "c")

第二步:中间操作 (Intermediate Operations)

  • 特点: 返回值依然是 Stream,可以链式调用。它是懒加载的,只有最后一步触发时才会执行。

  • 常用方法:

    • filter(Predicate)过滤(挑出符合条件的)。

    • map(Function)转换(把苹果变成苹果汁,把 String 变成 int)。

    • sorted()排序

    • distinct()去重

    • limit(n) / skip(n)截取跳过

第三步:终端操作 (Terminal Operations)

  • 特点: 返回值不是 Stream(是具体的结果或 void),会触发整个流水线的执行。

  • 常用方法:

    • forEach(Consumer)遍历消费。

    • collect(Collectors.toList())收集结果(转回 List, Set, Map)。

    • count()统计个数。

    • findFirst() / anyMatch()查找匹配

3. 通俗例子:筛选简历

假设你有一个 List<Resume>(简历列表),你要做以下操作:

  1. 过滤出“本科以上”学历的;

  2. 转换,只保留“姓名”字段;

  3. 限制只看前 5 份;

  4. 收集成一个 List。

List<String> result = resumeList.stream()                 // 1. 上流水线
    .filter(r -> r.getDegree().equals("Bachelor"))        // 2. 中间操作:过滤
    .map(Resume::getName)                                 // 3. 中间操作:转换 (只要名字)
    .limit(5)                                             // 4. 中间操作:截取
    .collect(Collectors.toList());                        // 5. 终端操作:打包带走

4.Stream流的并行API是什么?

  • 定义: list.parallelStream()

  • 原理: 利用多核 CPU,把大数据拆分成多个小块(Fork/Join 框架),同时处理,最后合并结果。

  • 适用场景: 数据量非常大(几万条以上)且任务计算密集时。

  • 坑:

    1. 线程安全问题: 如果你在并行流里修改共享变量(比如往一个全局 List 里 add 数据),会出 Bug。

    2. 性能开销: 数据量小的时候,拆分线程的开销比直接算还要慢,别盲目用

5.completableFuture怎么用的?

在 Java 8 之前,我们主要靠 Future 来做异步,但它很难用(只能干等结果 get(),这会阻塞当前线程。而且很难解决“任务A做完自动做任务B”这种场景。)。Java 8 引入的 CompletableFuture 是对 Future 的重大升级,实现了异步任务的编排组合(Chain & Combine)。

CompletableFuture 的优势:

  • 非阻塞: 可以通过回调函数(Callback)在任务完成时自动触发后续操作,不需要死等。

  • 任务编排: 轻松实现“串行执行”、“并行执行”、“AND 汇聚”、“OR 汇聚”等复杂逻辑。

类别核心 API作用
创建supplyAsync开启异步任务(有返回值)
转换thenApply拿到结果,转换处理,返回新结果
消费thenAccept拿到结果,消费掉(无返回)
组合thenCombine任务 A + B 都完成,合并结果
竞争applyToEither任务 A 或 B 谁快用谁
全量allOf等待 N 个任务全部做完
异常exceptionally出了异常怎么办(兜底)

Java 21 是一个非常重要的 LTS 版本,最核心的特性肯定是 虚拟线程 (Virtual Threads)。它让 Java 实现了轻量级的用户态线程,大大降低了高并发编程的门槛和资源消耗,这在以前可能需要用复杂的响应式编程才能做到,现在用同步的代码风格就能实现高吞吐量。

其次是 集合框架的升级 (Sequenced Collections),它统一了 List、Set、Map 中关于“有序操作”的 API,比如现在可以直接用 getFirst()getLast(),不用再记各种不同的方法了。

还有就是语言语法的改进,比如 Switch 的模式匹配,现在的 Switch 可以直接匹配对象类型,还能处理 null 值,代码写起来比以前那种一长串的 if-else 优雅很多。