Java 8 新特性(保姆级教程)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

Java 8 的发布标志着 Java 编程语言的一次重大革新。自 2014 年正式推出以来,它引入了一系列颠覆性的新特性,如 Lambda 表达式Stream API默认方法新的日期时间 API等。这些特性不仅简化了代码的编写,还显著提升了开发效率和程序的可读性。无论是编程初学者还是中级开发者,掌握这些新特性都能为日常开发带来事半功倍的效果。本文将通过通俗易懂的讲解和实际案例,帮助读者系统性地理解 Java 8 的核心改进。


一、Lambda 表达式:代码简洁化的瑞士军刀

1.1 什么是 Lambda 表达式?

Lambda 表达式是 Java 8 引入的匿名函数语法,用于简化对函数式接口(仅包含一个抽象方法的接口)的调用。它的核心思想是将行为(一段代码)作为参数传递,而非传统的对象引用。

形象比喻
Lambda 表达式就像一个“即插即用”的工具包,你只需描述“要做什么”,而无需关心“如何实现”。

语法结构

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

1.2 Lambda 的经典应用场景

案例 1:集合排序

在 Java 8 之前,排序需要通过 Comparator 接口的匿名内部类实现:

// 旧写法  
list.sort(new Comparator<String>() {  
    @Override  
    public int compare(String a, String b) {  
        return a.length() - b.length();  
    }  
});  

Lambda 简化版

list.sort((a, b) -> a.length() - b.length());  

案例 2:遍历集合

// 旧写法  
for (String item : list) {  
    System.out.println(item);  
}  

使用 Lambda 和 forEach

list.forEach(item -> System.out.println(item));  

1.3 函数式接口的威力

Java 8 引入了 @FunctionalInterface 注解,用于标记函数式接口。例如:

@FunctionalInterface  
interface MyFunction {  
    int calculate(int a);  
}  

调用时可以直接传入 Lambda:

MyFunction add = (x) -> x + 5;  
System.out.println(add.calculate(10)); // 输出 15  

二、Stream API:数据处理的流水线

2.1 Stream 的核心概念

Stream API 是 Java 8 对集合操作的革命性改进。它通过链式调用提供了一种声明式(Declarative)的数据处理方式,将操作分为 中间操作终端操作

形象比喻
Stream 就像一条流水线,你可以依次添加加工步骤(中间操作),最终通过终端操作获取结果。

中间操作

  • 延迟执行(Lazy Evaluation)
  • 返回新的流,不改变原集合
  • 例如 filter()map()sorted()

终端操作

  • 触发实际计算
  • 返回具体结果或 void
  • 例如 collect()forEach()reduce()

2.2 Stream 的典型用法

案例 1:过滤与映射

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

// 过滤长度 > 4 的名字,转为大写后收集到新列表  
List<String> result = names.stream()  
    .filter(name -> name.length() > 4)  // 中间操作  
    .map(String::toUpperCase)           // 中间操作  
    .collect(Collectors.toList());      // 终端操作  

案例 2:数值计算

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);  

// 计算总和  
int sum = numbers.stream()  
    .mapToInt(Integer::intValue)  
    .sum();  // 输出 15  

// 找出最大值  
OptionalInt max = numbers.stream()  
    .mapToInt(Integer::intValue)  
    .max();  // 输出 5  

2.3 并行流的性能优化

通过 parallelStream() 可以利用多核 CPU 并行处理数据:

List<Integer> largeList = IntStream.range(0, 1_000_000).boxed().collect(Collectors.toList());  

// 串行流  
long startTime = System.currentTimeMillis();  
long sum = largeList.stream().mapToLong(i -> i).sum();  
System.out.println("串行耗时:" + (System.currentTimeMillis() - startTime));  

// 并行流  
startTime = System.currentTimeMillis();  
long parallelSum = largeList.parallelStream().mapToLong(i -> i).sum();  
System.out.println("并行耗时:" + (System.currentTimeMillis() - startTime));  

三、默认方法:接口的进化

3.1 问题背景

在 Java 8 之前,接口只能包含抽象方法。如果需要向已有接口添加新方法,所有实现类必须被迫实现,这会导致“破坏性变更”。

3.2 默认方法的解决方案

Java 8 允许在接口中定义 默认方法(Default Methods),提供方法的默认实现,从而兼容旧代码:

interface Shape {  
    void draw();  

    // 默认方法  
    default void resize() {  
        System.out.println("默认的 resize 实现");  
    }  
}  

