文章目录
Java 9 新特性
Java9 是Java8后一个比较大的更新,于2017年9月发布。包含新特性比较多,此篇文章只总结下Java 9 版本的一些重要的新特性。并不完全。
Java 9 全部的新特性,请看官网:Java 平台,标准版 Oracle JDK 9 中的新增功能
Java各个版本的文档入口:Java平台,标准版文档
Java各个版本下载:https://jdk.java.net/archive/
1、模块化
Java 9
中的模块化是对 Java
的一次重大改进。但是模块化并不是最近才提出来的,我们经常使用的 maven
构建工具,就是典型的模块化构建工具。模块化不仅让模块命名清晰,写出高内聚低耦合的代码,更可以方便处理模块之间的调用关系。
module 是新增的Java代码访问权限级别,每个module可以包含多个package。
通过module-info.java文件来声明该文件夹及其子文件夹为一个模块,exports 关键字可以用来控制模块内哪些包对外暴露。
module store.api{
exports com.dingtalk.store.api;
}
使用module后,即使包中的类是public的,如果未通过exports
显式导出其程序包,则外部模块是不可调用的。
如果一个模块想要使用被另一个模块导出的package包中的类,可以用requires
关键字在其module-info.java文件中来导入(读取)目标模块的package包。
module store.service {
requires com.dingtalk.store.api;
}
Java9 module 与Maven module 很像,但功能完全不一样,后者是作为依赖构建来方便管理应用代码,而Java Module是在于安全性、访问性控制,通过exports/requires 控制模块内需要暴露和依赖的具体包。
2、接口支持定义私有方法
在 Java 8
中增加了默认方法,在 Java 9
中又增加了私有方法,这时开始接口中不仅仅有了定义,还具有了行为。我想这是出于代码构造上的考虑,如果没有私有方法,那么当多个默认方法的行为一样时,就要写多个相同的代码。而有了私有方法,事情就变得不一样了。
举个例子:
public class Jdk9Interface {
public static void main(String[] args) {
ChinaPeople chinaPeople = new ChinaPeople();
chinaPeople.sleep();
chinaPeople.eat();
chinaPeople.doXxx();
}
}
class ChinaPeople implements People {
@Override
public void sleep() {
System.out.println("躺着睡");
}
}
interface People {
void sleep();
default void eat() {
drink();
}
default void doXxx() {
drink();
}
private void drink() {
System.out.println("喝水");
}
}
例子中的接口 people 中的 eat()
和 doXxx()
默认行为一致,使用私有方法可以方便的抽取一个方法出来。
输出结果:
躺着睡
喝水
喝水
3、集合工厂方法
在 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("java");
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
创建的集合的数据结构实现上,有兴趣的同学可以自行研究下。
4、增强流(Stream)API
Stream
流操作自从 Java 8
引入以来,一直广受好评。对于Java 8 的介绍可以看:Java 8 新特性-终极指南
当然,学习 Stream
之前要先学习 Lambda
,也是Java 8的内容。
在 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);
5、HTTP / 2 Client
Java 9
内置了新的 HTTP/2 客户端,请求更加方便。
随便访问一个不存在的网页。
HttpClient client = HttpClient.newHttpClient();
URI uri = URI.create("http://www.tianqiapi.com/api/xxx");
HttpRequest req = HttpRequest.newBuilder(uri).header("User-Agent", "Java").GET().build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());
String body = resp.body();
System.out.println(body);
输出得到的结果,这里是这个网站的报错信息。
There is no method xxxAction in ApiController
可能你运行的时候会报找不到 httpClient
模块之类的问题,这时候需要你在你项目代码目录添加 httpClient 模块
才能解决,添加方式看下面的图。
export
写自己的包路径,requires
写引入的模块名。
这也就是Java 9 新引进的模块化。
6、Java REPL - JShell
交互式的编程环境在其他语言如 Python 上早就有了,而 Java 上的交互式语言只到 Java 9
才出现。交互式的编程可以让开发者在输入代码的时候就获取到程序的运行结果,而不用像之前一样新建文件、创建类、导包、测试一系列流程。
JShell
中支持 tab
补全代码以及自动添加分号,下面通过一个例子演示 JShell
的使用。
-
进入 JShell. 查看帮助文档。
C:\Users>jshell | 欢迎使用 JShell -- 版本 9 | 要大致了解该版本, 请键入: /help intro jshell> /help | 键入 Java 语言表达式, 语句或声明。 | 或者键入以下命令之一: | /list [<名称或 id>|-all|-start] | 列出您键入的源 | /edit <名称或 id> | 编辑按名称或 id 引用的源条目 | /drop <名称或 id> | 删除按名称或 id 引用的源条目 | /save [-all|-history|-start] <文件> | 将片段源保存到文件。 | /open <file> | 打开文件作为源输入 | /vars [<名称或 id>|-all|-start] | 列出已声明变量及其值 | /methods [<名称或 id>|-all|-start] | 列出已声明方法及其签名 | /types [<名称或 id>|-all|-start] | 列出已声明的类型 | /imports | 列出导入的项 | /exit | 退出 jshell | /env [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>] ... | 查看或更改评估上下文 | /reset [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>]... | 重启 jshell | /reload [-restore] [-quiet] [-class-path <路径>] [-module-path <路径>]... | 重置和重放相关历史记录 -- 当前历史记录或上一个历史记录 (-restore) | /history | 您键入的内容的历史记录 | /help [<command>|<subject>] | 获取 jshell 的相关信息 | /set editor|start|feedback|mode|prompt|truncation|format ... | 设置 jshell 配置信息 | /? [<command>|<subject>] | 获取 jshell 的相关信息 | /! | 重新运行上一个片段 | /<id> | 按 id 重新运行片段 | /-<n> | 重新运行前面的第 n 个片段 | | 有关详细信息, 请键入 '/help', 后跟 | 命令或主题的名称。 | 例如 '/help /list' 或 '/help intro'。主题: | | intro | jshell 工具的简介 | shortcuts | 片段和命令输入提示, 信息访问以及 | 自动代码生成的按键说明 | context | /env /reload 和 /reset 的评估上下文选项 jshell>
-
定义一个变量:a = 10,遍历从 0 到 a 的数字。
jshell> int a =10; a ==> 10 jshell> for(int i=0;i<a;i++){System.out.println(i);} 0 1 2 3 4 5 6 7 8 9
-
定义一个集合,赋值 1,2,3,4,5。然后输出集合。
jshell> List list = List.of(1,2,3,4,5); list ==> [1, 2, 3, 4, 5] jshell> list list ==> [1, 2, 3, 4, 5]
-
查看输入过的代码。
jshell> /list 1 : int a =10; 2 : for(int i=0;i<a;i++){System.out.println(i);} 3 : List list = List.of(1,2,3,4,5); 4 : list
-
列出导入的包。
jshell> /imports | import java.io.* | import java.math.* | import java.net.* | import java.nio.file.* | import java.util.* | import java.util.concurrent.* | import java.util.function.* | import java.util.prefs.* | import java.util.regex.* | import java.util.stream.*
-
将代码保存到文件并退出。
jshell> /save d:/JShell.java jshell> /exit 再见
在 D 盘看到的保存的代码片段。
操作起来还是挺简单的,还记得上面介绍集合工厂 of
方法创建出来的 set
和 map
数据在每个 JVM
周期里是无序的嘛?也可以用 JShell
实验下。
7、JVM 调优的新特性
第一个:删除 JDK 8 中已弃用的垃圾收集器 (GC) 组合
-
这意味着以下 GC 组合不再存在:
-
DefNew + CMS
-
ParNew + SerialOld
-
增量CMS
-
-
并发标记扫描 (CMS) 的“前台”模式也已被删除。以下命令行标志已被删除:
-
-Xincgc
-
-XX:+CMSIncrementalMode
-
-XX:+UseCMSCompactAtFullCollection
-
-XX:+CMSFullGCsBeforeCompaction
-
-XX:+UseCMSCollectionPassing
-
-
命令行标志
-XX:+UseParNewGC
不再有效。ParNew 只能与 CMS 一起使用,而 CMS 需要 ParNew。因此,该-XX:+UseParNewGC
标志已被弃用,并且可能会在未来的版本中被删除。
第二个:使 G1 成为默认垃圾收集器
- 使垃圾优先 (G1) 成为 32 位和 64 位服务器配置上的默认垃圾收集器 (GC)。对于大多数用户来说,使用低暂停收集器(例如 G1)比以前默认的面向吞吐量的收集器(例如 Parallel GC)提供更好的整体体验。
- 增强垃圾优先 (G1) 垃圾收集器以自动确定几个重要的内存回收设置。以前必须手动设置这些设置以获得最佳结果。此外,修复了 G1 垃圾收集器的可用性、确定性和性能问题。
- 请参阅Java 平台中的 Garbage-First Garbage Collector ,标准版 HotSpot 虚拟机垃圾收集调优指南
第三个:弃用并发标记扫描 (CMS) 垃圾收集器
- 弃用并发标记扫描 (CMS) 垃圾收集器。
-XX:+UseConcMarkSweepGC
使用该选项在命令行上请求时会发出警告消息。Garbage-First (G1) 垃圾收集器旨在替代 CMS 的大多数用途。
8、其他更新
Java 9
中增加或者优化的功能远不止这些,上面只是列举了常用的一些新特性,更多的新特性如:
- 不能使用下划线 _ 作为变量名,因为它是一个关键字。
- Javadoc 支持 HTML5 并且支持搜索功能。
- Nashorn 引擎升级,更好的支持 Javascript.
- String 存储结构变更从 char -> byte
- 多Jdk版本共存jar:在同一个Jar包可以包含多个Java版本的class文件,在不同Jdk环境下使用对应该 jdk 版本的 jar。(这对算是用户很友好的功能)
- …
新特性很多,感兴趣的可以自己了解下。
Java 9 全部的新特性,请看官网:Java 平台,标准版 Oracle JDK 9 中的新增功能