JDK 1.9:Java SE 9新特性解读与应用

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:JDK 1.9,即Java SE 9,是Oracle公司发布的Java编程语言和Java平台标准版的重要更新。该版本在Java 8的基础上带来了诸多新特性、优化和改进,其中包括模块化系统(Project Jigsaw)的引入、JShell(REPL)的加入、改进的HTTP客户端、私有接口方法以及多版本兼容JAR等。这些新特性旨在提高开发效率、性能和安全性。同时,JDK 1.9还增强了字符串处理、并行垃圾收集器(G1 GC)以及Javadoc的实用性和易读性。Java开发者通过学习这些新特性,可以更好地利用Java 9进行软件开发。 jdk 1.9.zip

1. JDK 1.9(Java SE 9)简介

在经历了多年的功能升级与完善,Java终于在2017年9月迎来了其平台的第九个主要版本——JDK 1.9,也称为Java SE 9。这次更新不仅标志着Java发展的一个重要里程碑,而且引入了多个革命性的新特性。JDK 1.9的推出将Java推向了一个新的高度,为开发者提供了更为强大和现代化的工具集,以支持他们创建高效、模块化、易于维护的应用程序。

与前一版本相比,Java SE 9在代码管理、HTTP通信、以及API设计等方面都做了显著的改进。其中最值得关注的革新包括了模块化系统的加入,即Project Jigsaw,这使得Java平台可以更好地支持大型应用的构建和维护。除此之外,JShell的引入提供了一个强大的即时反馈环境,允许开发者快速测试和调试代码片段。

在本章中,我们将探讨JDK 1.9带来的核心变化,为后续章节中对各个新特性的详细介绍打下基础。通过了解JDK 1.9的基本概况,你将获得一个全面的认识,从而更好地把握Java未来的发展方向。接下来的章节将会对每个新特性进行深入的解析和应用实践,使你能够充分利用Java SE 9带来的新工具,优化你的开发流程和应用性能。

2. 模块系统(Project Jigsaw)

2.1 模块系统的核心概念

2.1.1 模块化与封装的优势

在软件开发领域,模块化是一种将复杂系统分解为可管理部分的方法。Java 9 引入的模块系统有助于封装和组织代码,从而带来了多种优势。首先,模块化有助于提高代码的封装性,隐藏内部实现,只暴露必要的接口给外界使用。这不仅使得代码结构更清晰,而且还能有效地控制代码的依赖关系,避免命名冲突。

封装性还意味着应用程序能够在更大的范围内保持独立性。例如,我们能够创建一个业务逻辑模块,该模块对外部代码隐藏其内部实现细节。这样的封装减少了维护时的复杂性,因为开发者能够更清楚地了解修改某些部分代码可能会对其他部分产生哪些影响。

最后,模块化促进了代码的复用。有了明确的模块定义,开发者可以轻松地将一组类和接口打包到一个模块中,并在多个项目中重用这个模块,从而提高开发效率。

2.1.2 模块的定义和描述符

模块化在Java中的实现是通过模块声明,这是一个新的 module-info.java 文件。这个文件定义了模块的名称和对外部模块的依赖关系。例如,一个名为 com.example.mymodule 的模块声明可能如下所示:

module com.example.mymodule {
    requires java.logging;
    exports com.example.api;
    uses com.example.service-provider;
}

在这个声明中, module 关键字指定了模块名称, requires 指令声明了依赖的其他模块, exports 指令定义了向外部公开的包,而 uses 指令表示该模块将使用提供的服务。

模块描述符充当了模块系统中模块之间通信的合同。它定义了模块可以做什么,哪些是公开的,哪些是私有的,以及它需要谁来工作。这种描述性文件使得Java平台的模块系统既强大又灵活。

2.2 模块化实践与迁移指南

2.2.1 从传统的JAR包到模块

在模块化之前,Java应用主要由JAR包组成。这些JAR包包含了包含在它们内部的所有类文件,但并没有明确的界限或封装性。迁移到模块意味着重新思考项目的结构,以及如何组织代码以适应模块的定义。

