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)。

核心特性:

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 包,包含 VarHandleMethodHandles。提供:

  • 等效于 java.util.concurrent.atomic 的操作
  • 替代 sun.misc.Unsafe 的对象字段/数组元素访问
  • 相近的性能水平

⚠️ 重要:Java 9 模块系统将禁止应用代码访问 sun.misc.Unsafe

10. 发布-订阅框架

java.util.concurrent.Flow 类提供支持 Reactive Streams 的接口。这些接口实现了:

  • JVM 上异步系统的互操作性
  • 可使用 SubmissionPublisher 工具类创建自定义组件

11. 统一 JVM 日志

为所有 JVM 组件引入通用日志系统。提供日志基础设施,但未在所有组件中添加实际日志调用。

日志框架定义了标签(如 gccompilerthreads 等)。通过 -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 获取。


原始标题:New Features in Java 9