Java 8 新特性全面教程


Java 8 是自 Java 5 以来最具革命性的版本,引入了许多改变Java编程方式的重大特性。本教程将系统性地介绍Java 8的核心新特性,包括Lambda表达式、函数式接口、Stream API、Optional类、新的日期时间API等,并通过大量代码示例展示如何在实际开发中应用这些特性。

一、Lambda表达式

Lambda表达式(也称为闭包)是Java 8最受期待的特性,它允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

1. Lambda表达式基础语法

Java 8中引入了一个新的操作符”->”,称为箭头操作符或Lambda操作符。它将Lambda表达式分成两部分:

  • 左侧:Lambda表达式的参数列表
  • 右侧:Lambda表达式中要执行的功能(Lambda体)

基本语法格式

(parameters) -> expression
或
(parameters) -> { statements; }

2. Lambda表达式六种语法格式

  1. 无参数,无返回值
() -> System.out.println("Hello Lambda");
// 示例
Runnable r1 = () -> System.out.println("Hello Lambda");
  1. 一个参数,无返回值
(x) -> System.out.println(x)
// 可简化为(一个参数时可省略括号)
x -> System.out.println(x)
  1. 多个参数,有返回值,Lambda体有多条语句
Comparator<Integer> com = (x,y) -> {
    System.out.println("函数式接口");
    return Integer.compare(x,y);
};
  1. Lambda体只有一条语句,return和大括号可省略
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
  1. Lambda参数列表的数据类型可省略(JVM编译器通过上下文推断):
Comparator<Integer> com = (Integer x, Integer y) -> Integer.compare(x,y);
// 可简化为
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
  1. 无参数,有返回值
() -> 42

3. Lambda表达式示例

传统写法 vs Lambda写法

// 传统写法 - 匿名内部类
Comparator<Integer> com = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1,o2);
    }
};

// Lambda写法
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);

集合排序示例

List<Employee> employees = Arrays.asList(
    new Employee("张三", 18, 9999.99),
    new Employee("李四", 45, 8888.88),
    new Employee("王五", 58, 7777.77)
);

// 按年龄排序
Collections.sort(employees, (e1, e2) -> {
    if(e1.getAge() == e2.getAge()){
        return e1.getName().compareTo(e2.getName());
    }else{
        return Integer.compare(e1.getAge(), e2.getAge());
    }
});

二、函数式接口

1. 什么是函数式接口

函数式接口(Functional Interface)就是只包含一个抽象方法的接口。可以使用@FunctionalInterface注解来检查它是否是一个函数式接口。

Java 8中java.util.function包下内置了四大核心函数式接口:

  1. Consumer:消费型接口
  • 方法:void accept(T t)
  • 用途:对类型为T的对象应用操作
  1. Supplier:供给型接口
  • 方法:T get()
  • 用途:返回类型为T的对象
  1. Function:函数型接口
  • 方法:R apply(T t)
  • 用途:对类型为T的对象应用操作,并返回结果R
  1. Predicate:断言型接口
  • 方法:boolean test(T t)
  • 用途:确定类型为T的对象是否满足约束

2. 内置函数式接口示例

Consumer示例

public void happy(double money, Consumer<Double> con){
    con.accept(money);
}

@Test
public void test(){
    happy(1000, m -> System.out.println("消费了"+m+"元"));
}

Supplier示例

public List<Integer> getNumList(int num, Supplier<Integer> sup){
    List<Integer> list = new ArrayList<>();
    for(int i=0; i<num; i++){
        list.add(sup.get());
    }
    return list;
}

@Test
public void test(){
    List<Integer> numList = getNumList(10, () -> (int)(Math.random()*100));
    numList.forEach(System.out::println);
}

Function示例

public String strHandler(String str, Function<String,String> fun){
    return fun.apply(str);
}

@Test
public void test(){
    String newStr = strHandler("hello world", str -> str.toUpperCase());
    System.out.println(newStr);
}

Predicate示例

public List<String> filterStr(List<String> list, Predicate<String> pre){
    List<String> strList = new ArrayList<>();
    for(String str : list){
        if(pre.test(str)){
            strList.add(str);
        }
    }
    return strList;
}

@Test
public void test(){
    List<String> list = Arrays.asList("Hello", "Java8", "Lambda", "ok");
    List<String> result = filterStr(list, s -> s.length() > 3);
    result.forEach(System.out::println);
}

三、方法引用与构造器引用