迁移的第一步是识别你的应用程序中的代码块和这些代码块的依赖关系。传统上,这可能通过分析项目依赖和文档来完成。现在,有了模块系统,我们可以通过模块声明文件来清晰地表达这些依赖关系。

每当我们引入 module-info.java 文件并开始定义模块时,我们实际上在创建一个清晰的边界,界定了哪些包是公开的,哪些包是私有的。这对于大型项目尤其有价值,因为它可以帮助团队成员更好地理解项目的整体结构。

2.2.2 模块化代码的迁移策略

迁移现有代码到模块系统并不是一个简单的重构练习。它需要深思熟虑的战略和计划。一种常见的迁移策略是先创建一个包含所有已有类的单个模块。然后,逐步将代码分割成多个模块,每次迁移一小部分。

在这个过程中,我们可以使用 Java 9 的模块解析工具和模块化工具,如 jdeps ,来分析项目的依赖并帮助我们确定模块的边界。对于依赖的处理,我们可以使用 requires 指令显式声明依赖关系,同时, jlink 工具可以用来创建一个包含所有依赖模块的自定义运行时镜像,这样可以减小最终部署包的大小。

2.2.3 模块依赖和解析过程

模块依赖和解析是模块系统中的核心概念之一。每个模块可以通过 requires 指令声明它对其他模块的依赖,而模块系统会负责确保这些依赖项在运行时能够满足。如果一个模块声明了对另一个模块的依赖,而这个依赖在运行时找不到,那么应用将无法启动。

模块的解析过程从模块路径的入口点开始。这是运行时环境首先查看的模块集合。在解析过程中,模块系统会检查每个模块的 module-info.class 文件,以确定它需要哪个模块。如果一个模块声明了对另一个模块的依赖,模块系统会尝试在模块路径上找到这个依赖。如果找到,它将继续解析这个依赖模块的依赖。

如果在解析过程中遇到了循环依赖或无法解析的依赖,模块系统会抛出异常。因此,开发者需要仔细设计模块之间的关系,避免出现这些情况。

为了加深理解,我们可以使用下面的代码块,它展示了如何在 Java 9 中声明模块依赖:

module com.example.mymodule {
    requires com.example.lib1;
    requires com.example.lib2;
    // ... 其他依赖声明
}

通过这种方式,我们清晰地告诉模块系统, com.example.mymodule 需要 com.example.lib1 com.example.lib2 这两个模块。这种依赖关系在编译时和运行时都会被检查和强制执行。

以上内容深入探讨了JDK 1.9中模块系统的核心概念和模块化实践的迁移指南,解释了模块化带来的优势以及如何定义和解析模块。下一节将继续扩展模块化迁移的具体实践和策略。

3. JShell(REPL)

3.1 JShell的基本使用

3.1.1 JShell的启动和配置

JShell是Java 9引入的一个新的交互式编程环境,它允许开发者快速测试和运行Java代码片段。JShell是一个REPL(Read-Eval-Print Loop)环境,它是一个语言无关的工具,使得开发者可以不必写完整个类或方法就可以测试Java代码。

要启动JShell环境,你可以直接在命令行界面(CLI)中输入 jshell 命令,然后按回车键。JShell会立即启动并且在你的控制台中显示一个提示符,等待你输入代码。

$ jshell
|  Welcome to JShell -- Version 9
|  Type /help for help

jshell>

在JShell的提示符之后,你可以开始输入代码。每当你完成一行代码的输入后,JShell会立即执行它,并且展示结果。JShell支持包括变量声明、方法定义和表达式在内的所有Java语法,还包括一些特定的JShell指令,例如 /exit 用于退出JShell, /list 用于查看已经输入的代码。

JShell提供了强大的配置选项,可以让你自定义你的工作环境。例如,你可以通过 /set start 来设置JShell启动时执行的代码,通过 /set editor 来设置默认的代码编辑器。你可以使用 /help 指令查看所有的配置选项和JShell指令。

3.1.2 命令行操作和代码片段执行

JShell为开发者提供了一个直接在命令行环境中测试和执行代码片段的能力。这使得开发者可以快速看到代码运行的结果,而无需编写完整的Java程序。以下是一些基本的JShell命令行操作:

  • 插入代码片段 :通过输入一段Java代码,然后按回车键,JShell会执行这段代码。比如:
