入职新公司两个月了,用 JDK11 写了两个月 JDK8 的代码,再也憋不住想看看 JDK11 的新特性了。没用过 JDK9,10 . 那就说说 JDK8-JDK11 的变化吧。
# 新
- 语法增强
- 本地变量类型推断
Collection增强Stream增强Optional增强InputStream增强String增强
- 模块化开发
- 新工具
REPL交互式编程Low-Overhead Heap ProfilingFlight Recorder
- 新功能
- 源代码直接执行
- 完全支持
Linux容器 - 支持
Unicode 10 - 新支持的加密算法
HttpClient
- 垃圾回收器
ZGCEpsilon- 更好的
G1
- 移除与不再推荐使用的类库或功能
- 移除了
Java EE和CORBA Moudles - 将
Nashorn Javascript标记为不推荐 - 将
Pack200 Tools and API标记为不推荐
- 移除了
# JDK8-JDK11 的语法新特性
# 本地变量类型推断
since JDK10
Java10 以后可以用 var 定义一个局部变量,不用显式写出它的类型。但要注意,被 var 定义的变量仍然是静态类型,编译器会试图去推断其类型。所以,我们需要注意 1 . 不兼容的类型是不能重新赋值的! 2 . 只要编译器无法推断出变量类型,就会编译错误!
举个栗子:
# 基本使用
1 | public static void test1() { |
# 简化泛型声明
1 | /** |
幸好编译器会有类型提示。如下图。

# 简化 lambda 参数
1 | /** |
# 不兼容的类型赋值
这种直接编译失败,第一行和第二行已经推断出来了 str 是 String 类型。 第三行就不能赋值为 double 类型了。

# 不能推测的类型

但是,对于用习惯了 IDEA 快捷指令的我来说,这个 var 变量,对我来说毫无意义。感觉 jdk 是在炫技,但我毫无兴趣。
比如,在 idea 中,你输入 new ArrayList().var , 然后回车, IDEA 就会为你自动生成类型。
类似的还有 lists.for , lists.fori 等等。所以,个人并不推荐大家使用该语法,并非不能用,比如在 lambda 中使用替换类型时还是比较好的。如果你还没有掌握 xx.var 这种异能加持的话,想用就用吧。
总之,看别人写了个 var 时,不要发出 “哇藕, Java 还可以这么写”。
# Collection 增强
这个比较简单,就是 List , Set , Map 这三种集合多了两个方法 of 和 copyOf .
since jdk9
这里直接用三个栗子分别演示一下
# List
List.of 内部是创建一个的 immutable collections 。不可变集合。所以不可以增删改元素。
List.of() 和 List.copyOf() 都是创建的不可变集合
1 | /** |
# Set
Set 和 List 的用法类似。 同样的,也是不可变集合。需要注意的是, copyOf 方法,如果形参是可变集合,则返回的也是可变集合。
1 | /** |
# Map
1 | /** |
# Stream 增强
since JDK9
Stream 相关内容,新增了 4 个方法.
# 增加单个参数构造方法
1 | /** |
# 增加 takeWhile 方法
1 | /** |
# 增加 dropWhile 方法
1 | /** |
# iterate 重载方法
这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件) 来指定什么时候结束迭代。
需要注意的是:相比 JDK8 的方法, iterator 是第三个参数,第二个参数是 Prediction 。 Prediction 中进行判断的取值是迭代之后的数值。
1 | /** |
# Optional 增强
Since JDK 9
1 | /** |
# InputStream 加强
InputStream 终于有了一个非常有用的方法: transferTo ,可以用来将数据直接传输到 OutputStream ,这是在处理原始数据流时非常常见的一种用法。
还是举个栗子吧:
1 | /** |
# String 增强
# isBlank
判断目标字符串是否是空白字符。
使用功能上的问题,感觉还是例子来的舒服。
1 | /** |
# strip,stripLeading 与 stripTrailing
去除字符串的前后字符串
1 | // 全角空格 + 制表符 + 回车 + 换行 + 半角空格 + <内容> + 全角空格 + 制表符 + 回车 + 换行 + 半角空格 |
# repeat
1 | var strOri = "jdk11"; |
# lines
1 | /** |
# 模块化开发
Java9 引入了模块化, Java Platform Module System , java 平台模块系统,简称 JPMS 。
这里和大家一起做一下。
# 新建两个 module
我们新建两个模块 core 和 business。 如下图

# core 模块配置
- 新建一个类 RestResult ,表示公共调用的类。
1 | public class RestResult<T> { |
- 新建一个
module-info.java. 声明module信息.exports将com.fxb.learn.module.core这个包下的类都 export 出去。- 如果是一个
java的普通应用,则在src目录下,新建module-info.java文件即可。 - 如果是一个
maven应用,则需要在src/main/java目录下,新建module-info.java文件。
- 如果是一个
1 | // core 是module的名称 |
# business 模块配置
- 在
business模块下,新建一个module-info.java. 文件位置上core模块中的一致。
1 | module business { |
- 在
Idea中配置,business模块引用core模块。

# 写个例子看看
1 | /** |
# 新工具
JDK 还提供了一些新的工具, REPL 交互式编程, Low-Overhead Heap Profiling (免费的低耗能飞行记录仪和堆分析仪), Flight Recorder (黑盒子)
# REPL 交互式编程
你是否使用 j upter , java 也有了!
Java 提供了一个新的工具 jshell , Java 终于可以像 python , scala 等语言那样,交互式演示语法了

具体命令可以使用 /help 命令查看。
# Low-Overhead Heap Profiling
免费的低耗能飞行记录仪和堆分析仪。
通过 JVMTI 的 SampledObjectAlloc 回调提供了一个开销低的 heap 分析方式提供一个低开销的,为了排错 java 应用问题,以及 JVM 问题的数据收集框架。
具有一下功能:
- 提供用于生产和消费数据作为事件的
API - 提供缓存机制和二进制数据格式
- 允许事件配置和事件过滤
- 提供
OS,JVM和JDK库的事件
# Flight Recorder
Flight Recorder 源自飞机的黑盒子。 Flight Recorder 以前是商业版的特性,在 java11 当中开源出来,它可以导出事件到文件中,之后可以用 Java Mission Control 来分析。
两种启动方式:
可以在应用启动时配置 java -XX:StartFlightRecording
应用启动之后,使用 jcmd 来录制,如下代码:
1 | $ jcmd <pid> JFR.start # 启动记录仪 |
不过在 jdk11 是没办法查看 jfr 的。如果想看,安装 jdk12 吧。 不,可以试试 jdk16 , jdk16 也是 LTS 版本!・
JFR 是一套集成进入 JDK、JVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR, 用以在复杂环境中进行 "After-the-fact" 分析。还需要苦恼重现随机问题吗?JFR 让问题简化了很多
在保证低开销的基础上,JFR 提供的能力也令人眼前一亮,例如:我们无需 BCI 就可以进行 Object Allocation Profiling, 终于不用担心 BTrace 之类把进程搞挂了。对锁竞争、阻塞、延迟,JVM GC、SafePoint 等领域,进行非常细粒度分析。甚至深入 JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR 提供了标准的 Java,C++ 等扩展 API, 可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供 All-in-One 解决方案。而这一切都是内建在 JDK 和 JVM 内部的,并不需要额外的依赖,开箱即用。
# 新功能
# HttpClient
JDK 9 开始引入 HttpClient API 来处理 HTTP 请求。 从 JDK 11 开始,这个・正式进入标准库包。
参考网址:http://openjdk.java.net/groups/net/httpclient/intro.html
HttpClient 具有以下特性:
- 支持
HTTP1.1和HTTP2,websocket协议 - 支持同步和异步编程模型
- 将请求和响应主体作为响应式流 (
reactive-streams) 处理,并使用构建器模式 - 要发送
http请求,首先要使用其构建器创建一个HttpClient。这个构建器能够配置每个客户端的状态:- 首选协议版本 (
HTTP/1.1或HTTP/2) - 是否跟随重定向
- 代理
- 身份验证
- 首选协议版本 (
一旦构建完成,就可以使用 HttpClient 发送多个请求。
# HttpRequest
HttpRequest 是由它的构建器创建的。请求的构建器可用于设置:
- 请求
URI - 请求
Method(GET,PUT,POST) - 请求主体 (如果有)
- 超时时间
- 请求头
HttpRequest 构建之后是不可变的,但可以发送多次。
# Synchronous or Asynchronous
请求既可以同步发送,也可以异步发送。当然同步的 API 会导致线程阻塞直到 HttpResponse 可用。异步 API 立即返回一个 CompletableFuture ,当 HttpResponse 可用时,它将获取 HttpResponse 并执行后续处理。
# Data as reactive-streams
请求和响应的主体作为响应式流 (具有非阻塞背压的异步数据流) 供外部使用。 HttpClient 实际上是请求正文的订阅者和响应正文字节的发布者。 BodyHandler 接口允许在接收实际响应体之前检查响应代码和报头,并负责创建响应 BodySubscriber 。
HttpRequest 和 HttpResponse 类型提供了许多便利的工厂方法,用于创建请求发布者和响应订阅者,以处理常见的主体类型,如文件、字符串和字节。这些便利的实现要么累积数据,直到可以创建更高级别的 Java 类型(如 String ),要么就文件流传输数据。 BodySubscriber 和 BodyPublisher 接口可以实现为自定义反应流处理数据。
HttpRequest 和 HttpResponse 还提供了转换器,用于将 java.util.concurrent.Flow 的 Publisher/Subscriber 类型转换为 HTTP Client 的 BodyPublisher/BodySubscriber 类型。
# 请求协议 HTTP/2
Java HTTP Client 支持 HTTP/1.1 和 HTTP/2 。默认情况下,客户端将使用 HTTP/2 发送请求。发送到尚不支持 HTTP/2 的服务器的请求将自动降级为 HTTP/1.1 。
以下是 HTTP/2 带来的主要改进:
- 标头压缩。 HTTP/2 使用 HPACK 压缩,从而减少了开销。
- 与服务器的单一连接减少了建立多个 TCP 连接所需的往返次数。
- 多路复用。 在同一连接上,同时允许多个请求。
- 服务器推送。 可以将其他将来需要的资源发送给客户端。
- 二进制格式。 更紧凑。
由于 HTTP/2 是默认的首选协议,并且在需要的地方无缝地实现回退到 HTTP/1.1 ,那么当 HTTP/2 被更广泛地部署时, Java HTTP 客户端就无需修正它的应用代码。
具体的 Java Doc 可以参考: https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html
看了 Java Doc , 感觉使用起来比较简单。这里就不举例了。感兴趣的朋友,可以自行深入研究一下。
# 源代码直接执行
一个单文件源代码,也就是说,单独的 java 文件,有 main 方法,且只依赖 jdk 类库以及自己文件内部定义的类,可以直接用 java 执行而无需先编译再执行编译后的 class 文件了。
你可能问了,有什么用呢?我平时也不关系它是否生成了 class 文件。
如果你是做数据相关的工作,可能需要写一些脚本的时候,这却是轻松了很多。
1 | ➜ learn git:(master) ✗ ll |
可以看到确实没有 class 文件生成。
# 完全支持 Linux 容器
在 Docker 容器中运行 Java 应用程序一直存在一个问题,那就是在容器中运行的 JVM 程序在设置内存大小和 CPU 使用率后,会导致应用程序的性能下降。这是因为 Java 应用程序没有意识到它正在容器中运行。随着 Java10 的发布,这个问题总算得以解诀, JVM 现在可以识别由容器控制组 ( cgroups ) 设置的约束,可以在容器中使用内存和 CPU 约束来直接管理 Java 应用程序,其中包括:
- 遵守容器中设置的内存限制
- 在容器中设置可用的
CPU - 在容器中设置
CPU约束
# 支持 Unicode 10
Unicode 10 新增了 8518 个字符,总计达到了 136690 个字符。包括 56 个新的 emoji 表情符号。
JDK11 在 java.lang 下增加了 4 个类来处理:
CharacterData00.classCharacterData01.classCharacterData02.classCharacterData0E.class
# 新支持的加密算法
Java 实现了 RFC7539 中指定的 ChaCha20 和 Poly1305 两种加密算法,代替 RC4 。
RFC7748 定义的密钥协商方案更高效,更安全, JDK 增加了两个新的接口 XECPublicKey 和 XECPrivateKey 。
# 垃圾回收器
# ZGC
启用方法: -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
说明: ZGC , A Scalable Low-Latency Garbage collector ( Experimental ) ,一个可伸缩的低延时的垃圾回收器。 GC 暂停时间不会超过 10ms ,既能处理几百兆的小堆,也能处理几个 T 的大堆。和 G1 相比,应用吞吐能力不会下降超过 15% ,为未来的 GC 功能和利用 colord 指针以及 Load barriers 优化奠定了基础。初始只支持 64 位系统。
ZGC 的设计目标是:支持 TB 级内存容量,暂停时间低 ( <10ms ),对整个程序吞吐量的影响小于 15% 。将来还可以扩 展实现机制,以支持不少令人兴奋的功能,例如多层堆 (即热对象置于 DRAM 和冷对象置于 NVMe 闪存),或压缩堆。
GC 是 java 主要优势之一。然而,当 GC 停顿太长,就会开始影响应用的响应时间。消除或者减少 GC 停顿时长, java 将有可能在更广泛的应用场景中成长为一个更有吸引力的平台。此外,现代系统中可用内存不断增长,用户和程序员希望 JVM 能够以高效的方式充分利用这些内存,并且无需长时间的 GC 暂停时间。
ZGC 是一个并发,基于 region , 压缩型的垃圾收集器,只有 root 扫描阶段会 STW , 因此 GC 停顿时间不会随着堆的增长和存活对象的增长而变长。
# Epsilon
实验性质,生产环境不建议使用。
启用方法: -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
说明:开发一个处理内存分配但不实现任何实际内存回收机制的 GC , 一旦可用堆内存用完, JVM 就会退出,如果有 System.gc() 调用,实际上什么也不会发生 (这种场景下和 -XX:+DisableExplicitGC 效果一样), 因为没有内存回收,这个实现可能会警告用户尝试强制 GC 是徒劳的。
主要用途如下:
- 性能测试 (它可以帮助过滤掉
GC引起的性能假象) - 内存压力测试 (例如,知道测试用例应该分配不超过
1GB的内存,我们可以使用-Xmx1g -XX:+UseEpsilonGC,如果程序有问题,则程序会崩溃。 - 非常短的
JOB任务 (对于这种任务,GC是在浪费资源) VM接口测试Last-drop延迟 & 吞吐改进
# 更好的 G1
对于 G1 GC , 相比于 JDK8 , 升级到 JDK 11 即可免费享受到:并行的 Full GC , 快速的 CardTable 扫描,自适应的堆占用比例调整 ( IHOP ), 在并发标记阶段的类型卸载等等。这些都是针对 G1 的不断增强,其中串行 FullGC 等甚至是曾经被广泛诟病的短板,你会发现 GC 配置和调优在 JDK11 中越来越方便。
# 移除与不再推荐使用的类库或功能
Jdk9 到 Jdk11 ,陆续移除了一些类库或功能。
# 移除了 Java EE 和 CORBA Moudles
在 java11 中移除了不太使用的 JavaEE 模块和 CORBA 技术。
CORBA 来自于二十世纪九十年代, Oracle 认为,现在用 CORBA 开发现代 Java 应用程序已经没有意义了,维护 CORBA 的成本已经超过了保留它带来的好处。
但是删除 CORBA 将使得那些依赖于 JDK 提供部分 CORBAAPI 的 CORBA 实现无法运行。目前还没有第三方 CORBA 版本,也不确定是否会有第三方愿意接手 CORBA API 的维护工作。
在 java11 中将 java9 标记废弃的 Java EE 及 CORBA 模块移除掉,具体如下:
xml 相关被移除的:
java.xml.wsjava.xml.bindjava.xml.wsjava.xml.ws.annotationjdk.xml.bindjdk.xml.ws
只剩下 java.xml , java.xml.crypto.jdk.xml.dom 这几个模块。
其它被移除的 Java EE 和 CORBA 相关类库:
java.corbajava.se.eejava.activationjava.transaction(但是java11新增了一个java.transaction.xa模块)
# 其他移除的类库
com.sun.awt.AWTUtilitiessun.miss.Unsafe.defineClassThread.destroy()以及Thread.stop(Throwable)方法sun.nio.ch.disableSystemWideOverlappingFileLockCheck属性sun.locale.formatasdefault属性jdk snmp模块javafxjava Mission ControlRoot Certificates: 一些根证书被移除:Baltimore Cybertrust Code Signing CA, SECOM Root Certificate, AOL and Swisscom Root Certificates
其中,使用 java.lang.invoke.MethodHandles.Lookup.defineClass 来替代移除的 sun.miss.Unsafe.defineClass 。
# 将 Nashorn Javascript 标记为不推荐
将 Javascript 引擎标记为 Deprecate ,后续版本会移除,有需要的可以考虑使用开源的 GraalVM 。
# 将 Pack200 Tools and API 标记为不推荐
java11 中将 pack200 以及 unpack200 工具以及 java.tiljar 中的 Pack200 API 标记为 Deprecate 。因为 Pack200 主要是用来压缩 jar 包的工具,由于网络下载速度的提升以及 java9 引入模块化系统之后不再依赖 Pack200 ,因此这个版本将其标记为 Deprecate 。
# 预告
继 LTS JDK8 之后,又一 LTS , 你会用吗? JDK16 , 它来了。
# 对了
JDK11 写 JDK8 的代码?
代码始终是代码,写的再多,写不懂你我。
多看一点,就比其他们多懂一点。所以,你关不关注我,问题不大!
人情世故。不是世故,就是事故。问题真的不大。
文中所有代码,在 https://gitee.com/fangjiaxiaobai/learn_java/tree/master/fxb_jdk11
# 最后
希望和你一起遇见更好的自己
