JDK 1.8 新特性详解:新增类与常用方法
一、概述
Java Development Kit (JDK) 1.8(也称为 Java 8)是 Java 语言发展历程中的一个里程碑式版本,于 2014 年 3 月发布。它引入了多项革命性的新特性,彻底改变了 Java 开发者的编码方式和思维模式。这些新特性不仅大幅提升了代码的简洁性和可读性,还为 Java 语言注入了函数式编程的强大能力。
本文将详细介绍 JDK 1.8 中新增的类以及常用方法,帮助开发者全面理解并掌握这些重要特性。
二、Lambda 表达式:函数式编程的基石
2.1 核心概念
Lambda 表达式是 Java 8 引入的最重要特性之一,它允许我们将一段代码(函数)作为参数传递给方法,而无需创建匿名内部类。Lambda 表达式的本质是函数式接口的实例。
函数式接口 :只包含一个抽象方法的接口,例如Runnable、Comparator等。
2.2 语法结构
Lambda 表达式的基本语法格式为:
(参数列表) -> {方法体}
语法简化规则:
- 参数列表的数据类型可以省略(类型推断)
- 如果参数列表只有一个参数,括号可以省略
- 如果方法体只有一条语句,大括号和分号可以省略
- 如果方法体有返回值且只有一条语句,
return关键字可以省略
2.3 常用示例
线程创建:
// 传统方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello, Java 8!");
}
}).start();
// Lambda表达式
new Thread(() -> System.out.println("Hello, Java 8!")).start();
集合排序:
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 传统方式
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
// Lambda表达式
Collections.sort(list, (s1, s2) -> s1.length() - s2.length());
// 更简洁的方式(Java 8新增的List.sort()方法)
list.sort((s1, s2) -> s1.length() - s2.length());
2.4 核心特性
- 简洁性:大幅减少代码量,提高可读性
- 延迟执行:只有在被调用时才会执行
- 类型推断:编译器自动推断参数类型
三、Stream API:声明式集合处理
3.1 概念与优势
Stream API 是 Java 8 中用于处理集合的新 API,它借鉴了函数式编程的思想,提供了一种声明式的集合操作方式。
核心优势:
- 代码更简洁:无需手动编写循环逻辑
- 可读性更强:专注于 "做什么" 而非 "怎么做"
- 支持并行处理:充分利用多核 CPU 性能
3.2 操作流程
Stream API 的操作流程分为三个步骤:
- 创建 Stream :通过集合的
stream()或parallelStream()方法 - 中间操作:对 Stream 中的数据进行处理(惰性执行)
- 终止操作:触发计算并返回结果
3.3 常用中间操作
| 方法 | 功能描述 |
|---|---|
filter(Predicate<T> predicate) |
过滤符合条件的元素 |
map(Function<T, R> mapper) |
将元素映射为另一种类型 |
sorted() |
自然排序 |
sorted(Comparator<T> comparator) |
自定义排序 |
distinct() |
去重 |
limit(long maxSize) |
限制元素数量 |
skip(long n) |
跳过前 n 个元素 |
3.4 常用终止操作
| 方法 | 功能描述 |
|---|---|
forEach(Consumer<T> action) |
遍历元素 |
collect(Collector<T, A, R> collector) |
收集结果到集合 |
count() |
统计元素数量 |
max(Comparator<T> comparator) |
求最大值 |
min(Comparator<T> comparator) |
求最小值 |
reduce(T identity, BinaryOperator<T> accumulator) |
归约操作 |
anyMatch(Predicate<T> predicate) |
是否有任意元素匹配 |
allMatch(Predicate<T> predicate) |
是否所有元素都匹配 |
noneMatch(Predicate<T> predicate) |
是否没有元素匹配 |
3.5 实战示例
筛选偶数并计算平方和:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 筛选偶数
.map(n -> n * n) // 计算平方
.reduce(0, Integer::sum); // 计算总和
System.out.println(sum); // 输出 120
复杂业务场景处理:
List<Order> orders = Arrays.asList(
new Order("O001", "张三", 1200, LocalDate.of(2025, 2, 10)),
new Order("O002", "李四", 800, LocalDate.of(2024, 12, 5)),
new Order("O003", "王五", 1500, LocalDate.of(2025, 3, 15)),
new Order("O004", "赵六", 2000, LocalDate.of(2025, 1, 20))
);
LocalDate thresholdDate = LocalDate.of(2025, 1, 1);
Map<String, String> resultMap = orders.stream()
// 筛选2025年1月1日后创建且金额大于1000的订单
.filter(order -> order.getCreateTime().isAfter(thresholdDate) && order.getAmount() > 1000)
// 按金额降序排序
.sorted((o1, o2) -> Double.compare(o2.getAmount(), o1.getAmount()))
// 提取订单编号和客户姓名,收集到Map
.collect(Collectors.toMap(Order::getOrderId, Order::getCustomerName));
四、Optional 类:优雅处理 null 值
4.1 设计目的
Optional类是一个可以为 null 的容器对象,设计用来帮助开发者更好地处理可能为 null 的值,从而避免空指针异常(NullPointerException)。
4.2 常用方法
| 方法 | 功能描述 |
|---|---|
Optional.of(T value) |
创建 Optional 实例(value 不能为 null) |
Optional.ofNullable(T value) |
创建 Optional 实例(value 可以为 null) |
Optional.empty() |
返回空的 Optional 实例 |
isPresent() |
判断值是否存在 |
get() |
获取值(值不存在时抛出异常) |
orElse(T other) |
值存在则返回该值,否则返回 other |
orElseGet(Supplier<? extends T> other) |
值存在则返回该值,否则返回 Supplier 提供的值 |
orElseThrow(Supplier<? extends X> exceptionSupplier) |
值存在则返回该值,否则抛出指定异常 |
ifPresent(Consumer<? super T> consumer) |
值存在时执行 Consumer 操作 |
map(Function<? super T, ? extends U> mapper) |
映射值 |
flatMap(Function<? super T, Optional<U>> mapper) |
扁平化映射 |
4.3 使用示例
public class OptionalExample {
public static void main(String[] args) {
String name = null;
// 使用Optional.ofNullable避免NullPointerException
Optional<String> optionalName = Optional.ofNullable(name);
// 判断是否存在
if (optionalName.isPresent()) {
System.out.println(optionalName.get());
} else {
System.out.println("Name is not present.");
}
// 使用orElse提供默认值
String defaultName = optionalName.orElse("Default Name");
System.out.println(defaultName);
// 使用orElseGet提供默认值
String defaultName2 = optionalName.orElseGet(() -> "Default Name from Supplier");
System.out.println(defaultName2);
// 使用orElseThrow抛出异常
try {
String requiredName = optionalName.orElseThrow(() ->
new IllegalArgumentException("Name must be provided"));
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
// 使用ifPresent
optionalName.ifPresent(System.out::println);
}
}
五、新日期时间 API:java.time 包
5.1 设计理念
Java 8 引入了全新的日期时间 API(JSR 310),解决了旧版日期时间类(java.util.Date、java.util.Calendar)存在的诸多问题:
- 不可变性:所有核心类都是不可变的
- 领域驱动设计:精确建模不同的日期时间概念
- 线程安全:所有类都是线程安全的
- 支持 ISO 8601 标准
5.2 核心类
5.2.1 LocalDate
表示日期(年、月、日),不包含时间信息。
常用方法:
LocalDate.now():获取当前日期LocalDate.of(int year, int month, int dayOfMonth):创建指定日期LocalDate.of(int year, Month month, int dayOfMonth):使用枚举创建日期getYear():获取年份getMonth():获取月份(枚举)getMonthValue():获取月份(数字)getDayOfMonth():获取日期getDayOfWeek():获取星期几plusDays(long daysToAdd):增加天数minusMonths(long monthsToSubtract):减少月份isAfter(ChronoLocalDate other):判断是否在指定日期之后isBefore(ChronoLocalDate other):判断是否在指定日期之前format(DateTimeFormatter formatter):格式化日期
5.2.2 LocalTime
表示时间(时、分、秒、纳秒),不包含日期信息。
常用方法:
LocalTime.now():获取当前时间LocalTime.of(int hour, int minute):创建指定时间LocalTime.of(int hour, int minute, int second):创建指定时间getHour():获取小时getMinute():获取分钟getSecond():获取秒plusHours(long hoursToAdd):增加小时minusMinutes(long minutesToSubtract):减少分钟
5.2.3 LocalDateTime
表示日期和时间,不包含时区信息。
常用方法:
LocalDateTime.now():获取当前日期时间LocalDateTime.of(LocalDate date, LocalTime time):创建指定日期时间toLocalDate():转换为 LocalDatetoLocalTime():转换为 LocalTimeplusWeeks(long weeksToAdd):增加周数
5.2.4 ZonedDateTime
表示带时区的日期时间。
常用方法:
ZonedDateTime.now():获取当前带时区的日期时间ZonedDateTime.now(ZoneId zone):获取指定时区的日期时间withZoneSameInstant(ZoneId zone):转换时区getZone():获取时区
5.2.5 Instant
表示时间戳(从 1970-01-01T00:00:00Z 开始的秒数)。
常用方法:
Instant.now():获取当前时间戳toEpochMilli():转换为毫秒数plusMillis(long millisToAdd):增加毫秒数
5.2.6 Period 和 Duration
Period:表示日期间隔(年、月、日)Duration:表示时间间隔(时、分、秒、纳秒)
5.3 使用示例
import java.time.*;
import java.time.format.DateTimeFormatter;
public class DateTimeExample {
public static void main(String[] args) {
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("Today's date is " + today);
// 获取当前时间
LocalTime now = LocalTime.now();
System.out.println("Current time is " + now);
// 获取当前日期时间
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println("Current date and time is " + nowDateTime);
// 设置特定日期
LocalDate birthday = LocalDate.of(1990, Month.JANUARY, 1);
System.out.println("Birthday is " + birthday);
// 设置特定时间
LocalTime meetingTime = LocalTime.of(13, 30);
System.out.println("Meeting time is " + meetingTime);
// 设置特定日期时间
LocalDateTime appointment = LocalDateTime.of(2023, Month.APRIL, 15, 10, 0);
System.out.println("Appointment is " + appointment);
// 时区
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("Current date and time in New York is " + zonedDateTime);
// 时间戳
Instant instant = Instant.now();
System.out.println("Current timestamp is " + instant);
// 日期间隔
Period period = Period.between(LocalDate.of(2020, 1, 1), LocalDate.of(2021, 1, 1));
System.out.println("Period between dates is " + period);
// 时间间隔
Duration duration = Duration.between(LocalTime.of(12, 0), LocalTime.of(13, 30));
System.out.println("Duration between times is " + duration);
// 日期格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = nowDateTime.format(formatter);
System.out.println("Formatted date time: " + formattedDateTime);
}
}
六、接口增强:默认方法与静态方法
6.1 接口默认方法
Java 8 允许在接口中定义带有实现的方法,使用default关键字修饰。
语法:
public interface MyInterface {
default void myMethod() {
// 默认方法的实现代码
}
}
使用场景:
- 为接口添加新方法,不破坏已有代码兼容性
- 提供默认实现,减少实现类的工作量
示例:
public interface Shape {
double getArea();
default double getPerimeter() {
return 0;
}
}
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
6.2 接口静态方法
Java 8 允许在接口中定义静态方法。
语法:
public interface MyInterface {
static void myStaticMethod() {
// 静态方法的实现代码
}
}
使用场景:
- 提供与接口相关的工具方法
- 可以直接通过接口名调用
示例:
public interface DateUtils {
LocalDate getCurrentDate();
static long getDaysBetween(LocalDate date1, LocalDate date2) {
return ChronoUnit.DAYS.between(date1, date2);
}
}
// 使用
LocalDate date1 = LocalDate.of(2022, 3, 1);
LocalDate date2 = LocalDate.of(2022, 3, 4);
long days = DateUtils.getDaysBetween(date1, date2);
System.out.println("Days between: " + days); // 输出 3
七、并发编程增强
7.1 CompletableFuture:异步编程的利器
CompletableFuture是 Java 8 中引入的异步编程工具,实现了Future和CompletionStage接口。
常用方法:
7.1.1 创建 CompletableFuture
CompletableFuture.supplyAsync(Supplier<U> supplier):异步执行有返回值的任务CompletableFuture.runAsync(Runnable runnable):异步执行无返回值的任务CompletableFuture.completedFuture(U value):创建已完成的 CompletableFuture
7.1.2 结果处理方法
thenApply(Function<? super T,? extends U> fn):当任务完成时,应用函数到结果thenAccept(Consumer<? super T> action):当任务完成时,消费结果thenRun(Runnable action):当任务完成时,执行 RunnablethenCompose(Function<? super T,? extends CompletionStage<U>> fn):组合两个 CompletableFuturethenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn):组合两个结果exceptionally(Function<Throwable,? extends T> fn):处理异常whenComplete(BiConsumer<? super T,? super Throwable> action):完成时处理结果或异常
7.1.3 示例
// 异步执行任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Hello";
});
// 处理结果
future.thenApply(result -> result + " World")
.thenAccept(System.out::println)
.exceptionally(ex -> {
ex.printStackTrace();
return null;
});
7.2 StampedLock:更灵活的锁机制
StampedLock是 Java 8 中引入的一种新型锁,支持三种锁模式:
- 写锁(Write Lock):独占锁
- 悲观读锁(Read Lock):共享锁
- 乐观读锁(Optimistic Read):非阻塞锁
常用方法:
long writeLock():获取写锁long readLock():获取悲观读锁long tryOptimisticRead():尝试获取乐观读锁boolean validate(long stamp):验证乐观读锁是否有效void unlockWrite(long stamp):释放写锁void unlockRead(long stamp):释放悲观读锁
示例:
public class StampedLockExample {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
7.3 ConcurrentHashMap 增强
Java 8 对ConcurrentHashMap进行了重大改进,主要包括:
-
新的方法:
computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
-
性能优化:
- 使用 CAS 操作代替锁
- 红黑树代替链表存储冲突的键值对
示例:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// computeIfAbsent:键不存在时计算值
map.computeIfAbsent("key1", k -> 10);
// computeIfPresent:键存在时更新值
map.computeIfPresent("key1", (k, v) -> v + 5);
// merge:合并值
map.merge("key1", 3, Integer::sum);
八、方法引用:Lambda 的简化形式
8.1 概念
方法引用是 Lambda 表达式的一种简化形式,当 Lambda 表达式仅仅是调用一个已经存在的方法时,可以使用方法引用来简化代码。
8.2 四种类型
8.2.1 对象::实例方法
// Lambda表达式
Consumer<String> consumer = s -> System.out.println(s);
// 方法引用
Consumer<String> consumer = System.out::println;
8.2.2 类::静态方法
// Lambda表达式
Function<Integer, Integer> function = x -> Math.abs(x);
// 方法引用
Function<Integer, Integer> function = Math::abs;
8.2.3 类::实例方法
// Lambda表达式
Comparator<String> comparator = (s1, s2) -> s1.compareTo(s2);
// 方法引用
Comparator<String> comparator = String::compareTo;
8.2.4 类::new(构造函数引用)
// Lambda表达式
Supplier<List<String>> supplier = () -> new ArrayList<>();
// 方法引用
Supplier<List<String>> supplier = ArrayList::new;
8.3 使用场景
方法引用在 Stream API 中广泛使用:
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 使用方法引用遍历
list.forEach(System.out::println);
// 使用方法引用排序
list.sort(String::compareToIgnoreCase);
// 使用方法引用映射
List<Integer> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
九、其他重要新特性
9.1 Base64 编码
Java 8 在java.util.Base64包中提供了 Base64 编码和解码的支持。
常用方法:
Base64.getEncoder().encodeToString(byte[] src):编码Base64.getDecoder().decode(String src):解码
示例:
String originalInput = "test input";
String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes());
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes);
9.2 Nashorn JavaScript 引擎
Java 8 引入了 Nashorn JavaScript 引擎,替代了 Rhino 引擎。
示例:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
engine.eval("print('Hello, JavaScript!')");
} catch (ScriptException e) {
e.printStackTrace();
}
9.3 并行数组排序
Java 8 新增了Arrays.parallelSort()方法,支持并行排序。
示例:
int[] array = {5, 3, 8, 1, 2};
Arrays.parallelSort(array);
9.4 OptionalInt、OptionalLong、OptionalDouble
Java 8 为基本数据类型提供了 Optional 版本:
OptionalInt optionalInt = OptionalInt.of(10);
if (optionalInt.isPresent()) {
System.out.println(optionalInt.getAsInt());
}
OptionalLong optionalLong = OptionalLong.empty();
long value = optionalLong.orElse(0L);
OptionalDouble optionalDouble = OptionalDouble.of(3.14);
optionalDouble.ifPresent(System.out::println);
十、总结
JDK 1.8 引入的新特性彻底改变了 Java 的编程方式,主要体现在以下几个方面:
- 函数式编程支持:Lambda 表达式和 Stream API 让 Java 支持函数式编程风格
- 更好的 null 值处理:Optional 类提供了优雅的 null 值处理方式
- 现代化的日期时间 API:解决了旧 API 的诸多问题
- 接口增强:默认方法和静态方法提供了更好的接口设计方式
- 并发编程改进:CompletableFuture、StampedLock 等提供了更强大的并发工具
- 性能优化:ConcurrentHashMap 等类的性能大幅提升
掌握这些新特性对于 Java 开发者来说已经成为必备技能。它们不仅能让代码更加简洁、优雅,还能提高开发效率和程序性能。
在实际开发中,应该根据具体需求选择合适的特性,合理运用 Lambda 表达式、Stream API 等新特性,写出更加优雅、高效的 Java 代码。