jshell> int a = 5;
|  创建了变量 a : int
jshell> System.out.println(a);
|  打印出 5
  • 使用变量和方法 :JShell中声明的变量和方法在后续的代码片段中可以直接使用。例如:
jshell> int b = 10;
|  创建了变量 b : int
jshell> a + b
|  表达式: int + int
|  错误: 不兼容的类型: int 不能转换为 java.lang.String
|  至少需要: String
|  a + b
|  ~~~~

在这个例子中,JShell会报错,因为期望的是一个字符串输出,而不是一个整数计算。

  • 获取帮助 :你可以使用 /help 指令来获取关于JShell的帮助信息,或者查看某个特定命令的用法。例如:
jshell> /help
|  /help: 打印帮助信息
|  /list: 显示已输入的代码片段
|  /var: 显示已声明的变量
  • 列出代码片段 :使用 /list 命令可以查看你输入的所有代码片段。这可以帮你快速回顾你的工作历史。
jshell> /list
1: int a = 5;
2: int b = 10;
3: a + b

通过这些基本的操作,JShell为Java开发者提供了一个灵活的测试环境,使得学习新特性和快速原型设计成为可能。然而,JShell的真正力量在于它能够即时提供反馈,并允许开发者在编写更复杂代码之前,快速尝试和验证他们的想法。

4. 改进的HTTP客户端

4.1 新HTTP客户端的特点

4.1.1 响应式流和异步支持

在JDK 1.9中引入的新HTTP客户端引入了对响应式流(Reactive Streams)的支持,这允许开发者在处理HTTP通信时使用异步方式,以及非阻塞地处理数据。响应式流是一种异步处理数据流和数据流控制信号的规范,它使用四个核心接口: Publisher , Subscriber , Subscription , 和 Processor 。这种支持对于需要处理大量并发或事件驱动的应用程序来说,特别有用。

响应式编程提供了一种声明式的数据流处理方式,可以更有效地处理I/O操作和网络通信。在新HTTP客户端中,这一特性通过 HttpClient API得以体现。开发者可以利用响应式API,例如Reactor或者RxJava来创建与HTTP客户端交互的应用程序,这样可以在不产生过多线程的情况下,以非阻塞方式发送和接收数据。

异步支持也意味着能够更高效地利用系统资源。当一个请求被发送时,当前线程不会被阻塞,而是可以继续处理其他任务,直到响应到达时才会进行相应的处理。这显著提高了程序的吞吐量,尤其是在高延迟的网络环境中。

4.1.2 WebSocket和HTTP/2的集成

新HTTP客户端的一个重要特性是它对WebSocket和HTTP/2协议的原生集成。WebSocket协议提供了一种在单个TCP连接上进行全双工通信的方式,非常适合需要实时通信的应用,如在线游戏、聊天室和即时消息服务。通过新HTTP客户端,开发者可以非常简单地创建和管理WebSocket连接,实现服务器和客户端之间快速、双向的通信。

HTTP/2带来了许多性能上的提升,包括多路复用、首部压缩、服务器推送等。新HTTP客户端利用这些特性,可以让开发者享受到更快速、更高效的应用性能。与传统的HTTP/1.x相比,HTTP/2可以减少网络延迟,提高应用加载速度和服务器响应能力。通过新HTTP客户端,开发者可以轻松地实现对HTTP/2的支持,无需深入了解底层协议细节。

为了支持这些特性,新HTTP客户端提供了一套简洁易用的API,使得开发者可以无需关注底层的网络编程细节,而是专注于业务逻辑的实现。这种集成方式大大降低了开发高性能网络应用的难度。

4.2 实战:新HTTP客户端应用开发

4.2.1 构建HTTP请求和处理响应

在JDK 1.9的新HTTP客户端中,构建HTTP请求和处理响应变得异常简单。首先,通过 HttpClient 类创建一个HTTP客户端实例,然后使用 HttpRequest HttpRequest.Builder 构建请求。请求构建完成后,可以通过 HttpClient send() 方法发送请求并获取响应。

