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
关键特性:
- 类型声明可选(编译器自动推断):
(int x) -> x * x; // 显式类型 x -> x * x; // 隐式类型
- 圆括号可选(单参数时):
x -> x.toString(); // 合法 (x) -> x.toString(); // 合法
- 花括号可选(单语句时):
x -> System.out.println(x); // 合法 x -> { System.out.println(x); } // 合法
- 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 流水线是通过链式操作组合数据处理流程的机制。核心组成:
- 数据源(集合/数组等)
- 中间操作链(0-N 个)
- 终端操作(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 开发的基础。面试前务必掌握这些特性,避免在基础问题上踩坑。祝面试顺利!