1. 概述
Java 9 带来了丰富的功能集。虽然没有引入新的语言概念,但新增的 API 和诊断命令对开发者来说绝对值得关注。
本文将快速概览部分核心特性,完整特性列表可参考 OpenJDK 官方文档。
2. 模块化系统 – Jigsaw 项目
先从最重磅的改进说起——将模块化引入 Java 平台。
该模块系统提供了类似 OSGi 框架的能力:
- ✅ 支持模块依赖声明
- ✅ 可导出公共 API
- ✅ 隐藏实现细节
核心动机之一是构建模块化 JVM,使其能在内存受限的设备上运行。JVM 只需加载应用必需的模块和 API(模块详情参考)。
⚠️ 注意:JVM 内部 API(如 com.sun.*
)将不再对应用代码开放。
模块定义通过代码根目录下的 module-info.java
描述:
module com.baeldung.java9.modules.car {
requires com.baeldung.java9.modules.engines;
exports com.baeldung.java9.modules.car.handling;
}
此例中:
car
模块依赖engine
模块- 导出
handling
包供外部使用
深入示例可参考 OpenJDK 快速指南。
3. 全新 HTTP 客户端
期待已久的 HttpURLConnection
替代方案终于来了!
新 API 位于 java.net.http
包(注意更新:实际已移至 jdk.incubator.http
)。
核心特性:
- 支持 HTTP/2 协议
- 支持 WebSocket 握手
- 性能媲美 Apache HttpClient、Netty 和 Jetty
3.1. 快速 GET 请求
采用 Builder 模式,使用简单粗暴:
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandler.asString());
4. 进程 API 增强
进程控制和管理能力得到显著提升。
4.1. 进程信息获取
java.lang.ProcessHandle
类封装了新功能:
ProcessHandle self = ProcessHandle.current();
long PID = self.getPid();
ProcessHandle.Info procInfo = self.info();
Optional<String[]> args = procInfo.arguments();
Optional<String> cmd = procInfo.commandLine();
Optional<Instant> startTime = procInfo.startInstant();
Optional<Duration> cpuUsage = procInfo.totalCpuDuration();
current()
获取当前 JVM 进程Info
子类提供进程详细信息
4.2. 进程销毁
通过 destroy()
终止所有子进程:
childProc = ProcessHandle.current().children();
childProc.forEach(procHandle -> {
assertTrue("Could not kill process " + procHandle.getPid(), procHandle.destroy());
});
5. 语言层面小改进
5.1. Try-With-Resources 增强
Java 7 要求每个资源必须声明新变量,Java 9 支持直接使用 final 或等效 final 变量:
MyAutoCloseable mac = new MyAutoCloseable();
try (mac) {
// 使用 mac 执行操作
}
try (new MyAutoCloseable() { }.finalWrapper.finalCloseable) {
// 使用 finalCloseable 执行操作
} catch (Exception ex) { }
5.2. 钻石操作符扩展
现在可与匿名内部类搭配使用:
FooClass<Integer> fc = new FooClass<>(1) { // 匿名内部类
};
FooClass<? extends Integer> fc0 = new FooClass<>(1) {
// 匿名内部类
};
FooClass<?> fc1 = new FooClass<>(1) { // 匿名内部类
};
5.3. 接口私有方法
接口现在支持私有方法,用于拆分冗长的默认方法:
interface InterfaceWithPrivateMethods {
private static String staticPrivate() {
return "static private";
}
private String instancePrivate() {
return "instance private";
}
default void check() {
String result = staticPrivate();
InterfaceWithPrivateMethods pvt = new InterfaceWithPrivateMethods() {
// 匿名类
};
result = pvt.instancePrivate();
}
}
6. JShell 命令行工具
JShell 是 Java 的 REPL(Read-Eval-Print Loop)工具。
简单说,这是个交互式工具,可即时执行声明、语句和表达式。特别适合测试小段代码,无需创建带 main
方法的类。
工具位置:<JAVA_HOME>/bin/jshell
jdk-9\bin>jshell.exe
| Welcome to JShell -- Version 9
| For an introduction type: /help intro
jshell> "This is my long string. I want a part of it".substring(8,19);
$5 ==> "my long string"
支持历史记录和自动补全,还能保存/加载代码片段:
jshell> /save c:\develop\JShell_hello_world.txt
jshell> /open c:\develop\JShell_hello_world.txt
Hello JShell!
加载文件时会自动执行代码。
7. JCMD 子命令增强
探索 jcmd
工具的新子命令,例如查看 JVM 中所有类的继承结构。
以下示例展示 Eclipse Neon 中 java.net.Socket
的继承树:
jdk-9\bin>jcmd 14056 VM.class_hierarchy -i -s java.net.Socket
14056:
java.lang.Object/null
|--java.net.Socket/null
| implements java.io.Closeable/null (declared intf)
| implements java.lang.AutoCloseable/null (inherited intf)
| |--org.eclipse.ecf.internal.provider.filetransfer.httpclient4.CloseMonitoringSocket
| | implements java.lang.AutoCloseable/null (inherited intf)
| | implements java.io.Closeable/null (inherited intf)
| |--javax.net.ssl.SSLSocket/null
| | implements java.lang.AutoCloseable/null (inherited intf)
| | implements java.io.Closeable/null (inherited intf)
jcmd
首参数是目标 JVM 的进程 ID(PID)。
另一个实用子命令是 set_vmflag
,支持在线修改 JVM 参数,无需重启进程:
jcmd 14056 VM.flags -all # 查看所有可用标志
8. 多分辨率图像 API
java.awt.image.MultiResolutionImage
接口将不同分辨率的图像封装为单个对象。可根据 DPI 指标和图像变换获取特定分辨率变体。
java.awt.Graphics
类会根据当前显示 DPI 自动选择合适变体。基础实现类:
BufferedImage[] resolutionVariants = ....
MultiResolutionImage bmrImage
= new BaseMultiResolutionImage(baseIndex, resolutionVariants);
Image testRVImage = bmrImage.getResolutionVariant(16, 16);
assertSame("Images should be the same", testRVImage, resolutionVariants[3]);
9. 变量句柄
API 位于 java.lang.invoke
包,包含 VarHandle
和 MethodHandles
。提供:
- 等效于
java.util.concurrent.atomic
的操作 - 替代
sun.misc.Unsafe
的对象字段/数组元素访问 - 相近的性能水平
⚠️ 重要:Java 9 模块系统将禁止应用代码访问 sun.misc.Unsafe
。
10. 发布-订阅框架
java.util.concurrent.Flow
类提供支持 Reactive Streams 的接口。这些接口实现了:
- JVM 上异步系统的互操作性
- 可使用
SubmissionPublisher
工具类创建自定义组件
11. 统一 JVM 日志
为所有 JVM 组件引入通用日志系统。提供日志基础设施,但未在所有组件中添加实际日志调用。
日志框架定义了标签(如 gc
、compiler
、threads
等)。通过 -Xlog
参数启用日志:
java -Xlog:gc=debug:file=gc.txt:none ...
使用 -Xlog:help
查看所有选项和示例。运行时可通过 jcmd
修改配置:
jcmd 9615 VM.log output=gc_logs what=gc
12. 新增 API
12.1. 不可变集合
java.util.Set.of()
创建不可变集合:
Set<String> strKeySet = Set.of("key1", "key2", "key3");
返回的是内部类 java.util.ImmutableCollections.SetN
,继承自 java.util.AbstractSet
。尝试修改会抛出 UnsupportedOperationException
。
12.2. Optional 转 Stream
java.util.Optional.stream()
简化 Optional 元素的流式操作:
List<String> filteredList = listOfOptionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
13. 总结
Java 9 带来了模块化 JVM 及众多其他改进和新特性。这些更新显著提升了开发体验和运行时效率。
本文示例代码可在 GitHub 获取。