本文详细介绍了 Java 8 核心新特性,涵盖语法、API、性能等多方面。包括 Lambda 表达式简化函数式接口实现,函数式接口为 Lambda 提供类型支持;Stream API 实现集合高效聚合操作,支持链式与并行处理;Optional 类避免空指针异常,接口新增默认与静态方法解决演化问题。还涉及方法引用、新日期时间 API、重复与类型注解、Nashorn 引擎、CompletableFuture、Predicate 断言及 Map 增强方法等,每个特性均附详细解析与代码示例,全面展现 Java 8 带来的编程体验提升。
1. Lambda表达式
Lambda表达式是一种匿名函数,允许将函数作为参数传递给方法,或作为返回值。它简化了函数式接口的实现,语法为(参数) -> 表达式
或(参数) -> { 代码块 }
。Lambda表达式的类型由上下文推断,无需显式声明。
示例:
java
import java.util.Arrays;
import java.util.List;
public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 传统匿名内部类方式
names.forEach(new java.util.function.Consumer<String>() {
@Override
public void accept(String name) {
System.out.println(name);
}
});
// Lambda表达式方式(简化写法)
names.forEach(name -> System.out.println(name));
}
}
2. 函数式接口
函数式接口是只包含一个抽象方法的接口(可包含多个默认方法或静态方法),通过@FunctionalInterface
注解标识(非必需,但可用于编译时校验)。Java 8内置了大量函数式接口(如Predicate
、Function
、Consumer
等),为Lambda表达式提供了类型支持。
示例:
java
import java.util.function.Predicate;
// 自定义函数式接口
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
public class FunctionalInterfaceExample {
public static void main(String[] args) {
// 使用自定义函数式接口
MathOperation addition = (a, b) -> a + b;
System.out.println("3 + 5 = " + addition.operate(3, 5));
// 使用内置函数式接口Predicate(判断参数是否满足条件)
Predicate<Integer> isPositive = num -> num > 0;
System.out.println("5 is positive? " + isPositive.test(5)); // 输出true
}
}
3. Stream API
Stream API是Java 8中处理集合的强大工具,它允许以声明式方式处理数据,支持链式操作和并行处理。Stream不是数据结构,而是来自数据源的元素序列,数据源可以是集合、数组或I/O通道等。Stream操作分为中间操作(返回新Stream,可链式调用,如filter
、map
)和终止操作(返回结果或副作用,如collect
、forEach
),中间操作具有惰性执行特性,仅在终止操作被调用时才会执行。
Stream API提供了丰富的操作方法,主要包括:
- 筛选与切片:
filter
、distinct
、limit
、skip
- 映射:
map
、flatMap
- 排序:
sorted
- 查找与匹配:
anyMatch
、allMatch
、noneMatch
、findFirst
、findAny
- 归约:
reduce
- 收集:
collect
(配合Collectors
工具类)
示例:
java
import java.util.*;
import java.util.stream.Collectors;
public class StreamDetailedExample {
public static void main(String[] args) {
// 准备测试数据:产品列表
List<Product> products = Arrays.asList(
new Product("Laptop", "Electronics", 999.99, 10),
new Product("Smartphone", "Electronics", 699.99, 25),
new Product("Shirt", "Clothing", 29.99, 50),
new Product("Pants", "Clothing", 49.99, 30),
new Product("Headphones", "Electronics", 199.99, 15),
new Product("Socks", "Clothing", 9.99, 100),
new Product("Laptop", "Electronics", 1299.99, 5) // 重复名称的产品
);
// 1. 筛选与切片操作
System.out.println("=== 筛选价格>100的电子产品 ===");
List<Product> expensiveElectronics = products.stream()
.filter(p -> "Electronics".equals(p.getCategory())) // 筛选电子产品
.filter(p -> p.getPrice() > 100) // 筛选价格>100
.distinct() // 去重(需重写Product的equals和hashCode)
.skip(1) // 跳过第一个元素
.limit(2) // 只取前2个元素
.collect(Collectors.toList());
expensiveElectronics.forEach(System.out::println);
// 2. 映射操作
System.out.println("\n=== 提取所有产品名称并转为大写 ===");
List<String> upperCaseNames = products.stream()
.map(Product::getName) // 提取名称(映射为String流)
.map(String::toUpperCase) // 转为大写
.collect(Collectors.toList());
System.out.println(upperCaseNames);
// 3. 排序操作
System.out.println("\n=== 按价格升序排序(价格相同按库存降序) ===");
List<Product> sortedProducts = products.stream()
.sorted(Comparator.comparingDouble(Product::getPrice) // 主排序:价格升序
.thenComparing(Comparator.comparingInt(Product::getStock).reversed())) // 次排序:库存降序
.collect(Collectors.toList());
sortedProducts.forEach(p -> System.out.printf("%.2f -> %s%n", p.getPrice(), p.getName()));
// 4. 查找与匹配
System.out.println("\n=== 查找与匹配示例 ===");
boolean hasCheapClothing = products.stream()
.anyMatch(p -> "Clothing".equals(p.getCategory()) && p.getPrice() < 10); // 是否有便宜服装
System.out.println("有价格<10的服装? " + hasCheapClothing);
boolean allElectronicsExpensive = products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.allMatch(p -> p.getPrice() > 100); // 所有电子产品都>100?
System.out.println("所有电子产品价格都>100? " + allElectronicsExpensive);
Optional<Product> firstExpensiveProduct = products.stream()
.filter(p -> p.getPrice() > 1000)
.findFirst(); // 查找第一个价格>1000的产品
firstExpensiveProduct.ifPresent(p -> System.out.println("第一个高价产品:" + p.getName()));
// 5. 归约操作
System.out.println("\n=== 归约操作示例 ===");
// 计算所有电子产品的总库存
int totalElectronicsStock = products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.mapToInt(Product::getStock) // 转为IntStream
.sum(); // 求和(简化的归约)
System.out.println("电子产品总库存:" + totalElectronicsStock);
// 计算所有产品的总价值(价格*库存)
double totalValue = products.stream()
.mapToDouble(p -> p.getPrice() * p.getStock())
.reduce(0.0, Double::sum); // 归约求和
System.out.printf("所有产品总价值:%.2f%n", totalValue);
// 6. 收集操作(高级分组与统计)
System.out.println("\n=== 高级收集操作 ===");
// 按类别分组,并统计每个类别的产品数量
Map<String, Long> productsByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.counting() // 下游收集器:计数
));
System.out.println("按类别分组的产品数量:" + productsByCategory);
// 按类别分组,并计算每个类别的平均价格
Map<String, Double> avgPriceByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.averagingDouble(Product::getPrice) // 下游收集器:计算平均值
));
System.out.println("按类别分组的平均价格:" + avgPriceByCategory);
// 7. 并行流操作(处理大数据时提高效率)
System.out.println("\n=== 并行流操作 ===");
long startTime = System.currentTimeMillis();
// 并行计算所有产品价格总和(模拟大数据量场景)
double parallelSum = products.parallelStream() // 获取并行流
.mapToDouble(Product::getPrice)
.sum();
long endTime = System.currentTimeMillis();
System.out.printf("并行流计算价格总和:%.2f(耗时:%dms)%n", parallelSum, (endTime - startTime));
}
// 产品类(用于测试)
static class Product {
private String name;
private String category;
private double price;
private int stock;
public Product(String name, String category, double price, int stock) {
this.name = name;
this.category = category;
this.price = price;
this.stock = stock;
}
// getter方法
public String getName() { return name; }
public String getCategory() { return category; }
public double getPrice() { return price; }
public int getStock() { return stock; }
// 重写equals和hashCode用于distinct()去重
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return Objects.equals(name, product.name) &&
Objects.equals(category, product.category);
}
@Override
public int hashCode() {
return Objects.hash(name, category);
}
@Override
public String toString() {
return String.format("%s (%s): $%.2f, 库存: %d", name, category, price, stock);
}
}
}
- 示例创建了
Product
类模拟实际业务对象,演示了Stream API在实际场景中的应用 - 包含7类核心操作,覆盖了Stream API的主要功能点
- 展示了中间操作(
filter
、map
、sorted
等)和终止操作(collect
、sum
、findFirst
等)的配合使用 - 演示了
Collectors
工具类的高级用法,如分组统计、聚合计算等 - 包含并行流(
parallelStream()
)的使用,适合处理大数据量时提高效率 - 所有操作均采用链式调用,代码简洁且可读性强,体现了声明式编程的优势
4. Predicate断言
Predicate<T>
是Java 8提供的核心函数式接口之一,用于表示一个参数的布尔值判断(断言),包含唯一抽象方法test(T t)
。它常与Stream的filter()
方法结合使用,用于筛选符合条件的元素。此外,Predicate
还提供了默认方法进行逻辑组合,如and()
(与)、or()
(或)、negate()
(非),可实现复杂条件的拼接,避免编写冗余的Lambda表达式。
示例:
java
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. 基本使用:筛选偶数
Predicate<Integer> isEven = n -> n % 2 == 0;
List<Integer> evenNumbers = numbers.stream()
.filter(isEven)
.collect(Collectors.toList());
System.out.println("偶数: " + evenNumbers); // 输出 [2, 4, 6, 8, 10]
// 2. 逻辑组合:筛选大于5的偶数(even AND greaterThan5)
Predicate<Integer> greaterThan5 = n -> n > 5;
Predicate<Integer> evenAndGreaterThan5 = isEven.and(greaterThan5);
List<Integer> result1 = numbers.stream()
.filter(evenAndGreaterThan5)
.collect(Collectors.toList());
System.out.println("大于5的偶数: " + result1); // 输出 [6, 8, 10]
// 3. 逻辑非:筛选非偶数(即奇数)
Predicate<Integer> isOdd = isEven.negate();
List<Integer> oddNumbers = numbers.stream()
.filter(isOdd)
.collect(Collectors.toList());
System.out.println("奇数: " + oddNumbers); // 输出 [1, 3, 5, 7, 9]
// 4. 复杂组合:筛选能被2或3整除,但不能被6整除的数
Predicate<Integer> divisibleBy2 = n -> n % 2 == 0;
Predicate<Integer> divisibleBy3 = n -> n % 3 == 0;
Predicate<Integer> divisibleBy6 = n -> n % 6 == 0;
Predicate<Integer> complexCondition = divisibleBy2.or(divisibleBy3).and(divisibleBy6.negate());
List<Integer> result2 = numbers.stream()
.filter(complexCondition)
.collect(Collectors.toList());
System.out.println("能被2或3整除,但不能被6整除: " + result2); // 输出 [2, 3, 4, 8, 9]
// 5. 自定义对象断言
List<Product> products = Arrays.asList(
new Product("Laptop", 999.99),
new Product("Mouse", 25.50),
new Product("Keyboard", 49.99)
);
Predicate<Product> isExpensive = p -> p.getPrice() > 100;
List<Product> expensiveProducts = products.stream()
.filter(isExpensive)
.collect(Collectors.toList());
System.out.println("高价产品: " + expensiveProducts.stream()
.map(Product::getName)
.collect(Collectors.toList())); // 输出 [Laptop]
}
static class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public double getPrice() { return price; }
}
}
5. Map集合增强
Java 8对Map
接口进行了大幅增强,新增了一系列实用方法,简化了Map的操作逻辑,减少了传统代码中的空指针检查和条件判断。主要新增方法包括:
forEach(BiConsumer)
:遍历Map的键值对,替代传统的entrySet().iterator()
循环getOrDefault(Object, V)
:获取键对应的值,若键不存在则返回默认值putIfAbsent(K, V)
:仅当键不存在时才放入键值对,避免覆盖已有值remove(Object, Object)
:仅当键存在且对应值匹配时才删除,避免误删replace(K, V)
:仅当键存在时才替换值replace(K, V, V)
:仅当键存在且当前值匹配时才替换为新值compute(K, BiFunction)
:根据键和当前值计算新值并更新(支持动态生成值)computeIfAbsent(K, Function)
:仅当键不存在时,根据键计算值并放入(适合初始化集合类型的值)computeIfPresent(K, BiFunction)
:仅当键存在时,根据键和当前值计算新值并更新
这些方法均为默认方法,不影响现有实现类,同时支持Lambda表达式简化代码。
示例:
java
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class MapNewFeaturesExample {
public static void main(String[] args) {
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 85);
scores.put("Bob", 90);
scores.put("Charlie", 78);
// 1. forEach:遍历键值对
System.out.println("=== 所有成绩 ===");
scores.forEach((name, score) -> System.out.println(name + ": " + score));
// 2. getOrDefault:获取值,不存在则返回默认值
int davidScore = scores.getOrDefault("David", 0);
System.out.println("\nDavid的成绩(默认值): " + davidScore); // 输出 0
// 3. putIfAbsent:键不存在时才放入
scores.putIfAbsent("Bob", 95); // Bob已存在,不替换
scores.putIfAbsent("David", 88); // David不存在,新增
System.out.println("Bob的成绩: " + scores.get("Bob")); // 输出 90
System.out.println("David的成绩: " + scores.get("David")); // 输出 88
// 4. remove:条件删除(键存在且值匹配)
boolean isRemoved = scores.remove("Charlie", 77); // 值不匹配,删除失败
System.out.println("Charlie是否被删除(值77): " + isRemoved); // 输出 false
isRemoved = scores.remove("Charlie", 78); // 值匹配,删除成功
System.out.println("Charlie是否被删除(值78): " + isRemoved); // 输出 true
// 5. replace:条件替换
scores.replace("Alice", 85, 87); // 仅当当前值为85时替换为87
scores.replace("Bob", 100); // 无论当前值如何,替换Bob的成绩为100(键存在)
System.out.println("\n替换后Alice的成绩: " + scores.get("Alice")); // 输出 87
System.out.println("替换后Bob的成绩: " + scores.get("Bob")); // 输出 100
// 6. compute:动态计算值
scores.compute("Alice", (name, score) -> score + 5); // Alice成绩+5
System.out.println("Alice加分后成绩: " + scores.get("Alice")); // 输出 92
// 7. computeIfAbsent:键不存在时计算并放入(适合初始化集合)
Map<String, Integer> studentAges = new HashMap<>();
studentAges.computeIfAbsent("Alice", name -> 20); // Alice不存在,新增20
studentAges.computeIfAbsent("Alice", name -> 21); // Alice已存在,不修改
System.out.println("\nAlice的年龄: " + studentAges.get("Alice")); // 输出 20
// 8. computeIfPresent:键存在时计算并更新
scores.computeIfPresent("David", (name, score) -> score - 2); // David成绩-2
System.out.println("David减分后成绩: " + scores.get("David")); // 输出 86
// 9. 统计操作(结合Stream)
Map<String, Integer> highScores = scores.entrySet().stream()
.filter(entry -> entry.getValue() >= 90)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
System.out.println("\n90分以上的学生: " + highScores); // 输出 {Alice=92, Bob=100}
}
}
- 示例覆盖了Map的所有新增核心方法,展示了如何通过这些方法简化代码
forEach
方法直接接收键值对处理器,替代了传统的迭代器遍历getOrDefault
和putIfAbsent
有效避免了空指针异常和重复初始化逻辑- 条件性的
remove
和replace
方法增强了操作的安全性,避免误操作 compute
系列方法支持动态计算值,特别适合需要根据现有值更新的场景(如计数器累加)- 结合Stream API可实现Map的复杂筛选和转换,体现了函数式编程的优势
6. Optional类
Optional<T>
是一个容器类,用于包装可能为null
的值,避免NullPointerException
。它提供了of()
(不允许null)、ofNullable()
(允许null)、isPresent()
(判断是否有值)、orElse()
(默认值)等方法,强制开发者显式处理null情况。
示例:
java
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
// ofNullable:允许参数为null
Optional<String> optionalName = Optional.ofNullable(getName());
// isPresent:判断是否有值
if (optionalName.isPresent()) {
System.out.println("Name: " + optionalName.get());
}
// orElse:如果为null则返回默认值
String name = optionalName.orElse("Unknown");
System.out.println("Name: " + name);
}
private static String getName() {
return null; // 模拟可能返回null的场景
}
}
7. 接口的默认方法
接口中可通过default
关键字定义具有实现的方法(默认方法),解决了接口演化问题(无需修改实现类即可为接口新增方法)。默认方法可被实现类重写,若实现类继承的多个接口有同名默认方法,需显式重写以避免冲突。
示例:
java
interface Vehicle {
// 默认方法
default void start() {
System.out.println("Vehicle starting...");
}
}
class Car implements Vehicle {
// 重写默认方法
@Override
public void start() {
System.out.println("Car starting...");
}
}
public class DefaultMethodExample {
public static void main(String[] args) {
Vehicle vehicle = new Car();
vehicle.start(); // 输出 "Car starting..."
}
}
8. 接口的静态方法
接口中可通过static
关键字定义静态方法,用于提供与接口相关的工具方法。静态方法属于接口本身,需通过接口名直接调用,实现类无法继承或重写。
示例:
java
interface MathUtils {
// 静态方法
static int sum(int a, int b) {
return a + b;
}
}
public class StaticMethodExample {
public static void main(String[] args) {
// 通过接口名调用静态方法
int result = MathUtils.sum(3, 5);
System.out.println("Sum: " + result); // 输出 8
}
}
9. 方法引用
方法引用是Lambda表达式的简化形式,用于直接引用已存在的方法(无需参数列表和箭头)。常见形式有:
- 静态方法引用:
类名::静态方法
- 实例方法引用:
对象::实例方法
- 类的实例方法引用:
类名::实例方法
(参数列表第一个元素为调用者) - 构造方法引用:
类名::new
示例:
java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 静态方法引用:System.out::println 等价于 s -> System.out.println(s)
names.forEach(System.out::println);
// 类的实例方法引用:String::toUpperCase 等价于 s -> s.toUpperCase()
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
}
}
10. Date/Time API(java.time包)
Java 8引入了全新的日期时间API(java.time
包),解决了旧API(Date
、Calendar
)的线程不安全、设计混乱等问题。核心类包括:
LocalDate
:日期(年/月/日)LocalTime
:时间(时/分/秒)LocalDateTime
:日期+时间Duration
:时间间隔(秒/纳秒)Period
:日期间隔(年/月/日)
所有类均为不可变且线程安全。
示例:
java
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
public class DateTimeExample {
public static void main(String[] args) {
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("Today: " + today); // 输出 2024-05-20(示例)
// 解析日期字符串
LocalDate birthday = LocalDate.parse("1990-01-15");
// 计算日期差
Period age = Period.between(birthday, today);
System.out.println("Age: " + age.getYears() + " years");
// 格式化日期时间
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("Formatted: " + now.format(formatter)); // 输出 2024-05-20 15:30:45
}
}
11. 重复注解
Java 8允许在同一个元素(类、方法、字段等)上多次使用同一注解,需通过@Repeatable
注解声明注解的容器类。重复注解在编译时会被包装到容器注解中,可通过反射获取所有注解实例。
示例:
java
import java.lang.annotation.*;
// 定义重复注解的容器类
@Retention(RetentionPolicy.RUNTIME)
@interface RolesContainer {
Role[] value();
}
// 声明重复注解(指定容器类)
@Repeatable(RolesContainer.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Role {
String value();
}
// 多次使用重复注解
@Role("Admin")
@Role("User")
class UserService {}
public class RepeatableAnnotationExample {
public static void main(String[] args) {
// 获取所有Role注解
Role[] roles = UserService.class.getAnnotationsByType(Role.class);
for (Role role : roles) {
System.out.println(role.value()); // 输出 Admin、User
}
}
}
12. 类型注解
Java 8扩展了注解的使用范围,允许在类型声明的位置使用注解(如变量类型、返回值类型、泛型参数等),称为类型注解。类型注解配合检查工具(如Checker Framework)可实现更严格的类型校验(如非空检查、正则匹配检查)。
示例:
java
import java.lang.annotation.*;
import java.util.List;
// 自定义类型注解(标记非空)
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface NonNull {}
public class TypeAnnotationExample {
// 在变量类型上使用
private @NonNull String name;
// 在方法返回值上使用
public @NonNull String getName() {
return name;
}
// 在泛型参数上使用
public void processList(List<@NonNull String> list) {}
}
13. Nashorn JavaScript引擎
Java 8引入Nashorn引擎替代Rhino,用于在JVM中执行JavaScript代码,性能更优且支持ECMAScript 5.1标准。通过ScriptEngineManager
可获取引擎实例,实现Java与JavaScript的交互。
示例:
java
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class NashornExample {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn"); // 获取Nashorn引擎
// 执行JavaScript代码
engine.eval("print('Hello from JavaScript!')"); // 输出 Hello from JavaScript!
// Java与JavaScript交互
engine.put("message", "Hello"); // 向JS传递变量
engine.eval("print(message + ' Nashorn')"); // 输出 Hello Nashorn
}
}
14. 并行数组(Arrays增强)
java.util.Arrays
类新增了并行操作方法(如parallelSort
、parallelSetAll
、parallelPrefix
),利用Fork/Join框架实现多线程并行处理,大幅提升大型数组的处理效率。
示例:
java
import java.util.Arrays;
public class ParallelArraysExample {
public static void main(String[] args) {
int[] largeArray = new int[1_000_000];
// 填充随机数
Arrays.parallelSetAll(largeArray, i -> (int) (Math.random() * 1000));
// 并行排序(比传统sort更快)
Arrays.parallelSort(largeArray);
// 并行计算前缀和(每个元素 = 前n个元素之和)
int[] numbers = {1, 2, 3, 4, 5};
Arrays.parallelPrefix(numbers, (a, b) -> a + b);
System.out.println(Arrays.toString(numbers)); // 输出 [1, 3, 6, 10, 15]
}
}
15. CompletableFuture
CompletableFuture
是对Future
的增强,支持异步任务的链式操作、异常处理和结果组合,无需手动阻塞等待结果。它提供了supplyAsync
(带返回值的异步任务)、thenApply
(处理结果)、exceptionally
(异常处理)等方法,简化了异步编程。
示例:
java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 异步执行任务(无返回值)
CompletableFuture.runAsync(() -> {
System.out.println("Task 1: Running in background");
});
// 异步执行任务(带返回值)并处理结果
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "Hello";
}).thenApply(s -> s + " World") // 处理结果
.exceptionally(ex -> "Error: " + ex.getMessage()); // 异常处理
System.out.println(future.get()); // 输出 Hello World
}
}
16. Collector接口
Collector
接口用于定义Stream的收集操作(如collect()
方法),包含四个核心方法:supplier
(初始化容器)、accumulator
(累加元素)、combiner
(合并容器,用于并行流)、finisher
(最终转换)。Java 8通过Collectors
工具类提供了大量内置Collector(如toList
、groupingBy
)。
示例:
java
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class CollectorExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "cat", "dog", "elephant");
// 按长度分组(内置Collector:groupingBy)
Map<Integer, List<String>> groupByLength = words.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupByLength); // 输出 {3=[cat, dog], 5=[apple], 6=[banana], 8=[elephant]}
// 拼接字符串(内置Collector:joining)
String joined = words.stream()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(joined); // 输出 [apple, banana, cat, dog, elephant]
}
}
17. Base64编码支持
Java 8在java.util
包中新增Base64
类,提供标准的Base64编码和解码功能,无需依赖第三方库。支持三种编码方式:基本编码(getEncoder()
)、URL安全编码(getUrlEncoder()
)、MIME编码(getMimeEncoder()
)。
示例:
java
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64Example {
public static void main(String[] args) {
String original = "Hello Base64!";
// 编码
String encoded = Base64.getEncoder()
.encodeToString(original.getBytes(StandardCharsets.UTF_8));
System.out.println("Encoded: " + encoded); // 输出 SGVsbG8gQmFzZTY0IQ==
// 解码
byte[] decodedBytes = Base64.getDecoder().decode(encoded);
String decoded = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println("Decoded: " + decoded); // 输出 Hello Base64!
}
}
18. 元空间(Metaspace)
Java 8用元空间(Metaspace)取代了永久代(PermGen),用于存储类元数据(如类信息、方法信息)。元空间位于本地内存而非JVM堆,大小默认不受限(可通过-XX:MetaspaceSize
和-XX:MaxMetaspaceSize
限制),解决了永久代OOM(OutOfMemoryError)问题。
说明:元空间是JVM层面的改进,无直接代码示例,可通过JVM参数配置:
ini
# 设置元空间初始大小和最大大小
java -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m MyClass
19. try-with-resources增强
Java 7引入的try-with-resources语句在Java 8中进一步简化,允许在try块的资源声明中使用隐式final变量,且资源类型无需显式声明为AutoCloseable
(编译器自动推断)。资源会在try块结束后自动关闭,无需手动调用close()
。
示例:
java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesExample {
public static void main(String[] args) {
// 声明多个资源(用分号分隔),自动关闭
try (FileReader fr = new FileReader("test.txt");
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
20. String.join()方法
String
类新增join()
静态方法,用于将多个字符串按指定分隔符拼接,简化了传统的循环拼接方式。方法签名为String.join(CharSequence delimiter, CharSequence... elements)
。
示例:
java
import java.util.Arrays;
import java.util.List;
public class StringJoinExample {
public static void main(String[] args) {
// 拼接数组元素
String joined1 = String.join(", ", "Apple", "Banana", "Cherry");
System.out.println(joined1); // 输出 Apple, Banana, Cherry
// 拼接列表元素
List<String> fruits = Arrays.asList("Orange", "Grape");
String joined2 = String.join(" | ", fruits);
System.out.println(joined2); // 输出 Orange | Grape
}
}
21. Iterable接口的forEach()方法
Iterable
接口新增默认方法forEach(Consumer<? super T> action)
,允许直接对集合(如List、Set)调用forEach
遍历元素,无需手动写for循环,配合Lambda表达式更简洁。
示例:
java
import java.util.Arrays;
import java.util.List;
public class IterableForEachExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 直接调用forEach遍历(替代传统for循环)
numbers.forEach(num -> System.out.print(num + " ")); // 输出 1 2 3 4 5
}
}
22. 接口私有方法(Java 9预览,Java 8部分支持)
****Java 8中接口可通过默认方法间接实现"私有方法"逻辑(将重复代码提取到默认方法,由其他默认方法调用),Java 9正式引入private
关键字支持接口私有方法。
示例(Java 8模拟) :
java
interface Calculator {
// 公共默认方法
default int add(int a, int b) {
return calculate(a, b, (x, y) -> x + y);
}
default int multiply(int a, int b) {
return calculate(a, b, (x, y) -> x * y);
}
// 模拟私有方法(实际为默认方法,不建议外部调用)
default int calculate(int a, int b, MathOperation op) {
System.out.println("Calculating...");
return op.operate(a, b);
}
}
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
public class InterfacePrivateMethodExample implements Calculator {
public static void main(String[] args) {
Calculator calc = new InterfacePrivateMethodExample();
System.out.println(calc.add(2, 3)); // 输出 5
System.out.println(calc.multiply(2, 3)); // 输出 6
}
}