Java 9 带有丰富的特性集。虽然没有新的语言概念,但新的 API 和诊断命令肯定会引起开发人员的兴趣。
在这篇文章中,我们将对一些新功能进行快速、高级的了解;此处提供了新功能的完整列表。
让我们从最重要的开始——将模块化引入 Java 平台。
模块化系统提供类似于 OSGi 框架系统的功能。模块具有依赖关系的概念,可以导出公共 API 并将实现细节隐藏/私有。
这里的主要动机之一是提供模块化 JVM,它可以在可用内存少得多的设备上运行。JVM 只能使用应用程序所需的那些模块和 API 运行。查看此链接以了解这些模块是什么。
此外,无法再从应用程序代码访问com.sun.*等 JVM 内部(实现)API 。
简而言之,这些模块将在位于 java 代码层次结构顶部的名为module-info.java的文件中进行描述:
module com.baeldung.java9.modules.car {
requires com.baeldung.java9.modules.engines;
exports com.baeldung.java9.modules.car.handling;
}
我们的模块车需要模块引擎来运行并导出一个包进行处理。
期待已久的旧HttpURLConnection替代品。
新 API 位于java.net.http包下。
它应该支持HTTP/2 协议和WebSocket握手,其性能应该可以与Apache HttpClient、Netty和Jetty相媲美。
让我们通过创建和发送一个简单的 HTTP 请求来看看这个新功能。
更新:HTTP 客户端 JEP正在移至 Incubator 模块,因此它不再在包java.net.http中可用,而是在jdk.incubator.http 下可用。
快速获取请求
API 使用 Builder 模式,这使得快速使用变得非常容易:
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("http://postman-echo.com/get"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandler.asString());
进程 API 已针对控制和管理操作系统进程进行了改进。
(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子类提供有关该过程的详细信息。
(2)销毁进程
现在 - 让我们使用destroy()停止所有正在运行的子进程:
childProc = ProcessHandle.current().children();
childProc.forEach(procHandle -> {
assertTrue("Could not kill process " + procHandle.getPid(), procHandle.destroy());
});
(1)试用资源
在 Java 7 中,try-with-resources语法要求为语句管理的每个资源声明一个新变量。
在 Java 9 中还有一个额外的改进:如果资源被 final 或有效的 final 变量引用,则 try-with-resources 语句可以在不声明新变量的情况下管理资源:
MyAutoCloseable mac = new MyAutoCloseable();
try (mac) {
// do some stuff with mac
}
try (new MyAutoCloseable() { }.finalWrapper.finalCloseable) {
// do some stuff with finalCloseable
} catch (Exception ex) { }
(2)钻石运算符扩展
现在我们可以将菱形运算符与匿名内部类结合使用:
FooClass<Integer> fc = new FooClass<>(1) { // anonymous inner class
};
FooClass<? extends Integer> fc0 = new FooClass<>(1) {
// anonymous inner class
};
FooClass<?> fc1 = new FooClass<>(1) { // anonymous inner class
};
(3)接口私有方法
即将发布的 JVM 版本中的接口可以具有私有方法,可用于拆分冗长的默认方法:
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() {
// anonymous class
};
result = pvt.instancePrivate();
}
}}
JShell 是 read-eval-print 循环——简称 REPL。
简而言之,它是一个交互式工具,用于评估 Java 的声明、语句和表达式,以及一个 API。测试小代码片段非常方便,否则需要使用main方法创建一个新类。
jshell可执行文件本身可以在<JAVA_HOME>/bin文件夹中找到:
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!
代码片段在文件加载时执行。
让我们探索jcmd命令行实用程序中的一些新子命令。我们将获得 JVM 中加载的所有类及其继承结构的列表。
在下面的示例中,我们可以看到运行 Eclipse Neon 的 JVM 中加载的java.lang.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参数,而不需要重启JVM进程和修改它的启动参数。
您可以使用子命令jcmd 14056 VM.flags -all找出所有可用的 VM 标志
接口java.awt.image.MultiResolutionImage将一组具有不同分辨率的图像封装到一个对象中。我们可以根据给定的 DPI 指标和图像转换集检索特定于分辨率的图像变体,或者检索图像中的所有变体。
java.awt.Graphics类根据当前显示 DPI 指标和任何应用的转换从多分辨率图像中获取变体。
java.awt.image.BaseMultiResolutionImage类提供了基本实现:
BufferedImage[] resolutionVariants = ....
MultiResolutionImage bmrImage
= new BaseMultiResolutionImage(baseIndex, resolutionVariants);
Image testRVImage = bmrImage.getResolutionVariant(16, 16);
assertSame("Images should be the same", testRVImage, resolutionVariants[3]);
API 位于java.lang.invoke下,由VarHandle和MethodHandles组成。它在对象字段和数组元素上提供等效的java.util.concurrent.atomic和sun.misc.Unsafe操作,具有相似的性能。
使用 Java 9 Modular 系统将无法从应用程序代码访问sun.misc.Unsafe 。
java.util.concurrent.Flow类提供了支持Reactive Streams发布-订阅框架的接口。这些接口支持跨在 JVM 上运行的多个异步系统的互操作性。
我们可以使用实用程序类SubmissionPublisher来创建自定义组件。
此功能为 JVM 的所有组件引入了一个通用的日志记录系统。它提供了进行日志记录的基础设施,但它没有添加来自所有 JVM 组件的实际日志记录调用。它也不向 JDK 中的 Java 代码添加日志记录。
日志框架定义了一组标签——例如gc、编译器、线程等。我们可以使用命令行参数-Xlog在启动期间打开日志。
让我们使用'debug'级别将带有'gc'标签的消息记录到一个名为'gc.txt'的文件中,没有任何修饰:
java -Xlog:gc=debug:file=gc.txt:none ...
-Xlog:help将输出可能的选项和示例。可以使用jcmd命令在运行时修改日志记录配置。我们将设置 GC 日志为 info 并将它们重定向到一个文件 - gc_logs:
jcmd 9615 VM.log output=gc_logs what=gc
(1)不可变集
java.util.Set.of() – 创建给定元素的不可变集合。在 Java 8 中,创建一个由多个元素组成的 Set 需要几行代码。现在我们可以简单地做到这一点:
Set<String> strKeySet = Set.of("key1", "key2", "key3");
(2)可选择流式传输
java.util.Optional.stream()为我们提供了一种在 Optional 元素上使用 Streams 功能的简单方法:
List<String> filteredList = listOfOptionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
你适合学Java吗?4大专业测评方法
代码逻辑 吸收能力 技术学习能力 综合素质
先测评确定适合在学习