1. 引言

本文整理了面试中常见的 Java 8 相关问题。Java 8 作为一次重大版本升级,引入了众多语言特性和库函数。这些新特性大多旨在让代码更简洁紧凑,同时也添加了 Java 之前从未支持过的新功能。

2. Java 8 基础知识

Q1. Java 8 新增了哪些特性?

主要新特性包括:

  • ✅ Lambda 表达式
  • ✅ 方法引用
  • ✅ Stream API
  • ✅ Optional 容器类
  • ✅ 函数式接口
  • ✅ 默认方法
  • ✅ Nashorn JavaScript 引擎
  • ✅ 新的日期时间 API

3. 方法引用

Q1. 什么是方法引用?

方法引用是 Java 8 引入的语法糖,用于直接引用方法而不调用它。本质上它是 Lambda 表达式的简化形式,能减少代码冗余。例如:

// 原始 Lambda
(o) -> o.toString();

// 方法引用写法
Object::toString();

方法引用通过双冒号 :: 分隔类/对象名和方法名,常见类型:

  • 构造器引用:String::new
  • 静态方法引用:String::valueOf
  • 绑定实例方法引用:str::toString
  • 非绑定实例方法引用:String::toString

Q2. String::valueOf 表达式的含义?

这是 String 类静态方法 valueOf 的方法引用,等价于 Lambda 表达式:

(s) -> String.valueOf(s)

4. Optional

Q1. 什么是 Optional?如何使用?

Optional 是 Java 8 新增的容器类,用于封装可能为空的值。它本质是一个包装器,可包含零个或一个元素。

