Java8 新特性
Java 8 的主要新特性,深入每个特性的背景、用法、实际应用场景,并提供代码示例,力求全面且易懂。Java 8(2014年3月发布)是 Java 语言的一次重大升级,引入了函数式编程、现代化 API 和性能优化,极大地提升了开发效率和代码可读性。以下是详细内容:
1. Lambda 表达式
背景:
Lambda 表达式是 Java 8 引入的函数式编程核心特性,旨在减少样板代码,允许将行为作为参数传递。之前,Java 处理匿名函数(如匿名内部类)时代码冗长,Lambda 简化了这一过程。
特性:
- 语法:
(参数) -> 表达式
或(参数) -> { 语句块; }
。 - 无需显式声明参数类型(编译器可推断)。
- 可访问外部 final 或“有效 final”变量(即未被修改的变量)。
- 用于实现函数式接口(只有一个抽象方法的接口,如
Runnable
,Comparator
)。
代码示例:
// 传统匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};
// Lambda 表达式
Runnable r2 = () -> System.out.println("Hello, World!");
// 带参数的 Lambda
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println("Hello, " + name));
应用场景:
- 简化集合操作(如
forEach
)。 - 实现事件监听器或回调函数。
- 与 Stream API 结合进行数据处理。
注意事项:
- Lambda 表达式不能抛出比函数式接口方法更广的受检异常。
- 性能上与匿名内部类相当,但代码更简洁。
2. Stream API
背景:
Stream API(java.util.stream
包)是为了更高效、声明式地处理集合数据而设计的,支持函数式操作(如过滤、映射、归约)。它借鉴了 Scala 和其他语言的流处理概念,并支持并行处理以利用多核 CPU。
特性:
- 流(Stream):表示元素序列,支持惰性求值(仅在终端操作时计算)。
- 操作类型:
- 中间操作(如
filter
,map
,sorted
):返回新 Stream,惰性执行。 - 终端操作(如
collect
,forEach
,reduce
):触发计算,返回结果或副作用。
- 中间操作(如
- 并行流:通过
parallelStream()
或parallel()
启用多线程处理。 - 常用方法:
filter
:筛选元素。map
:转换元素。flatMap
:展平嵌套结构。reduce
:归约操作(如求和)。collect
:将流转换为集合或其他结构。
代码示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 过滤偶数,映射为平方,收集到新列表
List<Integer> squares = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(squares); // 输出: [4, 16, 36]
// 并行流求和
int sum = numbers.parallelStream()
.reduce(0, Integer::sum);
System.out.println(sum); // 输出: 15
应用场景:
- 数据过滤、转换和聚合(如统计、排序)。
- 处理大数据集,尤其是并行流可提升性能。
- 替代传统循环,代码更简洁。
注意事项:
- 流只能使用一次,重复使用会抛
IllegalStateException
。 - 并行流需注意线程安全和性能开销(如小数据集可能更慢)。
- 避免在流中产生副作用(如修改外部变量)。
3. Optional 类
背景:NullPointerException
(NPE)是 Java 中常见问题,传统上通过显式 null
检查来规避。Optional
类(java.util.Optional
)提供了一种更优雅的方式来表示可能为空的值,鼓励开发者显式处理空值。
特性:
Optional
是一个容器,可能包含值或为空。- 常用方法:
of(T value)
:创建包含非空值的 Optional。ofNullable(T value)
:创建可能为空的 Optional。orElse(T other)
:值为空时返回默认值。orElseThrow()
:值为空时抛异常。ifPresent(Consumer)
:值存在时执行操作。map(Function)
:对值进行转换。
代码示例:
// 传统方式
String name = null;
if (name != null) {
System.out.println(name.toUpperCase());
} else {
System.out.println("Unknown");
}
// 使用 Optional
Optional<String> optionalName = Optional.ofNullable(name);
optionalName.map(String::toUpperCase)
.ifPresent(System.out::println);
System.out.println(optionalName.orElse("Unknown")); // 输出: Unknown
应用场景:
- 方法返回值可能为空时,使用
Optional
代替返回null
。 - 链式调用(如
map
)处理嵌套对象,避免多层null
检查。 - API 设计中明确表示返回值可能为空。
注意事项:
- 不要滥用
Optional
(如用作方法参数,通常不推荐)。 Optional
本身不是null
,但仍需正确处理空值逻辑。
4. 默认方法(Default Methods)
背景:
Java 接口传统上只能定义抽象方法,扩展接口时需修改所有实现类。默认方法允许在接口中提供默认实现,增强接口的扩展性,同时保持向后兼容性。
特性:
- 使用
default
关键字在接口中定义方法实现。 - 实现类可选择覆盖默认方法。
- 若实现类继承多个接口,且默认方法冲突,需显式重写。
代码示例:
interface Vehicle {
void start(); // 抽象方法
default void stop() { // 默认方法
System.out.println("Vehicle stopped");
}
}
class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car started");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.start(); // 输出: Car started
car.stop(); // 输出: Vehicle stopped
}
}
应用场景:
- 扩展现有接口(如 Java 8 中的
Collection
添加forEach
)。 - 提供可选功能,减少实现类的样板代码。
- 支持接口的演进,保持 API 兼容性。
注意事项:
- 默认方法冲突需通过显式重写解决(如
ClassName.super.method()
调用特定接口的默认方法)。 - 默认方法不能覆盖
Object
类的方法(如toString
)。
5. 方法引用(Method References)
背景:
方法引用是 Lambda 表达式的简写形式,用于引用已有方法,进一步简化代码。它提高了代码可读性,尤其在 Stream API 中。
特性:
- 四种形式:
- 静态方法引用:
ClassName::staticMethod
- 实例方法引用:
instance::instanceMethod
或ClassName::instanceMethod
- 构造方法引用:
ClassName::new
- 静态方法引用:
- 编译器自动推断方法签名,需与函数式接口兼容。
代码示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Lambda 表达式
names.forEach(name -> System.out.println(name));
// 方法引用
names.forEach(System.out::println);
// 构造方法引用
List<String> upperNames = names.stream()
.map(String::new) // 等价于 x -> new String(x)
.collect(Collectors.toList());
应用场景:
- 替代简单的 Lambda 表达式,提高代码可读性。
- 与 Stream API 结合,简化映射或归约操作。
- 创建对象实例(如工厂方法)。
注意事项:
- 方法引用的签名必须与函数式接口的方法匹配。
- 不能传递额外参数,需用 Lambda 表达式处理复杂逻辑。
6. 新的日期和时间 API(java.time 包)
背景:
旧的 java.util.Date
和 Calendar
类存在设计缺陷(如线程不安全、API 复杂)。Java 8 引入了基于 Joda-Time 的新 API(java.time
包),提供更直观、线程安全的日期时间处理。
特性:
- 核心类:
LocalDate
:仅日期(如 2025-08-01)。LocalTime
:仅时间(如 15:02:00)。LocalDateTime
:日期和时间(如 2025-08-01T15:02:00)。ZonedDateTime
:带时区的日期时间。Instant
:时间戳(机器时间)。
- 不可变性:所有类都是不可变的,操作返回新实例。
- 格式化:通过
DateTimeFormatter
自定义格式。 - 时间计算:支持
plus
,minus
,Period
,Duration
等。
代码示例:
// 获取当前日期
LocalDate today = LocalDate.now(); // 2025-08-01
LocalDate tomorrow = today.plusDays(1); // 2025-08-02
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String formatted = today.format(formatter); // 2025/08/01
// 计算时间间隔
LocalDate birthday = LocalDate.of(1990, 1, 1);
Period age = Period.between(birthday, today);
System.out.println("Age: " + age.getYears()); // 输出: Age: 35
// 处理时区
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("America/New_York"));
应用场景:
- 日期时间计算(如计算年龄、到期日)。
- 国际化应用中处理时区。
- 日志系统记录精确时间戳。
注意事项:
- 新 API 线程安全,适合并发环境。
- 与旧 API 互操作需使用
toInstant()
或from()
方法。
7. Nashorn JavaScript 引擎
背景:
Java 8 引入 Nashorn 引擎(javax.script
包),替代老旧的 Rhino 引擎,允许在 JVM 上运行 JavaScript 代码,性能更高且支持 ECMAScript 5.1。
特性:
- 通过
ScriptEngine
执行 JavaScript。 - 支持 Java 与 JavaScript 互操作(如调用 Java 方法)。
- 提供
jjs
命令行工具运行 JavaScript 文件。
代码示例:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("print('Hello from JavaScript!');");
// Java 调用 JavaScript
engine.eval("function add(a, b) { return a + b; }");
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("add", 2, 3);
System.out.println(result); // 输出: 5
应用场景:
- 嵌入式脚本执行(如动态配置)。
- 服务端 JavaScript 开发(早期 Node.js 替代方案)。
- 调试或快速原型开发。
注意事项:
- Nashorn 在 Java 11 中标记为废弃,Java 15 中移除,推荐使用 GraalVM。
- 性能不如现代 JavaScript 引擎(如 V8)。
8. 并行数组操作
背景:
Java 8 增强了 java.util.Arrays
类,添加并行操作方法,利用多核 CPU 提高数组处理性能。
特性:
parallelSort()
:并行排序数组。parallelSetAll()
:并行设置数组元素。parallelPrefix()
:并行计算前缀操作。
代码示例:
int[] array = {5, 2, 8, 1, 9};
Arrays.parallelSort(array);
System.out.println(Arrays.toString(array)); // 输出: [1, 2, 5, 8, 9]
Arrays.parallelSetAll(array, i -> i * 2);
System.out.println(Arrays.toString(array)); // 输出: [0, 2, 4, 6, 8]
应用场景:
- 大规模数组排序或初始化。
- 高性能计算任务。
注意事项:
- 小数组使用并行操作可能因线程开销更慢。
- 确保操作无副作用(如线程安全)。
9. 其他重要改进
9.1 类型注解
- Java 8 扩展了注解的使用范围,支持在任何类型使用(如方法参数、局部变量)。
- 提供
@Repeatable
注解,允许重复应用同一注解。 - 示例:
@TypeAnnotation List<@TypeAnnotation String> list;
- 应用:结合工具(如 Checker Framework)进行静态类型检查。
9.2 并发改进
CompletableFuture
:支持异步编程,类似 Promise,提供链式调用和异常处理。- 示例:
CompletableFuture.supplyAsync(() -> "Hello") .thenApply(s -> s + " World") .thenAccept(System.out::println); // 输出: Hello World
- 示例:
ConcurrentHashMap
改进:支持流式操作和新方法(如forEach
)。- StampedLock:提供比
ReentrantReadWriteLock
更灵活的读写锁。
9.3 JVM 改进
- 移除永久代(PermGen):替换为元空间(Metaspace),动态分配内存,减少
OutOfMemoryError
。 - 改进 JIT 编译器:优化 Lambda 表达式和 Stream 的性能。
- G1 垃圾收集器增强:成为默认 GC,优化大堆性能。
总结
Java 8 的新特性极大地改变了 Java 编程范式:
- Lambda 和 Stream 引入函数式编程,简化数据处理。
- Optional 提高空值处理安全性。
- 默认方法 增强接口灵活性。
- 新日期时间 API 提供现代化时间处理。
- 并发和 JVM 优化 提升性能。