下面是构建一个GET请求并获取响应的示例代码:

// 创建HttpClient实例
HttpClient client = HttpClient.newHttpClient();

// 构建GET请求
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/data"))
        .build();

// 发送请求并获取响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// 获取响应状态码
System.out.println(response.statusCode());
// 打印响应体内容
System.out.println(response.body());

在上述代码中, HttpResponse.BodyHandlers.ofString() 是一个预定义的响应体处理器,用于将响应体作为字符串返回。我们还可以根据需要选择其他处理器,如将响应体作为字节流处理。

处理响应时, HttpResponse 对象提供了多种方法来访问响应头、状态码等信息。开发者可以通过这些信息进行错误处理或进一步的业务逻辑处理。

4.2.2 使用WebSocket进行实时通信

新HTTP客户端也简化了WebSocket的使用。通过 HttpClient newWebSocketBuilder() 方法,可以创建一个 WebSocket 客户端并连接到服务器。下面的代码展示了如何建立WebSocket连接,并向服务器发送消息,同时处理来自服务器的消息。

// 创建HttpClient实例
HttpClient client = HttpClient.newHttpClient();

// 构建WebSocket请求
URI uri = URI.create("wss://example.com/ws");

// 创建WebSocket客户端并建立连接
WebSocket webSocket = client.newWebSocketBuilder()
        .buildAsync(uri, new WebSocket.Listener() {
            @Override
            public void onOpen(WebSocket webSocket) {
                // 连接打开后的操作
            }

            @Override
            public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, 
                                             @NotNull EndpointConfig config) {
                // 收到文本消息后的操作
                System.out.println("Received text message: " + data);
                return super.onText(webSocket, data, config);
            }

            @Override
            public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, 
                                              String reason) {
                // 连接关闭后的操作
                System.out.println("WebSocket closed with status code " + statusCode);
                return super.onClose(webSocket, statusCode, reason);
            }

            // 其他覆盖方法(如onBinary等)...
        }).join();

在这个例子中,我们使用 buildAsync 方法异步地建立WebSocket连接。连接建立后,我们通过实现 WebSocket.Listener 接口来处理各种WebSocket事件,比如打开连接、接收文本消息、关闭连接等。

4.2.3 整合HTTP/2优化网络性能

整合HTTP/2以优化网络性能,主要在于使用支持HTTP/2的 HttpClient 实例。默认情况下,新HTTP客户端在支持HTTP/2的条件下会自动使用HTTP/2。如果要强制使用HTTP/2,可以使用 HttpClient.Builder 并调用 version(HttpClient.Version.HTTP_2) 方法。

示例代码如下:

// 创建支持HTTP/2的HttpClient实例
HttpClient client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)
        .build();

// 使用HttpClient实例构建和发送请求,如前面章节所述...

在实际应用中,开发者只需确保服务器端支持HTTP/2,然后通过 version() 方法指定客户端版本即可。在支持HTTP/2的环境下,新HTTP客户端将利用HTTP/2的所有特性,如多路复用和服务器推送等,从而提升整体网络性能。

新HTTP客户端的使用使得在Java中实现高效、异步的网络通信变得更为容易和高效。通过使用响应式流和WebSocket的支持,以及HTTP/2的优化,开发者可以构建出更加强大和响应迅速的应用程序。

5. 私有接口方法与多版本兼容JAR

在Java SE 9中,除了模块化这样的重量级特性之外,还引入了一些小的但同样重要的语言变化,比如私有接口方法,以及对多版本兼容JAR的改进。这些新特性进一步增强了Java语言的表达能力和库开发者的生产效率。

5.1 掌握私有接口方法的使用

5.1.1 接口中的私有方法意义和用途

在Java SE 8中,接口新增了默认方法,这允许开发者向接口添加新的方法,同时保持向后兼容。然而,默认方法在某些情况下会导致代码重复和混乱。Java SE 9扩展了这一概念,引入了私有接口方法,使得接口内部的方法可以被默认方法调用,而不必公开给实现这些接口的类。

私有方法通常用于以下目的: - 实现私有辅助方法,避免重复代码。 - 分解复杂的默认方法,提高代码的可读性和可维护性。 - 提供一种机制,让接口能够定义供内部使用的工具方法。