class Circle implements Shape {  
    @Override  
    public void draw() {  
        // 实现 draw 方法  
    }  

    // 可选择性覆盖默认方法  
    @Override  
    public void resize() {  
        System.out.println("Circle 的特殊 resize 实现");  
    }  
}  

3.3 冲突解决规则

如果多个接口为同一方法提供了默认实现,子类必须显式覆盖该方法:

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

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

class C implements A, B {  
    // 必须覆盖 show 方法  
    @Override  
    public void show() {  
        A.super.show(); // 调用 A 的实现  
        // 或 B.super.show();  
    }  
}  

四、新的日期时间 API:告别 Date 的混乱

4.1 旧 Date 类的痛点

Java 7 及之前的 java.util.DateCalendar 存在以下问题:

  • 不可变性差:Date 是可变类,容易引发线程安全问题。
  • API 设计混乱:如月份从 0 开始(0=1月),易出错。
  • 缺乏时区支持:需依赖 Calendar 的时区设置。

4.2 Java 8 的 java.time

Java 8 引入了全新的 java.time 包,包含以下核心类:
| 类名 | 用途 |
|-------------------|---------------------|
| LocalDate | 不带时区的日期(如 2023-10-01) |
| LocalTime | 不带时区的时间(如 14:30:00) |
| LocalDateTime | 日期与时间的组合 |
| ZonedDateTime | 带时区的日期时间 |
| Duration | 表示时间差(基于秒/纳秒) |

案例:日期计算与格式化

// 创建当前日期  
LocalDate today = LocalDate.now();  

// 计算 10 天后的日期  
LocalDate futureDate = today.plusDays(10);  

// 格式化输出  
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");  
System.out.println(futureDate.format(formatter)); // 输出 "2023-10-11"  

// 解析字符串为日期  
LocalDate parsedDate = LocalDate.parse("2023-10-01", formatter);  

五、Optional 类:告别空指针异常

5.1 Null 的隐患

在 Java 中,NullPointerException 是最常见的运行时异常之一。Optional 类通过封装 null 值,强制开发者显式处理可能为空的场景。

5.2 Optional 的基础用法

// 创建 Optional 实例  
Optional<String> optionalValue = Optional.of("Hello");  
Optional<String> emptyOptional = Optional.empty();  

// 安全获取值  
String result = optionalValue.orElse("默认值"); // 输出 "Hello"  
String safeValue = emptyOptional.orElse("默认值"); // 输出 "默认值"  

// 条件判断  
if (optionalValue.isPresent()) {  
    System.out.println(optionalValue.get()); // 仅当存在值时调用 get()  
}  

5.3 在流操作中的应用

List<Person> people = ...;  

// 安全获取第一个匹配的姓名  
Optional<String> firstName = people.stream()  
    .filter(p -> p.getAge() > 18)  
    .map(Person::getName)  
    .findFirst();  

// 使用或操作链式处理  
String name = firstName.orElseGet(() -> "匿名");  

六、其他重要特性

6.1 方法引用:Lambda 的简化形式

方法引用允许直接引用已有方法,进一步简化代码:

// 对应 Lambda:(s) -> System.out.println(s)  
list.forEach(System.out::println);  

// 构造方法引用  
List<Shape> shapes = Stream.generate(Shape::new)  
    .limit(5)  
    .collect(Collectors.toList());  

6.2 新的集合工具类:Collectors

Collectors 类提供了丰富的流终端操作工具,如分组、分区、统计等:

// 按性别分组统计人数  
Map<Gender, Long> countByGender = people.stream()  
    .collect(Collectors.groupingBy(Person::getGender, Collectors.counting()));  

// 收集到 Set  
Set<String> uniqueNames = people.stream()  
    .map(Person::getName)  
    .collect(Collectors.toSet());  

结论

Java 8 的新特性彻底改变了 Java 的编程范式,从 命令式编程声明式编程 转变。Lambda 和 Stream API 使得代码更简洁、易读;默认方法和 Optional 类则增强了 API 的灵活性和安全性。对于开发者而言,掌握这些特性不仅是技术能力的提升,更是适应现代 Java 生态系统的必要条件。

无论你是刚入门的开发者,还是希望优化现有代码的中级工程师,深入理解 Java 8 的核心改进都能让你在项目中更高效地解决问题。接下来,不妨尝试将这些新特性应用到实际项目中,感受 Java 8 带来的开发体验革新!

最新发布