Java 8新特性
Java 8新特性
Java 8 是语言演进的分水岭,引入 Lambda 表达式、Stream API、Optional 类和新日期时间 API,让 Java 从纯面向对象向函数式编程迈出关键一步。
Java 8 核心特性:
Lambda 表达式
Lambda 允许将函数作为一等公民传递给方法,使用 -> 操作符,左侧参数、右侧方法体。
Runnable r = () -> System.out.println("hello lambda");
Consumer<String> c = (args) -> System.out.println("hello " + args);
Comparator<Integer> com = (a, b) -> Integer.compare(a, b);函数式接口
仅含一个抽象方法的接口,可用 Lambda 替代。java.util.function 包提供内置函数式接口:
| 接口 | 方法 | 描述 |
|---|---|---|
Predicate<T> | boolean test(T t) | 断言型接口 |
Consumer<T> | void accept(T t) | 消费型接口 |
Function<T,R> | R apply(T t) | 函数型接口 |
Supplier<T> | T get() | 供给型接口 |
类型推断
编译器通过上下文推断 Lambda 类型和签名:
// 完整写法
List<Apple> heavyApples = filter(inventory, (Apple a) -> a.getWeight() > 150);
// 类型推断(省略参数类型)
List<Apple> heavyApples = filter(inventory, a -> a.getWeight() > 150);局部变量限制
Lambda 可捕获实例变量和静态变量,但局部变量必须显式声明为 final 或事实上 final:
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber); // 正确
portNumber = 31337; // 编译错误:portNumber 非 final方法引用
Lambda 的快捷写法,用 :: 操作符连接目标引用和方法名。三种类型:
Function<String, Integer> parse = Integer::parseInt; // 静态方法
Function<String, Integer> length = String::length; // 任意类型实例方法
Predicate<String> isEmpty = str::isEmpty; // 现有对象实例方法构造函数引用:
Supplier<Apple> c1 = Apple::new; // 无参
Function<Integer, Apple> c2 = Apple::new; // 带参
BiFunction<String, Integer, Apple> c3 = Apple::new; // 多参Stream API
Stream 不是集合,不存储数据,而是对数据的计算描述。支持内部迭代和并行处理。
| 特性 | Collection | Stream |
|---|---|---|
| 迭代方式 | 外部迭代 | 内部迭代 |
| 元素访问 | 可多次访问 | 只能遍历一次 |
| 延迟执行 | 即时执行 | 延迟执行 |
| 并行能力 | 手动管理 | 自动并行 |
Stream 处理流程:
创建流
Stream<String> s1 = list.stream(); // 从集合创建
Stream<String> s2 = Stream.of("a", "b", "c"); // 从值创建
Stream<Integer> s3 = Stream.iterate(0, n -> n + 2).limit(10); // 无限流
Stream<Double> s4 = Stream.generate(Math::random).limit(5); // 生成器中间操作
中间操作返回新 Stream,可链式调用:
List<Apple> heavyApples = inventory.stream()
.filter(a -> a.getWeight() > 150) // 筛选
.distinct() // 去重
.limit(10) // 截断
.skip(5) // 跳过
.collect(toList());// 映射
list.stream().map(String::toUpperCase).collect(toList());
// 扁平化
words.stream()
.flatMap(s -> Arrays.stream(s.split(" ")))
.collect(toList());终端操作
// 收集
stream.collect(Collectors.toList());
stream.collect(Collectors.toSet());
stream.collect(Collectors.toMap(Function.identity(), v -> v));
// 聚合
stream.count();
stream.max(Comparator.naturalOrder());
// 匹配
stream.allMatch(Predicate.isEqual(target));
stream.anyMatch(Predicate.isEqual(target));
stream.noneMatch(Predicate.isEqual(target));
// 查找
stream.findFirst();
stream.findAny();并行流
List<String> result = list.parallelStream()
.filter(s -> s.length() > 3)
.collect(toList());并行流使用 Fork/Join 框架自动分块处理。注意:数据量足够大时才有效,避免在流操作中修改共享变量。
默认方法
接口中用 default 关键字定义默认方法实现,主要作用是向后兼容。Java 8 为 Collection 添加 stream() 方法,所有实现类无需修改即可使用。
多重继承规则:类的方法优先 → 子接口的方法优先 → 显式指定 InterfaceName.super.methodName()。
Optional
容器对象,替代 null 减少空指针异常:
// 创建
Optional<String> opt1 = Optional.of("value");
Optional<String> opt2 = Optional.empty();
Optional<String> opt3 = Optional.ofNullable(null);
// 使用
opt.ifPresent(v -> System.out.println(v));
String result = opt.orElse("default");
String result2 = opt.orElseGet(() -> "default");
String result3 = opt.orElseThrow(RuntimeException::new);
// 链式操作
String name = person
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");CompletableFuture
Future 的扩展,支持声明式异步编程:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "result");
CompletableFuture<String> result = future
.thenApply(String::toUpperCase)
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "!"))
.exceptionally(ex -> "error");新日期时间 API
java.time 包提供清晰易用的日期时间 API,替代 java.util.Date 和 Calendar:
LocalDate date = LocalDate.now();
LocalDateTime dt = LocalDateTime.now();
LocalDate parsed = LocalDate.parse("2024-01-15");
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// 操作
LocalDate tomorrow = date.plusDays(1);
Duration duration = Duration.between(start, end);
Period period = Period.between(date1, date2);
// 时区
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));函数式编程思想
| 原则 | 说明 |
|---|---|
| 行为参数化 | 将代码(Lambda)作为参数传递,替代条件分支和策略模式 |
| 不可变数据 | 倾向不可变操作(如 reduce),避免并发问题 |
| 纯函数 | 相同输入产生相同输出,无副作用,天然适合并行化 |
实战技巧
避免装箱开销:使用 IntStream、LongStream、DoubleStream 避免自动装箱。
int sum = list.stream().mapToInt(Integer::valueOf).sum(); // 原始类型流使用短路操作:findFirst、findAny、anyMatch 找到匹配即停止,避免遍历整个流。
Stream 调试:用 peek 在中间步骤插入调试输出。
list.stream()
.filter(...)
.peek(e -> System.out.println("Filtered: " + e))
.map(...)
.collect(toList());