Skip to content

JDK 8 特性详解

JDK 8 是Java语言发展的一个重要里程碑,引入了许多革命性的特性,特别是函数式编程相关的功能。本文将详细介绍JDK 8的主要特性,并通过示例代码来说明其用法。

1. Lambda表达式

Lambda表达式是JDK 8最显著的特性之一,它允许我们以更简洁的方式表示匿名函数。

语法

java
(参数列表) -> 表达式
// 或
(参数列表) -> { 语句块 }

示例

java
// 传统方式
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, World!");
    }
};

// 使用Lambda表达式
Runnable runnableLambda = () -> System.out.println("Hello, World!");

// 带参数的Lambda表达式
Function<Integer, Integer> square = (x) -> x * x;
System.out.println(square.apply(5)); // 输出: 25

// 多参数Lambda表达式
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
System.out.println(add.apply(3, 4)); // 输出: 7

2. 函数式接口

函数式接口是只包含一个抽象方法的接口,可以使用@FunctionalInterface注解来标记。

内置函数式接口

接口名称参数返回值描述
Consumer<T>Tvoid接收一个参数,无返回值
Supplier<T>T无参数,返回一个值
Function<T, R>TR接收一个参数,返回一个值
Predicate<T>Tboolean接收一个参数,返回布尔值
BiFunction<T, U, R>T, UR接收两个参数,返回一个值

示例

java
// 使用Consumer
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello, Consumer!");

// 使用Supplier
Supplier<Double> random = () -> Math.random();
System.out.println(random.get());

// 使用Predicate
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // 输出: true

3. 方法引用

方法引用是一种更简洁的Lambda表达式写法,用于引用已存在的方法。

语法

java
对象::实例方法
::静态方法
::实例方法

示例

java
// 引用静态方法
Function<String, Integer> parseInt = Integer::parseInt;
System.out.println(parseInt.apply("123")); // 输出: 123

// 引用实例方法
String str = "Hello";
Supplier<Integer> length = str::length;
System.out.println(length.get()); // 输出: 5

// 引用构造方法
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();

4. Stream API

Stream API 是JDK 8引入的一个强大的处理集合的工具,它支持函数式风格的操作。

基本操作

  • 创建流: stream(), parallelStream()
  • 中间操作: filter(), map(), sorted(), distinct(), limit(), skip()
  • 终端操作: forEach(), collect(), reduce(), count(), anyMatch(), allMatch(), noneMatch()

示例

java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");

// 过滤并收集
List<String> filteredNames = names.stream()
    .filter(name -> name.length() > 3)
    .collect(Collectors.toList());
System.out.println(filteredNames); // 输出: [Alice, Charlie, David]

// 映射并计算
int totalLength = names.stream()
    .mapToInt(String::length)
    .sum();
System.out.println(totalLength); // 输出: 19

// 并行流
long count = names.parallelStream()
    .filter(name -> name.startsWith("A"))
    .count();
System.out.println(count); // 输出: 1

5. Optional类

Optional类是一个容器对象,用于处理可能为null的值,避免NullPointerException。

主要方法

  • of(): 创建一个非空的Optional对象
  • ofNullable(): 创建一个可能为空的Optional对象
  • empty(): 创建一个空的Optional对象
  • isPresent(): 检查值是否存在
  • ifPresent(): 如果值存在则执行操作
  • orElse(): 如果值不存在则返回默认值
  • orElseGet(): 如果值不存在则通过Supplier获取默认值
  • orElseThrow(): 如果值不存在则抛出异常

示例

java
// 创建Optional对象
Optional<String> optional1 = Optional.of("Hello");
Optional<String> optional2 = Optional.ofNullable(null);
Optional<String> optional3 = Optional.empty();

// 检查值是否存在
System.out.println(optional1.isPresent()); // 输出: true
System.out.println(optional2.isPresent()); // 输出: false

// 获取值
String value1 = optional1.orElse("Default");
String value2 = optional2.orElse("Default");
System.out.println(value1); // 输出: Hello
System.out.println(value2); // 输出: Default

// 函数式风格
optional1.ifPresent(System.out::println); // 输出: Hello

// 链式操作
String result = optional1
    .map(s -> s.toUpperCase())
    .orElse("DEFAULT");
System.out.println(result); // 输出: HELLO

6. 日期时间API

JDK 8引入了新的日期时间API,位于java.time包中,解决了旧API的许多问题。

