Java 从 JDK8 开始迎来史诗级迭代,大幅简化冗余代码、优化编程模型、补齐原生短板,后续 JDK9-JDK21 持续迭代模块化、语法简化、轻量并发等核心能力。新版特性彻底改变了Java编码风格,是日常开发、代码重构、大厂面试的高频核心考点。
本文系统化七大核心新特性:Lambda表达式、Stream流式API、Optional空值处理、全新日期时间API、Java模块化、var类型推断、JDK21虚拟线程,从底层原理、语法规范、实战代码、易错坑点到面试真题全覆盖,完全对标往期Java笔记体系,适配刷题、背诵、工程开发。
一、Lambda 表达式(JDK8 核心)
Lambda 是 JDK8 推出的函数式编程语法,用于简化匿名内部类冗余代码,实现函数式传参、行为参数化,让代码更简洁、紧凑、可读性更强,是Stream流式编程的基础前置条件。
1.1 核心前提:函数式接口
Lambda 表达式只能用于函数式接口。
函数式接口定义:有且仅有一个抽象方法的接口,默认注解@FunctionalInterface 标识。
四大内置核心函数式接口:
-
Consumer<T>:消费型,无返回值、有入参
-
Supplier<T>:供给型,无入参、有返回值
-
Function<T,R>:函数型,有入参、有返回值
-
Predicate<T>:断言型,入参返回布尔值,用于条件过滤
1.2 Lambda 标准语法与简化规则
标准格式:(参数列表) -> {方法体}
简化规则:
1、参数只有一个,可省略括号;
2、方法体只有一行代码,可省略大括号、分号;
3、单行return语句,可省略return关键字。
// 1. 无参无返回
Runnable run = () -> System.out.println("线程执行");
// 2. 单参数消费
Consumer<String> consumer = str -> System.out.println(str);
// 3. 多参数带返回
Function<Integer, Integer> func = x -> x * 2;
// 4. 断言过滤
Predicate<Integer> predicate = num -> num > 10;
1.3 核心特性与易错坑点
1、Lambda 无类、无方法名,属于匿名函数;
2、Lambda 内部引用局部变量,变量默认被隐式 final 修饰,禁止二次赋值;
3、Lambda 不是语法糖,是编译期生成私有静态方法实现;
4、无法在 Lambda 内部修改外部基本类型变量,仅能读取。
本节面试真题
题目:Lambda表达式的使用前提?为什么局部变量不能二次赋值?
答案:必须基于函数式接口(单个抽象方法);Lambda编译时会捕获外部局部变量,变量隐式final化,为保证线程安全与数据一致性,禁止二次修改。
二、Stream API 流式编程(JDK8)
Stream 是 JDK8 配套 Lambda 的集合流式处理框架,彻底替代传统for循环遍历,通过链式调用实现集合的过滤、排序、映射、聚合、去重、统计等操作,代码极简、可读性极强,是日常开发最高频的新特性。
2.1 Stream 核心特点
1、不存储数据:Stream是数据视图,不修改原集合;
2、惰性执行:中间操作延迟执行,仅触发终止操作才会执行;
3、流式链式:支持链式编程,代码连贯简洁;
4、一次性使用:流关闭后不可重复使用。
2.2 三大操作阶段
1、创建流
// 集合创建流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// 并行流
Stream<String> parallelStream = list.parallelStream();
2、中间操作(惰性、可链式)
常用:filter过滤、map映射、sorted排序、distinct去重、limit截断、skip跳过
3、终止操作(触发执行、关闭流)
常用:collect收集、forEach遍历、count统计、max/min最值、anyMatch匹配
2.3 实战经典代码
// 过滤、映射、排序、收集
List<Integer> resList = list.stream()
.filter(num -> num > 10) // 过滤大于10的数字
.map(num -> num * 2) // 数值翻倍
.sorted() // 自然排序
.distinct() // 去重
.collect(Collectors.toList()); // 收集为集合
2.4 并行流特性
parallelStream() 基于ForkJoinPool实现多线程并行处理,大幅提升大数据量集合处理效率;适合无状态、无顺序依赖的计算场景,不适合有线程竞争、顺序敏感的业务。
本节面试真题
题目:Stream中间操作和终止操作的区别?
答案:中间操作惰性执行,仅记录操作逻辑、不真正执行,支持链式调用;终止操作触发流式计算,执行所有中间逻辑,执行后流关闭,不可复用。
三、Optional 空值处理(JDK8 根治空指针)
Optional 是 JDK8 提供的空安全容器类,专门解决 Java 最常见的 NullPointerException 空指针异常,替代繁琐的 if(null != obj) 判断,实现优雅的空值处理,规避线上空指针BUG。
3.1 核心创建方式
// 可为空
Optional<String> opt1 = Optional.ofNullable(null);
// 非空校验,传入null直接报错
Optional<String> opt2 = Optional.of("test");
// 空对象
Optional<String> empty = Optional.empty();
3.2 核心常用方法
安全获取:
orElse():为空返回默认值;orElseGet():为空执行函数获取默认值;orElseThrow():为空主动抛异常
空值判断:isPresent()非空判断、isEmpty()空判断(JDK11+)
映射过滤:map()值映射、filter()条件过滤
3.3 优雅实战写法
// 优雅规避空指针,替代多层if判空
String name = Optional.ofNullable(user)
.map(User::getName)
.orElse("默认姓名");
3.4 核心坑点
1、Optional 本身不杜绝空指针,必须使用安全方法取值,禁止直接get()(空值报错);
2、禁止将 Optional 作为成员变量、方法参数,仅用于返回值包装;
3、orElse 无论是否为空都会执行,orElseGet 为空才执行,性能更优。
本节面试真题
题目:Optional为什么能解决空指针?orElse和orElseGet区别?
答案:Optional封装对象,强制开发者做非空处理,杜绝直接null调用;orElse立即执行,orElseGet惰性执行,非空时orElseGet性能更高。
四、全新日期时间 API(JDK8 LocalDate/LocalTime)
JDK8 彻底重构老旧的 Date/Calendar 时间类,推出 java.time全新时间包,解决旧时间API线程不安全、可读性差、修改繁琐、时区混乱的痛点,是目前企业开发唯一标准时间API。
4.1 核心三大时间类
-
LocalDate:仅年月日,无时间,适合日期业务
-
LocalTime:仅时分秒,无日期,适合时间业务
-
LocalDateTime:年月日+时分秒,最全时间格式,最常用
4.2 核心特性
1、线程安全:不可变类,修改时间返回新对象,无并发问题;
2、方法极简:now()获取当前时间、of()指定时间、plus/minus增减时间、with()修改时间;
3、自带格式化、时间比较、时间间隔计算,无需工具类冗余封装。
4.3 实战代码示例
// 获取当前时间
LocalDateTime now = LocalDateTime.now();
// 指定时间
LocalDate date = LocalDate.of(2026, 6, 20);
// 时间增减
LocalDateTime nextDay = now.plusDays(1);
// 时间格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String timeStr = now.format(formatter);
4.4 新旧API核心区别
旧Date/Calendar:可变、线程不安全、月份从0开始、API晦涩;
新LocalDateTime:不可变、线程安全、语义清晰、格式规范、自带时区与间隔计算。
本节面试真题
题目:为什么推荐使用LocalDateTime替代Date?
答案:新时间类不可变、线程安全,解决旧API并发安全问题;语法语义清晰、操作便捷、格式化友好,彻底规避旧时间类月份偏移、修改混乱等坑点。
五、var 类型推断(JDK10)
JDK10 推出 var 局部变量类型推断,编译器根据赋值语句自动推导变量类型,无需手动声明类型,简化冗余变量定义,简化代码书写,不改变Java强类型本质。
5.1 基础语法
// 传统写法
String str = "java";
// var推断写法
var str = "java";
var num = 100;
var list = new ArrayList<String>();
5.2 核心限制(高频坑点)
1、仅支持局部变量,不支持成员变量、方法参数、返回值;
2、定义变量必须立即初始化,无法先声明后赋值;
3、不能用于null直接赋值(无法推断类型);
4、var不是关键字,是语法糖,不改变强类型特性,变量类型固定不可变。
本节面试真题
题目:var是弱类型吗?有什么使用限制?
答案:不是弱类型,Java依旧是静态强类型,var仅编译期类型推断;仅限局部变量、必须立即初始化、不能赋值null、不能用于全局变量与参数。
六、Java 模块化系统(JDK9)
JDK9 推出模块化系统(JPMS),打破传统JRE大包加载、类路径混乱、依赖模糊的问题,将JDK拆分为独立模块,实现模块隔离、依赖管控、权限控制、精简运行环境,是Java工程化、轻量化的核心升级。
6.1 核心作用
1、解决传统Classpath“类冲突、依赖混乱、无法隔离”问题;
2、精细化控制模块访问权限,实现封装隔离;
3、支持按需裁剪JDK,构建轻量化运行镜像;
4、明确模块依赖关系,规避隐性依赖。
6.2 核心规则
1、项目根目录新增 module-info.java 模块描述文件;
2、requires:声明依赖其他模块;
3、exports:导出当前模块指定包,供外部访问;
4、默认所有包私有,未导出包外部无法访问。
本节面试真题
题目:Java模块化的核心意义是什么?
答案:实现代码模块化隔离、依赖显式管控、权限精细化控制,解决传统Classpath依赖混乱、类冲突、无隔离的弊端,支持精简JDK打包,提升工程安全性与规范性。
七、虚拟线程(JDK21 重磅特性)
虚拟线程(Virtual Thread)是 JDK21 正式推出的轻量级用户线程,彻底颠覆传统平台线程模型,极低内存开销、无线程数量限制、无需线程池复用,是Java并发编程的革命性升级。
7.1 核心定义
传统线程(平台线程):绑定操作系统内核线程,重量级、内存开销大、数量受限、创建销毁成本高;
虚拟线程:JVM 层面模拟的轻量级线程,不绑定内核线程,由JVM调度,开销极小、支持百万级并发。
7.2 核心优势
1、超高并发:支持数十万、百万级线程,突破操作系统线程上限;
2、极低开销:单虚拟线程内存开销KB级别,几乎无栈内存浪费;
3、无需线程池:创建销毁成本极低,无需池化复用;
4、使用简单:API兼容传统Thread,无需修改原有并发逻辑。
7.3 基础使用示例
// 创建并启动虚拟线程
Thread.startVirtualThread(() -> {
System.out.println("虚拟线程执行");
});
// 批量创建虚拟线程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10000; i++) {
executor.submit(() -> System.out.println("任务执行"));
}
}
7.4 核心特性与场景
1、虚拟线程是守护线程,不阻塞程序退出;
2、适合IO密集型、阻塞型任务(接口调用、数据库、网络请求);
3、不适合CPU密集型任务(CPU密集依旧依赖平台线程池)。
本节面试真题
题目:虚拟线程和传统平台线程的区别?
答案:平台线程绑定OS内核线程、重量级、数量受限、开销大;虚拟线程由JVM调度、轻量级、无数量限制、开销极低,适配高并发IO阻塞场景,彻底解决传统线程池参数调优难题。
八、全文终极背诵总结(面试必背)
1、Lambda:基于函数式接口,简化匿名内部类,实现行为参数化,局部变量隐式final;
2、Stream:流式处理集合,中间操作惰性、终止操作触发执行,简化遍历与聚合逻辑;
3、Optional:空安全容器,杜绝空指针,优先使用orElseGet惰性取值,禁止直接get();
4、新日期API:LocalDate/LocalDateTime线程安全、不可变,彻底替代不安全的Date类;
5、var类型推断:编译期推断类型,不改变强类型,仅限局部变量且必须初始化;
6、模块化:JDK9模块隔离、依赖显式管控,解决Classpath混乱问题,提升工程规范性;
7、虚拟线程:JDK21轻量级线程,JVM调度、超高并发、低开销,适配IO密集高并发场景。