核心特点:

  • ❌ 不直接包装 null,而是用 Optional.empty() 表示空值
  • ✅ 作为方法返回类型,强制调用者处理空值情况
  • ⚠️ 不建议用作类字段(未实现 Serializable

典型使用场景:

// 非空流
int min1 = Arrays.stream(new int[]{1, 2, 3, 4, 5})
  .min()
  .orElse(0); // 返回 1

// 空流
int min2 = Arrays.stream(new int[]{})
  .min()
  .orElse(0); // 返回 0

5. 函数式接口

Q1. 标准库中有哪些常用函数式接口?

java.util.function 包下的核心接口:

接口名 输入参数 返回值 说明
Function<T,R> 1个 接收参数返回结果
Consumer 1个 消费参数(副作用操作)
Supplier 无参生成结果
Predicate 1个 boolean 判断条件是否成立
BiFunction<T,U,R> 2个 双参数处理
BinaryOperator 2个 同类型双参数处理
UnaryOperator 1个 同类型单参数处理

Q2. 什么是函数式接口?定义规则是什么?

函数式接口是仅包含一个抽象方法的接口(默认方法不计入)。当需要该接口实例时,可用 Lambda 表达式替代。

核心规则:

  • ✅ 有且仅有一个抽象方法
  • ✅ 可包含多个默认方法或静态方法
  • ✅ 通常使用 @FunctionalInterface 注解(非强制但推荐)

示例对比:

// 传统匿名类写法
Thread thread = new Thread(new Runnable() {
    public void run() {
        System.out.println("Hello World!");
    }
});

// Lambda 写法
Thread thread = new Thread(() -> System.out.println("Hello World!"));

6. 默认方法

Q1. 什么是默认方法?何时使用?

默认方法是在接口中带实现的方法。主要用途:在不破坏已有实现类的前提下,向接口添加新功能

public interface Vehicle {
    public void move();
    
    default void hoot() {  // 默认方法
        System.out.println("peep!");
    }
}

典型场景:Java 8 为 Collection 接口添加 forEach 默认方法,避免所有实现类强制重写。

Q2. 以下代码能否编译?

@FunctionalInterface
public interface Function2<T, U, V> {
    public V apply(T t, U u);

    default void count() { // 默认方法不影响
        // increment counter
    }
}

可以编译。虽然有两个方法,但 count 是默认方法,接口仍只含一个抽象方法,符合函数式接口规范。

7. Lambda 表达式

Q1. 什么是 Lambda 表达式?作用是什么?

简单说,Lambda 是可作为对象传递的匿名函数。核心价值:

  • ✅ 替代匿名类,简化代码
  • ✅ 引入函数式编程风格
  • ✅ 使集合操作更简洁(配合 Stream API)

Q2. Lambda 表达式的语法和特性

基础语法:

parameters -> expression

关键特性:

  1. 类型声明可选(编译器自动推断):
    (int x) -> x * x;     // 显式类型
    x -> x * x;           // 隐式类型
    
  2. 圆括号可选(单参数时):
    x -> x.toString();    // 合法
    (x) -> x.toString();  // 合法
    
  3. 花括号可选(单语句时):
    x -> System.out.println(x);  // 合法
    x -> { System.out.println(x); } // 合法
    
  4. return 关键字可选(花括号内单表达式):
    (x, y) -> { return x + y; }  // 合法
    (x, y) -> { x + y; }         // 合法
    

8. Nashorn JavaScript

Q1. Java 8 中的 Nashorn 是什么?

Nashorn 是 Java 8 引入的 JavaScript 引擎,替代了旧版的 Rhino。优势:

  • ✅ 更符合 ECMAScript 规范
  • ✅ 显著提升运行性能
  • ✅ 更好的 Java 互操作性

Q2. 什么是 jjs?

jjs 是 Java 8 提供的命令行工具,用于直接执行 JavaScript 代码。示例:

jjs script.js

9. Stream API

Q1. 什么是 Stream?与集合有何区别?

Stream 是支持聚合操作的元素序列,核心差异:

特性 集合 (Collection) 流 (Stream)
数据存储 内存中存储数据 不存储数据,按需计算
遍历方式 外部迭代 (for/while) 内部迭代 (由 Stream 控制)
可修改性 可多次遍历/修改 只能遍历一次
执行模式 即时执行 惰性执行

典型用法:

int sum = Arrays.stream(new int[]{1, 2, 3})
  .filter(i -> i >= 2)
  .map(i -> i * 3)
  .sum(); // 15 (2*3 + 3*3)

Q2. 中间操作与终端操作的区别?

Stream 操作分为两类:

中间操作

  • ✅ 返回 Stream 对象,可链式调用
  • ✅ 惰性执行(触发终端操作时才计算)
  • ✅ 例如:filter(), map(), flatMap()

终端操作

  • ✅ 返回非 Stream 结果(如值/集合)
  • ✅ 触发整个流水线执行
  • ✅ 例如:forEach(), collect(), reduce()

关键验证代码:

// 无终端操作 - 不输出
System.out.println("无终端操作");
Arrays.stream(new int[] { 1, 2, 3 })
  .map(i -> {
      System.out.println("处理: " + i);
      return i * 2;
  });

// 有终端操作 - 触发输出
System.out.println("有终端操作");
Arrays.stream(new int[] { 1, 2, 3 })
  .map(i -> {
      System.out.println("处理: " + i);
      return i * 2;
  })
  .sum(); // 触发执行

输出结果:

无终端操作
有终端操作
处理: 1
处理: 2
处理: 3

Q3. map 与 flatMap 的区别?

核心差异:返回值类型

  • map:返回普通值(自动包装为 Stream)
  • flatMap:返回 Stream(自动解包)

典型场景:扁平化嵌套集合

Map<String, List<String>> people = new HashMap<>();
people.put("张三", Arrays.asList("13800138000", "13900139000"));
people.put("李四", Arrays.asList("13700137000", "13600136000"));

// 使用 flatMap 扁平化所有电话
List<String> phones = people.values().stream()
  .flatMap(Collection::stream)  // 将 List<String> 转为 String 流
  .collect(Collectors.toList());

Q4. 什么是 Stream 流水线?

Stream 流水线是通过链式操作组合数据处理流程的机制。核心组成:

  1. 数据源(集合/数组等)
  2. 中间操作链(0-N 个)
  3. 终端操作(1 个)

执行流程:

graph LR
A[数据源] --> B[中间操作1]
B --> C[中间操作2]
C --> D[终端操作]
D --> E[结果]

10. 日期时间 API

Q1. Java 8 的新日期时间 API 有何改进?

旧版 java.util.Date 存在严重缺陷:

  • ❌ 非线程安全
  • ❌ API 设计反直觉(年份从 1900 开始,月份 0-11)
  • ❌ 时区处理复杂

Java 8 在 java.time 包下提供全新 API:

  • ✅ 线程安全(不可变对象)
  • ✅ 清晰的 API 设计(LocalDate, LocalTime 等)
  • ✅ 更好的时区支持(ZonedDateTime
  • ✅ 与 Joda-Time 设计理念一致

11. 总结

本文梳理了 Java 8 面试中的高频问题,涵盖 Lambda、Stream、Optional 等核心特性。虽然 Java 已迭代多个版本,但 Java 8 引入的函数式编程思想仍是现代 Java 开发的基础。面试前务必掌握这些特性,避免在基础问题上踩坑。祝面试顺利!


原始标题:Java 8 Interview Questions(+ Answers)