对许多人来说,Java 9 似乎是一个维护版本,它推动了 Jigsaw 项目,而 Jigsaw 在 Java 8 中无法实现。但是随着 JDK 中的新模块系统以及与之相关的许多内部变化,Java 9 也带来了一个开发人员工具箱中的许多很酷的新东西。以下是亮点:
JDK 9 中没有实现的另一件事是打包 JMH 工具并提供默认的 JMH 微基准测试以供应用程序使用——工作仍在进行中……
假设我们有一个 Java 9 之前的应用程序,它可以将文本导出为不同的格式——文本、pdf 或 word。应用程序结构如下:
exporter.IExporter 接口为不同的导出器提供通用接口:
public interface IExporter { public void export(String text, ByteArrayOutputStream baos); }
三个具体的导出器实现如下:
public class PdfExporter implements IExporter { public void export(String text, ByteArrayOutputStream baos) { Document document = null; try { document = new Document(); PdfWriter.getInstance(document, baos); document.open(); document.add(new Paragraph(text)); document.close(); } catch (DocumentException e) { if (document != null) { document.close(); } } } }
public class WordExporter implements IExporter { public void export(String text, ByteArrayOutputStream baos) { XWPFDocument document = null; try { document = new XWPFDocument(); XWPFParagraph paragraph = document.createParagraph(); XWPFRun run = paragraph.createRun(); run.setText(text); document.write(baos); } catch (IOException e) { try { if (document != null) { document.close(); } } catch (IOException e1) { } // some logging or proper error handling ... } } }
public class TextExporter implements IExporter { public void export(String text, ByteArrayOutputStream baos) { try { baos.write(text.getBytes()); } catch (IOException e) { // some logging or proper error handling ... } } }
exporter.Main 类是应用程序的入口点,如果可能具有以下实现,则仅使用 PdfExporter 类:
public static void main(String[] args) throws IOException { PdfExporter exporter = new PdfExporter(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); exporter.export("sample text", baos); FileOutputStream fos = new FileOutputStream("example.pdf"); fos.write(baos.toByteArray()); fos.close(); }
对于 PDF 和 Word 导出器,我们需要以下依赖项(我们使用 Maven 构建应用程序):
<dependencies> <dependency> <groupid>org.apache.poi</groupid> <artifactid>poi</artifactid> <version>3.16</version> </dependency> <dependency> <groupid>org.apache.poi</groupid> <artifactid>poi-ooxml</artifactid> <version>3.16</version> </dependency> <dependency> <groupid>com.lowagie</groupid> <artifactid>itext</artifactid> <version>2.1.7</version> </dependency> </dependencies>
因此,第一件事是为您的应用程序定义模块以及您愿意使用的 JDK 模块。您的初始应用程序在类路径中包含所有 JDK 类,但您可以在 Java 9 中将其限制为您需要的 JDK 模块。对于我们的简单应用程序,我们仅使用来自 JDK 的 java.io 类,它们是每个 Java 应用程序所需的 java.base 模块的一部分。我们的简单应用程序不需要其他 JDK 模块,因此我们可以为我们的应用程序使用非常简约的 JRE 版本。
对于实际应用我们可以很容易的推导出四个逻辑模块:
接下来是为我们定义的每个模块添加模块元数据。为此,我们为每个模块定义了一个 module-info.java 文件,如下所示:
每个模块的模块元数据具有以下内容(请注意三个具体的导出器如何依赖于 API 模块,并且我们不需要 java.base 模块,因为它是隐式需要的):
module exporter { exports exporter; }
module exporter.pdf { requires exporter; requires itext; exports exporter.pdf; }
module exporter.text { requires exporter; exports exporter.text; }
module exporter.word { requires exporter; }
module exporter.demo { requires exporter; requires poi.ooxml; requires poi.ooxml.schemas; exports exporter.word; }
我们仍然需要以某种方式从模块中引用第三方库(IText 和 Apache POI)。如果这些库是模块化版本,我们可以简单地下载并在相应的应用程序模块中使用它们。然而,我们不能做出这种假设,因此我们需要一种方法将它们作为“原样”的依赖项进行引用。出于迁移的目的,Java 平台提供了一种将第三方库用作 Java 模块的机制。如果它们在我们应用程序的类路径中,它们将包含在全局“未命名”模块中,并且库中的所有公共类都将对我们的应用程序模块可见。另一方面,由于为指定所有应用程序模块的模块化应用程序引入了新的模块路径,第三方库也可以使用 JAR 文件的名称(不带 .jar 扩展名)从模块路径中引用——这些就是所谓的自动模块。我们更喜欢第二种选择,因为它是一种更“模块化”的迁移方法。现在最后一件事是构建和运行我们的模块化应用程序。事实上,我们对模块结构有不同的策略:比如在同一个项目中(我们有一个)多个模块,一个项目一个模块甚至嵌套模块结构。我们也可以将 module-info.java 文件放在模块中我们想要的任何位置,只要指定为 javac 编译过程的一部分(因此位于模块目录的根目录中)。
在撰写本文时,Maven 仍然不支持构建模块化应用程序,而且 Java IDE(例如 Eclipse、IntelliJ 和 NetBeans)对 Jigsaw 模块有部分和实验性支持。出于这个原因,我们将演示如何从命令行构建我们的模块化应用程序。为此,我们将首先在原始项目根目录中创建 modules 目录。并按如下方式构建五个模块(请注意,Java 9 还为 javac 提供了一个 –module-source-path 参数,以便可以立即编译模块,但我们不会将其用于演示如何单独构建模块的目的)——我们需要在项目的根目录中,并且在 PATH 上有最新的 JDK 9 bin 目录:
mkdir modules\exporter modules\exporter.pdf modules\exporter.word modules\exporter.text modules\exporter.demo javac -d modules\exporter src\exporter\module-info.java src\exporter\IExporter.java javac --module-path modules -d modules\exporter.text src\exporter\text\module-info.java src\exporter\text\TextExporter.java javac --module-path modules;C:\Users\Martin\.m2\repository\com\lowagie\itext\2.1.7 -d modules\exporter.pdf src\exporter\pdf\module-info.java src\exporter\pdf\PdfExporter.java javac -cp C:\Users\Martin\.m2\repository\org\apache\poi\poi\3.16\poi-3.16.jar --module-path modules;C:\Users\Martin\.m2\repository\org\apache\poi\poi-ooxml\3.16;C:\Users\Martin\.m2\repository\org\apache\poi\poi-ooxml-schemas\3.16 -d modules\exporter.word src\exporter\word\module-info.java src\exporter\word\WordExporter.java javac --module-path modules -d modules\exporter.demo src\exporter\demo\module-info.java src\exporter\demo\Main.java
这里有两个要点。首先确保从第三方依赖项的 Maven 目录中排除源 jars(如果有的话),因为这可能会使编译器误以为您在同一目录(例如 poi-3.16 和 poi-3.16-sources.jar)中有重复的模块 jar。罐)。其次,对于自动模块,您可能会从不同的库中导出相同的包(也称为拆分包),这在 Java 9 中是不允许的。Apache POI 就是这种情况:poiexporter.word 模块所需的 strong> 和 poi-ooxml 库导出 org.apache.poi 包,如果两者都导致编译错误其中包括作为自动模块。为了在我们的案例中解决这个问题,我们在类路径中包含 poi 库,它构成了从自动模块中可见的所谓的全局未命名模块。现在,为了运行演示应用程序,您需要运行以下命令,该命令结合了上面用于编译的模块路径/类路径,并指向 Main 类作为我们应用程序的主要入口点:
javac --module-path modules -d modules\exporter.demo src\exporter\demo\module-info.java src\exporter\demo\Main.java
因此,您应该在当前目录中生成了 example.pdf 文件。
JSHell REPL 位于 JDK9 的 bin 目录中,您只需导航到该目录并键入 jshell 即可运行它。您可以(重新)定义和检查变量、导入、方法和类型。您还可以保存当前进度并在 JSHell 中再次加载它。对导入、类型和方法也有很棒的自动完成支持。为了说明 JSHell 的基础知识,请考虑在 JShell 中尝试以下命令集:
2 + 3 public int sum(int a, int b) { return a + b; } import java.util.logging import java.util.logging.* Logger x = null; class z {} /help /imports /vars /methods /env /types /list /list -all /save script.jsh /open script.jsh /exit
在 Java 9 之前,您可以使用两种方法来创建新进程——使用 Runtime.getRuntime().exec() 方法或 java.lang.ProcessBuilder 类,如下所示:
Process p = Runtime.getRuntime().exec("cmd /c notepad"); ProcessBuilder pb = new ProcessBuilder("cmd", "/c", “notepad"); Process p = pb.start();</pre>
然而,这在管理创建的进程或检查主机操作系统中的其他外部进程方面非常有限。出于这个原因,JDK 9 一方面向 java.lang.Process 类引入了许多新方法,而 java.lang.ProcessHandle 提供了一个新实用程序,它另一方面,允许以类似流的方式操作外部进程。以下是新流程 API 的一些自我描述示例:
LOGGER.info("PID: " + process.pid()); LOGGER.info("Number of children: " + process.children().count()); LOGGER.info("Number of descendants: " + process.descendants().count()); ProcessHandle.Info info = process.info(); LOGGER.info("Process command: " + info.command()); LOGGER.info("Info: " + info.toString()); // ProcessHandle handle = process.toHandle(); CompletableFuture<process> exitedFuture = process.onExit(); exitedFuture.whenComplete((p, e) -> { LOGGER.info("Process exited ... ");}); exitedFuture.get();</process></pre> <pre class="brush: java;">ProcessHandle[] processes = ProcessHandle.allProcesses().filter((pHandle) -> { return pHandle.info().toString().contains(name); }).toArray(ProcessHandle[] :: new); for(ProcessHandle process : processes) { LOGGER.info("Process details: " + process.info().toString()); } return processes;
G1 垃圾收集器并不是新的,而是在 JDK 7 中引入的。G1 的具体之处在于它在堆中按区域工作,而不是按代工作,这是一种更细粒度的垃圾清理方法。它旨在在遵守一组约束条件的同时尽可能多地清理垃圾。它是一个低暂停收集器,以高吞吐量换取低暂停时间。在 JDK 9 中,它成为默认的垃圾收集器,取代了吞吐量更高的并行 GC。此更改背后的假设是,较短的 GC 暂停时间带来的用户体验改善优于收集器的高吞吐量(并行 GC 就是这种情况)。该假设是否正确留给应用程序来决定——如果有人不愿意冒转移到 G1 的风险,仍然可以使用 -XX:-UseParallelGC 为 JVM 指定并行 GC
jdk.incubator.http.HttpClient 类提供了一个新的 HTTP 2.0 客户端,该类仍处于孵化阶段(意味着可能会出现更多变化)。
java.lang.StackWalker 类提供了一个新的堆栈跟踪检查 API。它可用于使用类似流的操作以细粒度的方式过滤和操作堆栈跟踪信息。
想要提供适合反应式编程范例的发布-订阅机制的应用程序必须提供 java.util.concurrent.Flow 类的实现。 JDK 提供的一种标准发布器实现了Flow.Publisher 接口,由java.util.concurrent.SubmissionPublisher 类提供。
标签2: Java教程地址:https://www.cundage.com/article/jcg-java-9-glance.html