掌握 JDK 8+ 的核心新特性和现代编程范式
Java 8 是里程碑版本,引入了 Lambda、Stream、Optional 等现代特性。本篇系统梳理 Java 新特性的核心知识。
本篇重点围绕 Java 新特性的核心知识,帮助你系统掌握:
Lambda 表达式:一种简洁的匿名函数表示方式,支持函数式编程。
// 传统写法:匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// Lambda 写法
Runnable r2 = () -> System.out.println("Hello");
// 带参数的 Lambda
Comparator comparator = (a, b) -> a - b;
// 多行 Lambda
Consumer consumer = s -> {
System.out.println("Processing: " + s);
System.out.println("Done");
}; 优势:
函数式接口:只有一个抽象方法的接口,可以用 Lambda 表达式实现。
// 自定义函数式接口
@FunctionalInterface
interface MyFunction {
int apply(int x, int y);
}
// 使用 Lambda 实现
MyFunction add = (x, y) -> x + y;
System.out.println(add.apply(3, 5)); // 8常用函数式接口(java.util.function):
// 1. Function:接收 T 返回 R
Function strLength = s -> s.length();
System.out.println(strLength.apply("hello")); // 5
// 2. Consumer:接收 T 无返回值
Consumer printer = s -> System.out.println(s);
printer.accept("Hello");
// 3. Supplier:无参数返回 T
Supplier random = () -> Math.random();
System.out.println(random.get());
// 4. Predicate:接收 T 返回 boolean
Predicate isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true
// 5. BiFunction:接收 T 和 U 返回 R
BiFunction add = (a, b) -> a + b;
System.out.println(add.apply(3, 5)); // 8 方法引用:Lambda 的简化写法,直接引用已有方法。
// 1. 静态方法引用:类名::静态方法
Function parseInt1 = s -> Integer.parseInt(s);
Function parseInt2 = Integer::parseInt; // 简化
// 2. 实例方法引用:对象::实例方法
String str = "hello";
Supplier upper1 = () -> str.toUpperCase();
Supplier upper2 = str::toUpperCase; // 简化
// 3. 类的实例方法引用:类名::实例方法
Function length1 = s -> s.length();
Function length2 = String::length; // 简化
// 4. 构造器引用:类名::new
Supplier> list1 = () -> new ArrayList<>();
Supplier> list2 = ArrayList::new; // 简化
Function array1 = size -> new int[size];
Function array2 = int[]::new; // 简化
Stream:对集合进行函数式操作的流式 API,支持链式调用。
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 示例1:过滤 + 映射 + 收集
List result = numbers.stream()
.filter(n -> n % 2 == 0) // 过滤偶数
.map(n -> n * n) // 平方
.collect(Collectors.toList()); // 收集结果
// 结果:[4, 16, 36, 64, 100]
// 示例2:求和
int sum = numbers.stream()
.filter(n -> n > 5)
.mapToInt(Integer::intValue)
.sum();
// 结果:40
// 示例3:分组
Map> grouped = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// 结果:{false=[1,3,5,7,9], true=[2,4,6,8,10]}
// 示例4:字符串拼接
String joined = numbers.stream()
.map(String::valueOf)
.collect(Collectors.joining(", "));
// 结果:"1, 2, 3, 4, 5, 6, 7, 8, 9, 10" map:一对一映射,每个元素映射为一个新元素。
flatMap:一对多映射,每个元素映射为一个流,然后扁平化。
// map 示例:一对一
List words = Arrays.asList("hello", "world");
List lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// 结果:[5, 5]
// flatMap 示例:一对多
List words = Arrays.asList("hello", "world");
List chars = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.collect(Collectors.toList());
// 结果:["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]
// 更复杂的例子:嵌套列表扁平化
List> nested = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
// 使用 flatMap 扁平化
List flat = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]
Optional:一个容器对象,可能包含或不包含非 null 值,用于优雅处理 null。
// 创建 Optional
Optional opt1 = Optional.of("hello"); // 不能为 null
Optional opt2 = Optional.ofNullable(null); // 可以为 null
Optional opt3 = Optional.empty(); // 空 Optional
// 判断是否有值
if (opt1.isPresent()) {
System.out.println(opt1.get());
}
// 推荐:使用 ifPresent
opt1.ifPresent(System.out::println);
// 提供默认值
String value1 = opt2.orElse("default"); // 有值返回值,无值返回默认
String value2 = opt2.orElseGet(() -> "default"); // 懒加载默认值
String value3 = opt2.orElseThrow(() -> new RuntimeException("No value"));
// 转换值
Optional length = opt1.map(String::length);
// 过滤
Optional filtered = opt1.filter(s -> s.length() > 3);
// 链式调用
String result = Optional.ofNullable(getUserName())
.map(String::toUpperCase)
.orElse("UNKNOWN"); 使用场景:
默认方法(default):接口中有具体实现的方法,实现类可以不重写。
静态方法(static):接口自己的工具方法,通过接口名调用。
interface MyInterface {
// 抽象方法
void abstractMethod();
// 默认方法(JDK 8+)
default void defaultMethod() {
System.out.println("Default implementation");
}
// 静态方法(JDK 8+)
static void staticMethod() {
System.out.println("Static method");
}
// 私有方法(JDK 9+)
private void privateMethod() {
System.out.println("Private helper");
}
}
// 使用
class MyClass implements MyInterface {
@Override
public void abstractMethod() {
System.out.println("Abstract implementation");
}
// 可以不重写 defaultMethod
}
MyClass obj = new MyClass();
obj.abstractMethod();
obj.defaultMethod(); // 调用默认方法
MyInterface.staticMethod(); // 调用静态方法新 API(java.time):JDK 8 引入,替代旧的 Date 和 Calendar。
// 1. LocalDate:日期(年月日)
LocalDate date = LocalDate.now();
LocalDate birthday = LocalDate.of(1990, 1, 1);
LocalDate tomorrow = date.plusDays(1);
// 2. LocalTime:时间(时分秒)
LocalTime time = LocalTime.now();
LocalTime noon = LocalTime.of(12, 0, 0);
// 3. LocalDateTime:日期时间
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime specific = LocalDateTime.of(2024, 1, 1, 12, 0);
// 4. ZonedDateTime:带时区的日期时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
// 5. Duration:时间间隔
Duration duration = Duration.between(time, noon);
long seconds = duration.getSeconds();
// 6. Period:日期间隔
Period period = Period.between(birthday, date);
int years = period.getYears();
// 7. DateTimeFormatter:格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2024-01-01 12:00:00", formatter);优势:
并行流(Parallel Stream):利用多核 CPU 并行处理数据,提高性能。
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 串行流
long sum1 = numbers.stream()
.mapToLong(Integer::longValue)
.sum();
// 并行流
long sum2 = numbers.parallelStream()
.mapToLong(Integer::longValue)
.sum();
// 或者
long sum3 = numbers.stream()
.parallel()
.mapToLong(Integer::longValue)
.sum();
// 性能对比
long start = System.currentTimeMillis();
IntStream.range(0, 1000000)
.parallel()
.map(i -> i * 2)
.sum();
long end = System.currentTimeMillis();
System.out.println("Time: " + (end - start) + "ms"); 使用场景:
var(JDK 10+):局部变量类型推断,编译器自动推断类型。
// 传统写法
String name = "Alice";
List list = new ArrayList<>();
Map map = new HashMap<>();
// 使用 var
var name = "Alice"; // 推断为 String
var list = new ArrayList(); // 推断为 ArrayList
var map = new HashMap(); // 推断为 HashMap
// 复杂类型
var stream = list.stream().filter(s -> s.length() > 3);
var result = stream.collect(Collectors.toList()); 限制:
// 错误示例
var x; // 错误:必须初始化
var y = null; // 错误:不能推断为 null
public var method() { } // 错误:不能用于返回值Record(JDK 14+):不可变数据类,自动生成构造器、getter、equals、hashCode、toString。
// 传统写法:需要大量样板代码
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) { /* ... */ }
@Override
public int hashCode() { /* ... */ }
@Override
public String toString() { /* ... */ }
}
// Record 写法:一行搞定
public record Person(String name, int age) { }
// 使用
Person person = new Person("Alice", 30);
System.out.println(person.name()); // Alice
System.out.println(person.age()); // 30
System.out.println(person); // Person[name=Alice, age=30]
// 自定义方法
public record Person(String name, int age) {
// 紧凑构造器
public Person {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
// 自定义方法
public String greeting() {
return "Hello, " + name;
}
}优势:
模块化系统(JDK 9+):将 JDK 和应用程序划分为模块,提高封装性和可维护性。
// module-info.java
module com.example.myapp {
// 导出包,供其他模块使用
exports com.example.myapp.api;
// 依赖其他模块
requires java.sql;
requires java.logging;
// 使用服务
uses com.example.myapp.spi.MyService;
// 提供服务实现
provides com.example.myapp.spi.MyService
with com.example.myapp.impl.MyServiceImpl;
}核心概念:
优势:
Switch 表达式(JDK 12+):增强的 switch,支持表达式、箭头语法、模式匹配。
// 传统 switch 语句
String day = "MONDAY";
String result;
switch (day) {
case "MONDAY":
case "FRIDAY":
case "SUNDAY":
result = "6";
break;
case "TUESDAY":
result = "7";
break;
default:
result = "8";
break;
}
// 新 switch 表达式(箭头语法)
String result = switch (day) {
case "MONDAY", "FRIDAY", "SUNDAY" -> "6";
case "TUESDAY" -> "7";
default -> "8";
};
// 多行代码块
String result = switch (day) {
case "MONDAY", "FRIDAY", "SUNDAY" -> {
System.out.println("Weekend or Monday");
yield "6";
}
case "TUESDAY" -> {
System.out.println("Tuesday");
yield "7";
}
default -> "8";
};以下题目来自阿里巴巴、字节跳动、腾讯、美团等知名企业的真实面试。
性能问题场景:
// ❌ 性能问题
long sum = numbers.parallelStream() // 小数据集用并行流
.map(n -> n * 2) // 装箱
.reduce(0, Integer::sum);
// ✅ 优化方案
long sum = numbers.stream()
.mapToInt(Integer::intValue) // 避免装箱
.map(n -> n * 2)
.sum();原因:
// ❌ 错误
int count = 0;
Runnable r = () -> System.out.println(count);
count++; // 编译错误
// ✅ 正确:使用数组或 AtomicInteger
int[] count = {0};
Runnable r = () -> {
count[0]++;
System.out.println(count[0]);
};关键区别:
Optional opt = Optional.of("hello");
// orElse:总是执行
String r1 = opt.orElse(expensiveOperation()); // 总是执行
// orElseGet:懒加载
String r2 = opt.orElseGet(() -> expensiveOperation()); // 不执行