Java系列(六)| JDK1.8 新特性
JDK
已经出到17
了,但是你迭代你的版本,我用我的 8。JDK1.8
的一些新特性,当然现在也不新了,其实在工作中已经很常用了。
JDK1.8 都有哪些新特性?
JDK1.8
有不少新特性,我们经常接触到的新特性如下:
接口默认方法:
Java
8 允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字修饰即可Lambda
表达式和函数式接口:Lambda
表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda
允许把函数作为一个方法的参数(函数作为参数传递到方法中),使用Lambda
表达式使代码更加简洁,但是也不要滥用,否则会有可读性等问题,《Effective
Java》作者Josh Bloch
建议使用Lambda
表达式最好不要超过 3 行。Stream API
:用函数式编程方式在集合类上进行复杂操作的工具,配合Lambda
表达式可以方便的对集合进行处理。Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用
Stream API
对集合数据进行操作,就类似于使用SQL
执行的数据库查询。也可以使用Stream API
来并行执行操作。简而言之,
Stream API
提供了一种高效且易于使用的处理数据的方式。日期时间
API
:Java 8
引入了新的日期时间 API 改进了日期时间的管理。Optional
类:用来解决空指针异常的问题。很久以前Google Guava
项目引入了Optional
作为解决空指针异常的一种方式,不赞成代码被null
检查的代码污染,期望程序员写整洁的代码。受Google Guava
的鼓励,Optional
现在是Java 8
库的一部分。
Lambda 表达式了解多少?
Lambda
表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。
比如我们以前使用 Runnable
创建并运行线程:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running before Java8!");
}
}).start();
这是通过内部类的方式来重写 run 方法,使用 Lambda 表达式,还可以更加简洁:
new Thread( () -> System.out.println("Thread is running since Java8!") ).start();
当然不是每个接口都可以缩写成 Lambda
表达式。只有那些函数式接口(Functional
Interface
)才能缩写成 Lambda
表示式。
所谓函数式接口(Functional
Interface
)就是只包含一个抽象方法的声明。针对该接口类型的所有 Lambda 表达式都会与这个抽象方法匹配。
Java8 有哪些内置函数式接口?
JDK 1.8 API
包含了很多内置的函数式接口。其中就包括我们在老版本中经常见到的 Comparator 和 **Runnable
**,Java 8
为他们都添加了 @FunctionalInterface
注解,以用来支持 Lambda 表达式。
除了这两个之外,还有 Callable、Predicate、Function、Supplier、Consumer
等等。
Optional 了解吗?
Optional
是用于防范 NullPointerException
。
可以将 Optional
看做是包装对象(可能是 null
, 也有可能非 null
)的容器。当我们定义了 一个方法,这个方法返回的对象可能是空,也有可能非空的时候,我们就可以考虑用 Optional
来包装它,这也是在 Java 8 被推荐使用的做法。
Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
Stream 流用过吗?
Stream
流,简单来说,使用 java.util.Stream
对一个包含一个或多个元素的集合做各种操作。这些操作可能是 中间操作 亦或是 _终端操作_。终端操作会返回一个结果,而中间操作会返回一个 Stream
流。
Stream
流一般用于集合,我们对一个集合做几个常见操作:
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
- Filter 过滤
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa2", "aaa1"
- Sorted 排序
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa1", "aaa2"
- Map 转换
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
- Match 匹配
// 验证 list 中 string 是否有以 a 开头的, 匹配到第一个,即返回 true
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
// 验证 list 中 string 是否都是以 a 开头的
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
// 验证 list 中 string 是否都不是以 z 开头的,
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
- Count 计数
count
是一个终端操作,它能够统计 stream
流中的元素总数,返回值是 long
类型。
// 先对 list 中字符串开头为 b 进行过滤,让后统计数量
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); // 3
- Reduce
Reduce
中文翻译为:_减少、缩小_。通过入参的 Function
,我们能够将 list
归约成一个值。它的返回类型是 Optional
类型。
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
以上是常见的几种流式操作,还有其它的一些流式操作,可以帮助我们更便捷地处理集合数据。