主要类

  • LocalDate: 表示日期(年、月、日)
  • LocalTime: 表示时间(时、分、秒)
  • LocalDateTime: 表示日期和时间
  • ZonedDateTime: 表示带时区的日期和时间
  • Duration: 表示时间间隔
  • Period: 表示日期间隔
  • DateTimeFormatter: 日期时间格式化

示例

java
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println(today); // 输出: 2023-10-05

// 创建指定日期
LocalDate date = LocalDate.of(2023, 12, 25);
System.out.println(date); // 输出: 2023-12-25

// 日期计算
LocalDate nextWeek = today.plusWeeks(1);
System.out.println(nextWeek); // 输出: 2023-10-12

// 日期比较
boolean isAfter = nextWeek.isAfter(today);
System.out.println(isAfter); // 输出: true

// 格式化日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = today.format(formatter);
System.out.println(formattedDate); // 输出: 2023-10-05

// 解析日期
LocalDate parsedDate = LocalDate.parse("2023-10-05", formatter);
System.out.println(parsedDate); // 输出: 2023-10-05

7. 默认方法

默认方法是接口中可以有实现的方法,使用default关键字修饰。

示例

java
interface MyInterface {
    void abstractMethod();
    
    default void defaultMethod() {
        System.out.println("Default method implementation");
    }
}

class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println("Abstract method implementation");
    }
}

// 使用
MyClass myClass = new MyClass();
myClass.abstractMethod(); // 输出: Abstract method implementation
myClass.defaultMethod(); // 输出: Default method implementation

8. 静态方法在接口中

JDK 8允许在接口中定义静态方法。

示例

java
interface MyInterface {
    static void staticMethod() {
        System.out.println("Static method in interface");
    }
}

// 直接调用
MyInterface.staticMethod(); // 输出: Static method in interface

9. Nashorn JavaScript引擎

JDK 8引入了新的JavaScript引擎Nashorn,替代了旧的Rhino引擎。

示例

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");
        
        // 执行JavaScript代码
        engine.eval("print('Hello from Nashorn!')");
        
        // 调用JavaScript函数
        engine.eval("function add(a, b) { return a + b; }");
        Object result = engine.eval("add(2, 3)");
        System.out.println("2 + 3 = " + result); // 输出: 2 + 3 = 5
    }
}

10. 类型注解

JDK 8扩展了注解的使用范围,允许在更多地方使用注解,如类型参数、类型转换等。

示例

java
// 类型参数注解
public class TypeAnnotationExample {
    public <@MyAnnotation T> void method(T t) {}
    
    // 类型转换注解
    public void castExample(Object obj) {
        String str = (@MyAnnotation String) obj;
    }
    
    // 数组注解
    public void arrayExample(@MyAnnotation String[] array) {}
}

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}

11. 收集器(Collectors)

Collectors类提供了许多用于Stream API的收集操作。

示例

java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");

// 收集到List
List<String> nameList = names.stream()
    .collect(Collectors.toList());

// 收集到Set
Set<String> nameSet = names.stream()
    .collect(Collectors.toSet());

// 收集到Map
Map<String, Integer> nameLengthMap = names.stream()
    .collect(Collectors.toMap(name -> name, String::length));

// 分组
Map<Integer, List<String>> lengthGroups = names.stream()
    .collect(Collectors.groupingBy(String::length));

// 分区
Map<Boolean, List<String>> partitioned = names.stream()
    .collect(Collectors.partitioningBy(name -> name.length() > 3));

// 连接字符串
String joined = names.stream()
    .collect(Collectors.joining(", "));
System.out.println(joined); // 输出: Alice, Bob, Charlie, David, Eve

12. 并行数组操作

JDK 8在Arrays类中添加了一些并行操作方法。

示例

java
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 并行排序
Arrays.parallelSort(numbers);
System.out.println(Arrays.toString(numbers)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 并行前缀
int[] prefix = new int[numbers.length];
Arrays.parallelPrefix(numbers, (a, b) -> a + b);
System.out.println(Arrays.toString(numbers)); // 输出: [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

// 并行设置
Arrays.parallelSetAll(prefix, i -> i * 2);
System.out.println(Arrays.toString(prefix)); // 输出: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

总结

JDK 8引入了许多强大的特性,特别是函数式编程相关的功能,如Lambda表达式、Stream API等。这些特性使得Java代码更加简洁、易读,同时提高了开发效率。通过本文的示例,你应该对JDK 8的主要特性有了更深入的了解。