Java 新特性系列已经从 Java 7 开始补充到 Java 9 了,每一篇的反馈都还不错。这个系列会持续更新到 Java 13 版本,然后在每个 Java 新版本发布时同步更新这个系列。你可以在文末找到这个系列的其他文章。
Java 9
中最大的亮点是 Java 平台模块化的引入,以及模块化 JDK。但是 Java 9
还有很多其他新功能,这篇文字会将重点介绍开发人员特别感兴趣的几种功能。
1. 模块化
Java 9
中的模块化是对 Java
的一次重大改进。但是模块化并不是最近才提出来的,我们经常使用的 maven
构建工具,就是典型的模块化构建工具。模块化不仅让模块命名清晰,写出高内聚低耦合的代码,更可以方便处理模块之间的调用关系。
Java 9 模块系统
在 Oracle 官方中为 Java 9
中的模块系统的定义如下:
the module, which is a named, self-describing collection of code and data. This module system.
直白翻译:模块是一个命名的,自我描述的代码和数据的集合。
Java 9
不仅支持了模块化开发,更是直接把 JDK
自身进行了模块化处理。JDK
自身的模块化可以带来很多好处,比如:
- 方便管理,越来越大的 JDK 在模块化下结构变得更加清晰。
- 模块化 JDK 和 JRE 运行时镜像可以提高性能、安全性、维护性。
- 可以定制 JRE,使用更小的运行时镜像,比如网络应用不需要 swing 库,可以在打包时选择不用,减少性能消耗。
- 清晰明了的模块调用关系,避免调用不当出来的各种问题。
上面提到了 JDK 自身的模块化,我们通过浏览 JDK 9 的目录结构也可以发现一些变化。
JDK 模块化
最明显的是在 JDK 9 中 jre 文件夹不存在了。下面是在 IDEA 中查看的 JDK 9 的依赖,命名规范的模块看起来是不是让人赏心悦目呢?
JDK 9 在 DEA (这里我放错图了,微信不支持改图)
当然,这篇文章主要介绍 Java 9 的新特性,而模块化是一个巨大改变,结合示例介绍下来篇幅会比较长,这里就不占用太多篇幅了。
模块化文章预告:如何编写一个模块化系统,如何打包让没有安装 Java 环境的系统运行编写的代码,都可以通过模块化选择运行时模块实现。我后面的文章就会通过一个模块化项目介绍到,有兴趣的可以关注我后续文章 😎。
2. 集合工厂方法
在 Java 9 中为集合的创建增加了静态工厂创建方式,也就是 of
方法,通过静态工厂 of
方法创建的集合是只读集合,里面的对象不可改变。并在不能存在 null
值,对于 set
和 map
集合,也不能存在 key
值重复。这样不仅线程安全,而且消耗的内存也更小。
下面是三种集合通过静态工厂创建的方式。
// 工厂方法创建集合 List<String> stringList = List.of("a", "b", "c", "d"); Set<String> stringSet = Set.of("a", "b", "c", "d"); Map<String, Integer> stringIntegerMap = Map.of("key1", 1, "key2", 2, "key3", 3); Map<String, Integer> stringIntegerMap2 = Map.ofEntries(Map.entry("key1", 1), Map.entry("key2", 2)); // 集合输出 System.out.println(stringList); System.out.println(stringSet); System.out.println(stringIntegerMap); System.out.println(stringIntegerMap2);
得到输出结果。
[a, b, c, d] [d, a, c, b] {key2=2, key1=1, key3=3} {key2=2, key1=1}
再次运行,得到输出结果。
[a, b, c, d] [a, c, b, d] {key3=3, key2=2, key1=1} {key2=2, key1=1}
为什么我贴了两次运行结果呢?主要是要展示通过 of
方法创建的 set
和 map
集合在遍历时,在每个 JVM 周期遍历顺序是随机的,这样的机制可以发下代码中有没有对于顺序敏感的异常代码。
这种只读集合在 Java 9 之前创建是通过 Collections.unmodifiableList
修改集合操作权限实现的。
List<String> arrayList = new ArrayList<>(); arrayList.add("达西"); arrayList.add("未读代码"); // 设置为只读集合 arrayList = Collections.unmodifiableList(arrayList);
静态工厂 of
方法创建的集合还有一个特性,就是工厂内部会自由复用已有实例或者创建新的实例,所以应该避免对 of
创建的集合进行判等或者 haseCode
比较等操作。
像下面这样,创建两个 List
,你会发现两个 List
的 hashCode
是一样的。
// 工厂可以自由创建新的实例或者复用现有实例,所以 使用 of 创建的集合,避免 == 或者 hashCode 判断操作 List<String> stringList = List.of("a", "b", "c", "d"); List<String> stringList2 = List.of("a", "b", "c", "d"); System.out.println(stringList.hashCode()); System.out.println(stringList2.hashCode()); // 输出结果 // 3910595 // 3910596
这也是使用 of
方法创建集合的优势之一,消耗更少的系统资源。这一点也体现在 of
创建的集合的数据结构实现上,有兴趣的同学可以自行研究下。
3. Stream API
Stream
流操作自从 Java 8
引入以来,一直广受好评。便捷丰富的 Stream
操作让人爱不释手,更让没看过的同事眼花缭乱,在介绍 Java 8
新特性时已经对 Stream
进行了详细的介绍,没看过的同学可以看下这篇:
还看不懂同事的代码?超强的 Stream 流操作姿势还不学习一下
当然,学习 Stream
之前要先学习 Lambda
,如果你还没有看过,也可以看下之前这篇:
还看不懂同事的代码?Lambda 表达式、函数接口了解一下
在 Java 9
中,又对 Stream
进行了增强,主要增加了 4 个新的操作方法:dropWhile,takeWhile,ofNullable,iterate。
下面对这几个方法分别做个介绍。
- takeWhile: 从头开始筛选,遇到不满足的就结束了。
// takeWhile ,从头开始筛选,遇到不满足的就结束了 List<Integer> list1 = List.of(1, 2, 3, 4, 5); List<Integer> listResult = list1.stream().takeWhile(x -> x < 3).collect(Collectors.toList()); System.out.println(listResult); // takeWhile ,从头开始筛选,遇到不满足的就结束 List<Integer> list2 = List.of(1, 2, 3, 4, 3, 0); List<Integer> listResult2 = list2.stream().takeWhile(x -> x < 3).collect(Collectors.toList()); System.out.println(listResult2);
输出结果。
[1, 2] [1, 2]
- dropWhile: 从头开始删除,遇到不满足的就结束了。
// dropWhile ,从头开始删除,遇到不满足的就结束了 List<Integer> list1 = List.of(1, 2, 3, 4, 5); List<Integer> listResult = list1.stream().dropWhile(x -> x < 3).collect(Collectors.toList()); System.out.println(listResult); // dropWhile ,从头开始删除,遇到不满足的就结束 List<Integer> list2 = List.of(1, 2, 3, 4, 3, 0); List<Integer> listResult2 = list2.stream().dropWhile(x -> x < 3).collect(Collectors.toList()); System.out.println(listResult2);
输出结果。
[3, 4, 5] [3, 4, 3, 0]
- ofNullable: 创建支持全 null 的 Stream.
Stream<Integer> stream = Stream.of(1, 2, null); stream.forEach(System.out::print); System.out.println(); // 空指针异常 // stream = Stream.of(null); stream = Stream.ofNullable(null); stream.forEach(System.out::print);
输出结果。
12null
- iterate: 可以重载迭代器。
IntStream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::print);
输出结果。
0123456789
在 Stream
增强之外,还增强了 Optional
,Optional
增加了可以转换成 Stream
的方法。
Stream<Integer> s = Optional.of(1).stream(); s.forEach(System.out::print);