方法引用是Lambda表达式的一种简化形式,当Lambda体中的内容已经有方法实现了,可以使用方法引用。

1. 方法引用的三种形式

  1. 对象::实例方法名
Consumer<String> con = (x) -> System.out.println(x);
// 等价于
Consumer<String> con = System.out::println;
  1. 类::静态方法名
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
// 等价于
Comparator<Integer> com = Integer::compare;
  1. 类::实例方法名
BiPredicate<String,String> bp = (x,y) -> x.equals(y);
// 等价于
BiPredicate<String,String> bp = String::equals;

2. 构造器引用

语法:ClassName::new

Supplier<Employee> sup = () -> new Employee();
// 等价于
Supplier<Employee> sup = Employee::new;

3. 数组引用

语法:Type[]::new

Function<Integer,String[]> fun = (x) -> new String[x];
// 等价于
Function<Integer,String[]> fun = String[]::new;

四、Stream API

Stream API是Java 8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

1. Stream操作的三个步骤

  1. 创建Stream:通过数据源(集合、数组等)获取流
  2. 中间操作:对数据源的数据进行处理
  3. 终止操作:执行中间操作链,产生结果

2. 创建Stream的四种方式

// 1. 通过Collection系列集合的stream()或parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();

// 2. 通过Arrays中的静态方法stream()获取数组流
Employee[] emps = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(emps);

// 3. 通过Stream类中的静态方法of()
Stream<String> stream3 = Stream.of("aa","bb","cc");

// 4. 创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x+2);
// 生成
Stream.generate(() -> Math.random());

3. 常用中间操作

  1. 筛选与切片
  • filter:接收Lambda,从流中排除某些元素
  • limit:截断流,使其元素不超过给定数量
  • skip(n):跳过元素,返回一个扔掉了前n个元素的流
  • distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素
  1. 映射
  • map:接收Lambda,将元素转换成其他形式或提取信息
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
  1. 排序
  • sorted():自然排序
  • sorted(Comparator com):定制排序

4. 常用终止操作

  1. 查找与匹配
  • allMatch:检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配所有元素
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值
  1. 归约
  • reduce(T identity, BinaryOperator):可以将流中元素反复结合起来,得到一个值
  • reduce(BinaryOperator):可以将流中元素反复结合起来,得到一个值
  1. 收集
  • collect:将流转换为其他形式,接收一个Collector接口的实现

5. Stream API示例

List<Employee> emps = Arrays.asList(
    new Employee("张三", 18, 9999.99, Status.FREE),
    new Employee("李四", 45, 8888.88, Status.BUSY),
    new Employee("王五", 58, 7777.77, Status.VOCATION),
    new Employee("赵六", 25, 6666.66, Status.FREE),
    new Employee("田七", 13, 5555.55, Status.BUSY)
);

// 1. 获取工资大于6000的员工
List<Employee> list = emps.stream()
    .filter(e -> e.getSalary() > 6000)
    .collect(Collectors.toList());

// 2. 获取所有员工姓名
List<String> names = emps.stream()
    .map(Employee::getName)
    .collect(Collectors.toList());

// 3. 统计员工状态分组
Map<Status, List<Employee>> map = emps.stream()
    .collect(Collectors.groupingBy(Employee::getStatus));

// 4. 获取工资最高的员工
Optional<Employee> max = emps.stream()
    .max(Comparator.comparingDouble(Employee::getSalary));

// 5. 检查是否有状态为BUSY的员工
boolean anyBusy = emps.stream()
    .anyMatch(e -> e.getStatus() == Status.BUSY);

五、接口的默认方法与静态方法

Java 8允许接口中包含具有具体实现的方法,这种方法称为默认方法,使用default关键字修饰。

1. 默认方法

interface MyInterface {
    default String getName(){
        return "默认方法";
    }
}

2. 静态方法

interface MyInterface {
    static void show(){
        System.out.println("接口中的静态方法");
    }
}

3. 默认方法冲突解决

如果一个类实现了多个接口,且这些接口有相同的默认方法,那么实现类必须覆盖该方法:

interface A {
    default void hello(){
        System.out.println("A");
    }
}

interface B {
    default void hello(){
        System.out.println("B");
    }
}

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // 显式选择调用A接口的默认方法
    }
}

六、Optional类

Optional类是一个容器类,代表一个值存在或不存在,用来避免空指针异常。

