Java 8 新特性
1、Java 8新特性简介
- 速度更快
- 代码更少(Lambda 表达式的出现)
- 强大的 Stream API
- 便于运行
- 最大化减少空指针异常:Optional
- 新的引擎:Nashorn 引擎
2、Lambda 表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。
2.1、Lambda 表达式的使用
1)从匿名类到 Lambda 的转换举例
// 匿名内部类
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("Hello World!");
};
}
// 使用 Lambda 表达式
Runnable r1 = () -> {
System.out.println("Hello World!");
};
2)Lambda 表达式:在 Java 8 中引入的一种新的语法元素和操作符,这个操作符为 ->
,该操作符被称为Lambda 操作符或箭头操作符 。他将 Lambda 表达式分为两个部分:
- 左侧:Lambda 参数列表(其实就是接口中的抽象方法的形参列表)
- 右侧:指定了 Lambda 体(其实就是重写的抽象方法的方法体)
3)Lambda 表达式的本质:作为函数接口的实例
函数式接口:如果一个接口中,只声明了一个抽象方法,则此接口就叫称为函数式接口。
Lambda 表达式也只能应用在函数式接口上。
4)Lambda 表达式的几种情况介绍
- 无参数,无返回值
// 语法格式
() -> {具体方法代码};
// 举例
Runnable r1 = () -> {
System.out.println("Hello World!");
};
- 有参数,无返回值
// 语法格式
(方法参数) -> {具体方法代码}; // 多个参数,用逗号隔开
//举例
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
方法参数中的数据类型可以省略,因为可由编译器推断得出,称为"类型推断"。
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
如果方法参数只有一个,则参数的小括号也可以省略。
Consumer<String> con1 = s -> {
System.out.println(s);
};
- 有参数,有返回值
// 语法格式
(方法参数) -> {具体方法代码}; // 多个参数,用逗号隔开
//举例
Consumer<Integer> con1 = (o1, o2) -> {
return o1.compare(o2);
};
如果 Lambda 体只有一条执行语句时,return 与大括号都可以省略。
Consumer<Integer> con1 = (o1, o2) -> o1.compare(o2);
总结:
- lambda 形参列表的参数类型可以省略;
- 如果 lambda 形参列表只有一个参数,可以省略小括号
- 如果 lambda 体中只有一条执行语句,可以省略大括号,如果该语句是 return 语句,则 return 关键字也可以省略。
3、函数式接口
3.1、什么是函数式接口
- 只包含一个抽象方法的接口,称为函数式接口
- 我们可以在一个接口上使用
@FunctionalInterface
注解,这样做可以检查它是否是一个函数式接口。 - 在 java.util.function 包下定义了 java 8 的丰富的函数式接口
3.2、Java 内置的四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T> 消费型接口 |
T |
void |
对类型为 T 的对象应用操作,包含方法:void accept(T t) |
supplier<T> 供给型接口 |
无参数 |
T |
返回类型为 T 的对象,包含方法:T get() |
Function<T, R> 函数型接口 |
T |
R |
对类型为 T 的对象应用操作,并返回 R 类型的对象。包含方法:R apply(T t) |
Predicate<T> 断定型接口 |
T |
boolean |
确定类型为 T 的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t) |
这四大核心函数式接口还有一些扩展接口,都在 java.util.function 包。
4、方法引用与构造器引用
4.1、方法引用
1)什么是方法引用
- 当要传递给 lambda 体的操作,已经有实现的方法了,可以使用方法引用!
- 方法引用可以看作是 lambda 表达式更深层次的表达。换句话说,方法引用就是 lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是 lambda 表达式的一个语法糖。
2)方法引用,本质上就是 lambda 表达式,而 lambda 表达式作为函数式接口的实例。所以,方法引用,也是函数式接口的实例。
3)使用要求
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值保持一致。
4)格式
使用格式: 类(或对象):: 方法名
如下三种主要使用情况:
- 对象 :: 实例方法(非静态方法)名
/*
函数式接口: Consumer 中的 void accpet(T t)
PrintStream 中的 void println(T t)
上述的方法达到了使用要求
*/
// 使用 lambda 表达式
Consumer<String> con1 = str -> System.out.println(str);
// 使用 方法引用
PrintStream ps = System.out; // 得到了 PrintStream 类的实例对象
// 对象 :: 方法名
Consumer<String> con1 = ps :: println;
- 类 :: 静态方法名
/*
函数式接口: Comparator 中的 int compare(T t1, T t2)
Integer 中的 int compare(T t1, T t2)
上述的方法达到了使用要求
*/
// 使用 lambda 表达式
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
// 使用 方法引用,类 :: 静态方法
Comparator<Integer> com1 = Integer :: compare;
- 类 :: 实例方法(非静态方法)名
这种方式有点特殊(并没有达到使用方法引用的要求)
/*
函数式接口: Comparator 中的 int compare(T t1, T t2)
String 类中的 int compare(t2)
可以看到,上述方法并没有达到使用方法引用的要求,但是可以通过 类 :: 实例方法 的方式引用
*/
// 使用 lambda 表达式
Comparator<String> com1 = (t1, t2) -> t1.compare(t2);
// 使用 方法引用,类 :: 实例方法
Comparator<String> com1 = String :: compare;
4.2、构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值即为构造器所属的类。
/*
Supplier 中的 T get() 方法
Enployee 的空参构造器:Enployee()
可以看出这个两个方法都没有参数
*/
// 使用 lambda 表达式
Supplier<Enployee> sup1 = () -> new Enployee();
// 使用 构造器引用
Supplier<Enployee> sup2 = Enployee :: new;
/*
Function 中的 R apply(T t) 方法
Enployee 的有参构造器:Enployee(Integer id)
可以看出这两个方法都有参数
*/
// 使用 lambda 表达式
Function<Integer, Enployee> fun1 = (t1) -> new Enployee(id);
// 使用 构造器引用
Function<Integer, Enployee> fun2 = Enployee :: new;
//调用 fun2 的 apply(T t)方法,传入一个 Integer 类型值,就相当于调用 Enployee 类的 带有 Integer 类型参数的构造方法
4.3、数组引用
和构造器引用几乎一模一样。
/*
Function 中的 R apply(T t) 方法
Enployee 的有参构造器:Enployee(Integer id)
可以看出这两个方法都有参数
*/
// 使用 lambda 表达式
Function<Integer, String[]> fun1 = (t1) -> new String[](t1);
// 使用 构造器引用
Function<Integer, String[]> fun2 = String[] :: new;
5、强大的 Stream API
5.1、Stream 介绍
- Java 8 中有两大最为重要的改变,第一个是 lambda 表达式;另一个则是 Stream API。
- Stream API(java.util.stream)把真正的函数式编程风格引入到 java 中。
- Stream 是 java 8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
5.2、为什么要使用 Stream
- 实际开发中,项目中多数数据源都来自于 Mysql、Oracle 等。但现在数据源更多了,有 MongDB、Radis 等,而这些 NoSql 的数据就需要在 Java 层面进行处理。
- Stream 和 Conllection 集合的区别:Conllection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者主要是面向内存处理,存储在内存中;后者主要是面向 CPU,通过 CPU 实现计算。
5.3、什么是 Stream
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
"集合讲的是数据,Stream 讲的是计算"
注意:
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,它们会返回一个持有结果的新 Stream。
- Stream 操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。
5.4、Stream 的操作步骤
1)创建 Stream
一个数据源(如:集合、数组),获取一个流。
- 创建 Stream 方式一:通过集合
// Collection 接口中有两个方法可以获取
default Stream<E> stream() // 返回一个顺序流
default Stream<E> parallelStream() // 返回一个并行流
- 创建 Stream 方式二:通过数组
// Arrays 类的静态方法 stream() 返回一个顺序流
- 创建 Stream 方式三:通过 Stream 类的
of()
方法 - 创建 Stream 方式四:创建无限流
- 通过 Stream 类的
iterat()
方法,迭代 - 通过 Stream 类的
generate()
方法,生成
- 通过 Stream 类的
2)中间操作
一个中间操作,对数据源的数据进行处理
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为"惰性求值"。
- 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) |
接收 lambda,从流中排除某些元素 |
distinct() |
筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) |
截断流,使其元素不超过给定数量 |
skip(long n) |
跳过元素,返回一个扔掉前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补。 |
- 映射
方法 | 描述 |
---|---|
map(Function f) |
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
maoToInt(ToIntFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap(Function f) |
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流练成一个流。 |
- 排序
方法 | 描述 |
---|---|
sorter() |
产生一个新流,其中按自然顺序排序 |
sorted(Compartor com) |
产生一个新流,其中按比较器顺序排序 |
3)终止操作(终端操作)
一旦执行终止操作,就会执行中间操作链,并产生结果。之后,不会再被使用。
- 匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate p) |
检查是否匹配所有元素 |
anyMatch(Predicate p) |
检查是否少匹配一个元素 |
noneMatch(Predicate p) |
检查是否没有匹配元素 |
findFirst() |
返回第一个元素 |
findAny() |
返回当前流中的任意元素 |
count() |
返回流中元素总数 |
max(Comparator c) |
返回流中最大值 |
min(Comparator c) |
返回流中最小值 |
froEach(Comparator c) |
内部迭代(使用 Collection 接口需要用户自己去做遍历迭代,称为外部迭代。相反使用 Stream 的内部迭代,它会自动帮你遍历迭代。) |
- 规约
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) |
可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) |
可以将流中元素反复结合起来,得到一个值。返回 Optional |
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因FGoogle 用它来进行网络搜索而出名。
- 收集
方法 | 描述 |
---|---|
collect(Collector c) |
将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法 |
Collector 接口中实现方法决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外,Collectors 实现类提供了很多静态方法可以很方便的创建常见收集器实例,具体方法与实例请参考 API。
6、Optional 类
为了尽可能的阻止空指针异常出现而出现的类。
Optional
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!