5.1.2 如何在接口中定义和使用私有方法

定义私有接口方法非常简单,只需要在接口内部使用 private 关键字声明即可。下面是一个示例:

public interface AdvancedMath {
    default double sqrt(double value) {
        return squareRoot(value);
    }

    private double squareRoot(double value) {
        // 使用牛顿迭代法计算平方根的实现
        double t = value;
        double squareRoot = (value + 1.0) / 2.0;
        while (Math.abs(squareRoot - t) > 0.000001) {
            t = squareRoot;
            squareRoot = (t + value / t) / 2;
        }
        return squareRoot;
    }
}

在上面的代码中, squareRoot 方法被声明为私有方法,它仅仅被 sqrt 默认方法使用,这样就保持了接口的封装性。

5.2 多版本兼容JAR的策略和实现

5.2.1 项目对多版本Java的支持必要性

随着时间的推移,Java版本不断更新,旧版本的JVM仍然广泛部署在生产环境中。为了确保旧版本的JVM用户也能使用到新的库特性,库开发者需要创建多版本兼容的JAR文件。这样一来,Java应用程序可以使用最新的库特性,同时保持对旧版本JVM的兼容性。

5.2.2 创建和配置多版本兼容JAR

要创建一个多版本兼容的JAR文件,可以利用Java 9引入的multi-release JAR文件特性。这允许开发者在同一个JAR文件中包含多个版本的类文件。

以下是如何创建一个简单的多版本兼容JAR的步骤:

  1. 创建不同版本的源代码目录,并使用版本号作为目录名,例如 src/main/java-8 src/main/java-9
  2. 将特定于版本的实现放在相应的目录下。例如,只在Java 9版本中使用的私有接口方法应放在 src/main/java-9 目录。
  3. 构建项目时,使用Maven或Gradle等构建工具,并确保它们配置为包含所有版本的类文件。
  4. 构建工具会自动处理不同版本的文件,并在最终的JAR中使用特定的文件结构。

构建命令示例(Maven):

mvn clean package

5.2.3 使用multi-release JAR的场景和示例

多版本兼容JAR最适用的场景之一是,当一个库添加了新的API,这些API在新版本的Java中可用,但旧版本的Java不支持。通过多版本JAR,开发者可以在不破坏旧版本用户的情况下,向新版本用户发布新功能。

例如,假设有一个名为 lib.jar 的库,它在Java 9中引入了一个新的方法。下面是构建和使用multi-release JAR的示例:

构建示例(Maven):

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <release>9</release>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                    <manifestEntries>
                        <Multi-Release>true</Multi-Release>
                    </manifestEntries>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

使用示例:

public class Main {
    public static void main(String[] args) {
        if (Integer.parseInt(System.getProperty("java.specification.version")) >= 9) {
            // 在Java 9及以上版本,可以调用新增的方法
            NewFeature n = new NewFeature();
            System.out.println(n.newMethod());
        } else {
            // 在旧版本Java中,只能调用旧的方法
            OldFeature o = new OldFeature();
            System.out.println(o.oldMethod());
        }
    }
}

多版本兼容JAR文件的使用,确保了库能够向前兼容,同时逐步引入新特性,这有助于平滑地过渡到新的Java版本。这种策略已经成为现代Java库开发中不可或缺的一部分。

在下一章中,我们将探讨JDK 1.9引入的其他重要特性,以及它们对开发者实际编程工作的影响。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:JDK 1.9,即Java SE 9,是Oracle公司发布的Java编程语言和Java平台标准版的重要更新。该版本在Java 8的基础上带来了诸多新特性、优化和改进,其中包括模块化系统(Project Jigsaw)的引入、JShell(REPL)的加入、改进的HTTP客户端、私有接口方法以及多版本兼容JAR等。这些新特性旨在提高开发效率、性能和安全性。同时,JDK 1.9还增强了字符串处理、并行垃圾收集器(G1 GC)以及Javadoc的实用性和易读性。Java开发者通过学习这些新特性,可以更好地利用Java 9进行软件开发。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值