1. 介绍
JDK 8 是 Java 平台的一次重要更新,引入了许多新特性,主要目标是提升代码的可读性和生产力。主要特性包括:
- Lambda 表达式:简化函数式编程。
- Stream API:支持流式数据处理。
- 接口的默认方法和静态方法:增强接口的灵活性。
- 新时间与日期 API:解决旧版日期 API 的不足。
- Optional 类:优雅地处理空值问题。
- 并行操作的增强:更高效地利用多核处理器。
2. 集合相关
① ArrayList 被设计为懒加载模式,初始化无参构造,只维护一个空列表,当第一次添加元素才将数组初始化为10
② HashMap加入了红黑树数据结构
③ 新方法支持 Stream API:
- 集合框架中添加了
stream()
和parallelStream()
方法,支持流式处理集合中的数据。List<String> names = Arrays.asList("张三", "李四", "王五"); names.stream() .filter(name -> name.startsWith("张")) .forEach(System.out::println);
Map 的增强:
- 新增方法如
computeIfAbsent
、computeIfPresent
、forEach
等:Map<String, Integer> map = new HashMap<>(); map.put("A", 1); map.computeIfAbsent("B", key -> 2); // 如果 key "B" 不存在,则插入 2 map.forEach((k, v) -> System.out.println(k + ": " + v));
3. 接口相关
① 接口中可以书写普通方法,使用 default 关键字修饰,加在返回值类型之前、访问修饰符之后。
② 接口中可以书写静态方法。
默认方法(Default Methods):
- 在接口中可以定义带有方法体的
default
方法,避免破坏已有接口的实现。interface Vehicle { default void start() { System.out.println("Vehicle is starting"); } } class Car implements Vehicle {} Car car = new Car(); car.start(); // 输出: Vehicle is starting
静态方法:
- 接口中可以定义
static
方法,供所有实现类使用。interface Utils { static void print(String msg) { System.out.println(msg); } } Utils.print("Hello, Java 8!");
4. 函数式接口、编程
函数式接口,称之为SAM接口(Single Abstract Method)是只包含一个抽象方法的接口(可有默认或静态方法),可使用
@FunctionalInterface
注解标识。@FunctionalInterface interface Greeting { void sayHello(String name); }
函数式编程是一种编程思想,就像面向过程、面向对象等都属于编程思想
函数式编程的代码语言是Haskell
它更强调函数(方法)可以实现什么操作,执行了什么功能,不注重是哪个角色调用了这个函数(方法)使用函数式编程----即表示前提必须为函数式接口
5. lambda表达式
lambda表达式即函数式编程的最终实现
匿名内部类 :即我们可以直接new 接口、或者抽象类,相当于创建一个匿名内部类
使用了lambda表达式以后 之前匿名内部类书写格式混乱的问题 可以得到解决
前提:lambda表达式只能用于函数式接口
写法越简洁 前期越难理解 后期使用越方便
定义:
- Lambda 表达式是简洁地实现匿名内部类的一种方式。
- 格式:
(参数列表) -> {方法体}
List<String> names = Arrays.asList("张三", "李四", "王五"); names.forEach(name -> System.out.println(name));
优势:
- 更少的代码冗余,增强可读性。
- 配合函数式接口使用,代码更灵活。
示例:
package com.lambda.test; public class B { public static void main(String[] args) { C c1 = new C() { @Override public void m1() { System.out.println("匿名内部类的方式重写m1方法"); } }; c1.m1(); // 无参 无返回值 只有一条语句 C c2 = ()-> System.out.println("lambda表达式的方式重写m1方法"); c2.m1(); // 有一个参数 无返回值 只有一条语句 D d1 = (a)-> System.out.println("lambda表达式方式重写D接口m1方法" + a); d1.m1(100); // 有两个个参数 无返回值 只有一条语句 E e1 = (a,b)-> System.out.println("lambda表达式方式重写E接口m1方法" + a + b); e1.m1(123, "abc"); // 有两个参数 有返回值 只有一条语句 F f1 = (a,b)-> a + b; System.out.println(f1.m1(10, 20)); // 有一个参数 有返回值 有多条语句 F f2 = (a,b)->{ System.out.println(a + b); return a + b; }; } } interface C{ void m1(); } interface D{ void m1(int a); } interface E{ void m1(int a,String b); } interface F{ int m1(int a,int b); }
输出:
匿名内部类的方式重写m1方法 lambda表达式的方式重写m1方法 lambda表达式方式重写D接口m1方法100 lambda表达式方式重写E接口m1方法123abc 30
6. 常见函数式接口:
JDK提供的函数式接口位于 java.util.function 包中
这些函数式接口可以大致分为四类
- 断言型接口:Predicate<T>:boolean test(T t) 接收一个参数,返回布尔值。
- 功能型接口:Function<T, R>:R apply(T t) 接收一个参数,返回一个结果。
- 消费型接口:Consumer<T>:accept(T t) 接收一个参数,无返回值。
- 供给型接口:Supplier<T>:T get() 无参数,返回一个结果。
① Predicate<T>
- 功能:接收一个参数,返回一个布尔值。
- 典型用途:用于条件判断、过滤。
import java.util.function.Predicate; public class Main { public static void main(String[] args) { // 判断一个数是否为偶数 Predicate<Integer> isEven = n -> n % 2 == 0; System.out.println(isEven.test(4)); // 输出: true System.out.println(isEven.test(5)); // 输出: false } }
② Function<T, R>
- 功能:接收一个参数,返回一个结果。
- 典型用途:用于转换操作。
import java.util.function.Function; public class Main { public static void main(String[] args) { // 将一个字符串转换为其长度 Function<String, Integer> stringLength = str -> str.length(); System.out.println(stringLength.apply("Hello")); // 输出: 5 System.out.println(stringLength.apply("Java 8")); // 输出: 7 } }
③ Consumer<T>
- 功能:接收一个参数,无返回值。
- 典型用途:用于执行操作,例如打印、保存等。
import java.util.function.Consumer; public class Main { public static void main(String[] args) { // 打印一个字符串 Consumer<String> print = str -> System.out.println("Value: " + str); print.accept("Hello World"); // 输出: Value: Hello World print.accept("Java 8"); // 输出: Value: Java 8 } }
④ Supplier<T>
- 功能:无参数,返回一个结果。
- 典型用途:用于懒加载、生成默认值。
import java.util.function.Supplier; public class Main { public static void main(String[] args) { // 生成一个默认的字符串 Supplier<String> defaultMessage = () -> "Default Message"; System.out.println(defaultMessage.get()); // 输出: Default Message } }
综合示例
将以上函数式接口组合在一起处理一个简单任务:
import java.util.function.*; public class Main { public static void main(String[] args) { Predicate<Integer> isPositive = n -> n > 0; // 判断是否为正数 Function<Integer, String> toString = n -> "Number: " + n; // 转换为字符串 Consumer<String> print = str -> System.out.println(str); // 打印结果 Supplier<Integer> defaultNumber = () -> 42; // 提供默认值 int number = -5; if (isPositive.test(number)) { print.accept(toString.apply(number)); } else { print.accept(toString.apply(defaultNumber.get())); } } }
运行结果:
Number: 42
6.方法引用
方法引用 :在lambda表达式的基础上使用其他的方法的方法体来作为 lambda表达式(函数式接口中)抽象方法的方法体
具体细节:被引用的方法体和原本的方法之间的返回值、形参列表必须和函数式接口中抽象方法的 返回值、形参列表完全匹配
方法引用格式 ::
构造方法引用 类名 :: new;
静态方法引用 类名 :: 方法名;
实例方法引用 对象名 :: 方法名;示例:
package com.test; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; public class TestFunctional { public static void main(String[] args) { Consumer<Integer> consumer = System.out::println; consumer.accept(100); Function<String,Integer> function = Integer :: parseInt; System.out.println(function.apply("123")); Supplier<Double> supplier = Math ::random; System.out.println(supplier.get()); Predicate<String> predicate = String :: isEmpty; System.out.println(predicate.test("abc")); } }
输出:
100 123 0.28316372927345934 false
7. stream流式编程
定义:
Stream
是数据集合的高级抽象,可以对集合进行过滤、映射、归约等操作,支持并行处理。
Java8中有两大最为重要的改变。第一个是 Lambda表达式;另外一个则是 Stream API。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。
这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。
也可以使用 Stream API 来并行执行操作。
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream API 提供了一种高效且易于使用的处理数据的方式。
集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象,每次处理都会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行。
操作步骤:
Stream 操作流程
- 创建流:从数据源(集合、数组、生成器)获取流。
- 中间操作:通过链式操作对流进行处理。
- 终止操作:触发流的执行并生成最终结果。
① 创建 Stream
通过数据源(如集合、数组等)获取一个流。
方式一:通过集合
Java 8 中,Collection
接口提供了两个默认方法用于获取流:
- 顺序流:
default Stream<E> stream()
- 按顺序处理。 - 并行流:
default Stream<E> parallelStream()
- 支持多线程并行处理。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
// 获取顺序流
Stream<String> sequentialStream = list.stream();
// 获取并行流
Stream<String> parallelStream = list.parallelStream();
sequentialStream.forEach(System.out::println); // 顺序输出
parallelStream.forEach(System.out::println); // 并行输出(可能乱序)
}
}
方式二:通过数组
Java 8 提供 Arrays.stream()
静态方法,直接基于数组创建流。
static <T> Stream<T> stream(T[] array);
import java.util.Arrays;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
String[] array = {"X", "Y", "Z"};
// 使用 Arrays.stream() 创建流
Stream<String> stream = Arrays.stream(array);
stream.forEach(System.out::println); // 输出: X, Y, Z
}
}
方式三:通过 Stream.of()
使用 Stream.of()
静态方法,通过显式值快速创建一个流。该方法支持可变参数,因此可以传递任意数量的值。
public static <T> Stream<T> of(T... values);
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// 使用 Stream.of() 创建流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println); // 输出: 1, 2, 3, 4, 5
}
}
方式四:创建无限流(了解)
可以使用 Stream.iterate()
和 Stream.generate()
创建无限流,通常需要配合限制操作(如 limit()
)使用。
Stream.iterate()
:基于种子和规则生成序列。
public static <T> Stream<T> iterate(T seed, UnaryOperator<T> f);
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// 使用 iterate() 生成一个递增的无限流
Stream<Integer> stream = Stream.iterate(1, n -> n + 1);
// 限制输出前 5 个
stream.limit(5).forEach(System.out::println); // 输出: 1, 2, 3, 4, 5
}
}
Stream.generate()
:基于 Supplier
生成无限流。
public static <T> Stream<T> generate(Supplier<T> s);
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// 使用 generate() 生成随机数流
Stream<Double> stream = Stream.generate(Math::random);
// 限制输出 5 个随机数
stream.limit(5).forEach(System.out::println);
}
}
② 中间操作
中间操作会返回一个新的流(Stream
类型),不会立即执行,而是延迟到终止操作调用时执行。
常用方法
方 法 | 描 述 |
---|---|
filter(Predicate p) | 保存符合指定条件的元素 |
distinct() | 筛选,通过流所生成元素的equals() 去除重复元素 |
limit(long maxSize) | 保留指定个数的前 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
sorted() | 产生一个新流,其中按自然顺序排序 |
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个 |
③ 终止操作
终止操作会触发流的执行,并将结果返回或输出。
常用方法
-
方法 描述 boolean allMatch(Predicate p) 检查是否匹配所有元素 boolean anyMatch(Predicate p) 检查是否至少匹配一个元素 boolean noneMatch(Predicate p) 检查是否没有匹配所有元素 Optional<T> findFirst() 返回第一个元素 long count() 返回流中元素总数 Optional<T> max() 返回流中最大值 Optional<T> min() 返回流中最小值 void forEach(Consumer c) 迭代
示例
筛选出10人中年龄在20~30之前姓杨的女性
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; class Person { String name; int age; String gender; public Person(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + ", gender='" + gender + "'}"; } } public class Main { public static void main(String[] args) { // 数据源 List<Person> people = new ArrayList<>(); people.add(new Person("杨铭", 22, "女")); people.add(new Person("张三", 22, "男")); people.add(new Person("李四", 28, "男")); people.add(new Person("杨梅", 30, "女")); people.add(new Person("赵五", 21, "男")); people.add(new Person("杨花", 29, "女")); people.add(new Person("王六", 35, "男")); people.add(new Person("杨洪", 18, "男")); people.add(new Person("杨老六", 44, "女")); people.add(new Person("孙七", 27, "男")); // 使用 Stream API 筛选条件 List<Person> result = people.stream() .filter(person -> person.name.startsWith("杨")) .filter(person -> person.age >= 20 && person.age <= 30) .filter(person -> "女".equals(person.gender)) .collect(Collectors.toList()); // 收集结果为列表 System.out.println("筛选结果:"); result.forEach(System.out::println); } }
输出:
筛选结果: Person{name='杨铭', age=22, gender='女'} Person{name='杨梅', age=30, gender='女'} Person{name='杨花', age=29, gender='女'}