1. Optional常用方法

  1. Optional.of(T t):创建一个Optional实例
  2. Optional.empty():创建一个空的Optional实例
  3. Optional.ofNullable(T t):若t不为null则创建Optional实例,否则创建空实例
  4. isPresent():判断是否包含值
  5. get():获取值,若值为null则抛出NoSuchElementException
  6. orElse(T other):如果有值则返回,否则返回指定的other对象
  7. orElseGet(Supplier other):如果有值则返回,否则返回由Supplier接口实现提供的对象
  8. orElseThrow(Supplier exceptionSupplier):如果有值则返回,否则抛出由Supplier接口实现提供的异常
  9. map(Function mapper):如果有值,则对其应用Function接口实现,并返回结果
  10. flatMap(Function mapper):与map类似,要求返回值必须是Optional

2. Optional示例

public String getEmployeeName(Employee emp){
    return Optional.ofNullable(emp)
            .map(e -> e.getName())
            .orElse("Unknown");
}

@Test
public void test(){
    Employee emp = null;
    System.out.println(getEmployeeName(emp)); // Unknown

    emp = new Employee("张三");
    System.out.println(getEmployeeName(emp)); // 张三
}

七、新的日期时间API

Java 8引入了全新的日期时间API,位于java.time包下。

1. 主要类

  1. LocalDate:表示日期,格式为yyyy-MM-dd
  2. LocalTime:表示时间,格式为HH:mm:ss
  3. LocalDateTime:表示日期时间,格式为yyyy-MM-dd HH:mm:ss
  4. Instant:时间戳(以Unix元年开始计算)
  5. Duration:计算两个”时间”之间的间隔
  6. Period:计算两个”日期”之间的间隔
  7. DateTimeFormatter:格式化日期时间

2. 日期时间API示例

// 1. 获取当前日期时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt); // 2025-04-07T15:30:45.123

// 2. 指定日期时间
LocalDateTime ldt2 = LocalDateTime.of(2025, 4, 7, 15, 30, 45);
System.out.println(ldt2); // 2025-04-07T15:30:45

// 3. 日期时间运算
LocalDateTime ldt3 = ldt.plusYears(2); // 加2年
LocalDateTime ldt4 = ldt.minusMonths(3); // 减3个月

// 4. 格式化日期时间
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDate = ldt.format(dtf);
System.out.println(strDate); // 2025年04月07日 15:30:45

// 5. 时间戳
Instant instant = Instant.now(); // 默认获取UTC时区
System.out.println(instant); // 2025-04-07T07:30:45.123Z

// 6. 计算时间间隔
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2026, 1, 1);
Period period = Period.between(ld1, ld2);
System.out.println(period.getMonths() + "个月" + period.getDays() + "天");

八、其他新特性

1. 重复注解

Java 8允许在同一位置多次使用同一个注解,前提是该注解被@Repeatable修饰。

@Repeatable(Filters.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filter {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
public @interface Filters {
    Filter[] value();
}

@Filter("filter1")
@Filter("filter2")
public interface Filterable {
}

public static void main(String[] args) {
    for(Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) {
        System.out.println(filter.value());
    }
}

2. 类型注解

Java 8新增了类型注解,可以用于任何使用类型的地方。

@Target(ElementType.TYPE_USE)
public @interface NotNull {
}

public static void main(String[] args) {
    @NotNull String str = null; // 编译时会检查
}

3. Base64支持

Java 8内置了Base64编解码器。

// 编码
String base64 = Base64.getEncoder().encodeToString("Java8".getBytes());
System.out.println(base64); // SmF2YTg=

// 解码
byte[] bytes = Base64.getDecoder().decode("SmF2YTg=");
System.out.println(new String(bytes)); // Java8

九、总结

Java 8的新特性为Java语言带来了革命性的变化,使Java能够更好地支持函数式编程风格。主要特性包括:

  1. Lambda表达式:使代码更简洁,支持函数式编程
  2. 函数式接口:配合Lambda表达式使用,内置四大核心函数式接口
  3. 方法引用与构造器引用:进一步简化Lambda表达式
  4. Stream API:强大的集合操作工具,支持并行处理
  5. 接口的默认方法与静态方法:增强接口的能力
  6. Optional类:更好地处理空指针异常
  7. 新的日期时间API:解决了旧日期API的线程安全问题
  8. 其他特性:重复注解、类型注解、Base64支持等

掌握这些新特性可以显著提高Java开发效率和代码质量,是现代Java开发者必备的技能。

,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注