一文搞透 Java 9 新特性

大家好,我是大明哥,一个专注「死磕 Java」系列文章创作的程序员。 本文已收录到我的小站:skjava.com。


Java 9 发布于 2017 年 9 月 22 日。

JEP 261: 模块系统

在传统的 Java 应用中,类路径的机制限制了封装性。即使类被标记为 package-private,也可以被同一个类路径中的其他任意代码访问。而且随着应用程序规模的增长,类路径依赖变得越来越复杂。

Java 为了能够改进大型应用程序和库的封装性、可维护性和性能,在 Java 9 版本引入模块系统。模块系统是 Java 平台架构的一次重大变革,它旨在解决长期以来 Java 应用所面临的一些结构性问题,特别是在大型系统和微服务架构中。

其主要内容是:

  • 模块化 JDK:JDK 被划分为一系列的模块,使得可以只需要引入必需的 JDK 模块,减少了应用程序的体积。
  • 模块路径:取代了传统的类路径,用于指定模块的位置。
  • 模块声明 :使用 module-info.java 文件来声明模块,指定模块所需的其他模块和模块导出的包。

使用方法是:

新建模块后,我们就需要在 module-info.java 中定义模块信息了,信息主要包括如下几个部分:

  1. 使用module关键字定义模块,并指定模块的名称,例如:module java.module01 { }
  2. 使用requires关键字声明模块之间的依赖关系,例如:requires java.sql; 表示模块依赖于java.sql模块。
  3. 使用exports关键字声明模块中哪些包可以被其他模块访问,例如:exports com.skjava.module01.entity; 表示导出com.skjava.module01.entity包。

模块化系统带来三大好处:

  1. 更好的封装性:允许我们封装内部实现,只暴露必要的 API,从而减少了意外的依赖。
  2. 性能提升:模块化可以帮助 JVM 更高效地加载代码,提高启动速度和降低内存占用。
  3. 更好的安全性:可以限制哪些模块可以访问 JDK 的特定部分,从而提高安全性。

更多阅读:Java 9 新特性---模块化

JEP 269: 集合工厂方法

在 Java 9 之前创建一个不可变集合通常涉及多个步骤,包括创建、填充和包装。Java 9 引入该特性旨在提供一种简洁、安全且不可变的方式来创建集合(List、Set、Map)。

其内容包括:

  • List.of():创建一个不可变的 List,可以传递任意数量的元素。
  • Set.of():创建一个不可变的 Set,元素不可重复。
  • Map.of()Map.ofEntries():用于创建一个不可变的 Map。Map.of() 可以直接传递键值对,而 Map.ofEntries() 可以通过 Map.entry(k, v) 创建条目。

更多阅读:Java 9 新特性---新增只读集合和工厂方法

JEP 222:Jshell

在传统的 Java 开发中,即使是为了运行一个简单的程序,也需要编写完整的类和方法,然后编译和运行,这种方式对初学者很不友好,而且也不利于想要快速验证算法或逻辑的需求。

所以,Java 9 引入 Java Shell,其目的是提供一个官方的 Java REPL,支持快速测试、探索和实验 Java 代码片段。

Java Shell,是一个交互式的 Java REPL(Read-Eval-Print Loop),即一个用于交互式地执行 Java 代码的命令行工具。它的主要特点是:

  • 交互式执行:可以直接在命令行中输入并执行 Java 代码,无需创建完整的类文件。
  • 自动补全:支持代码自动补全,提高编码效率。
  • 临时变量和历史记录:自动为表达式的结果分配临时变量,并保存命令历史,方便回顾和修改。
  • 错误诊断:即使代码片段出错,JShell 也可以继续运行,提供错误信息帮助诊断问题。

能带来三大好处:

  • 提高效率:允许我们快速测试小片段的代码,而无需编写完整的应用程序。
  • 便于学习:为初学者提供了一个友好的环境,帮助他们理解 Java 语言的基础概念。
  • 快速原型验证:可以迅速验证想法和算法,加速开发过程。

更多阅读:Java 9 新特性---REPL 工具:JSheel 命令

JEP 213:接口支持私有方法

我们知道 Java 8 支持默认方法和静态方法,虽然提高了 Java 接口的灵活性和扩展性,但他们限制了接口的封装性和复用性。

为了提高接口的封装性和代码复用性,Java 9 支持在接口中定义私有方法和私有静态方法。

  • 私有方法:可以在接口内部定义私有方法,以帮助实现默认方法或其他私有方法。
  • 私有静态方法:类似地,可以定义私有静态方法,用于辅助接口内部的静态方法。

引入接口私有方法,除了增强接口的封装性和代码复用外,还能够使接口的设计者可以更清晰地区分公共 API 和内部实现细节,从而提供更干净、更简洁的公共 API。

更多阅读:Java 9 新特性---接口支持私有方法

Stream API 增强

Java 9 对 Stream API 新增了几个方法,这些方法使得 Stream 在处理数据流变得更加方便和强大了。

  1. takeWhile()** **:允许从流的开始处理元素,直到给定的谓词返回 false。这在处理有序流时特别有用。
  2. dropWhile()** **:与 takeWhile() 相反,它从流的开始丢弃元素,直到谓词返回 false,然后处理剩余的元素。
  3. ofNullable():用于创建单元素流,如果元素是 null,则返回一个空流,避免了 NullPointerException
  4. iterate() 的新重载:在 Java 8 中,iterate() 方法是无限的。Java 9 添加了一个重载,允许你提供一个谓词作为终止条件,这样就可以创建有限的流。

更多阅读:Java 9 新特性---Stream API的增强

Optional 的增强

Optional 是在 Java 8 中引入的,主要是用来解决 NullPointerException 的。Java 9 引入 3 个方法,进一步增强 Optional 的使用场景。

  1. stream() :允许将 Optional 对象转换为一个(最多只有一个元素的)流。这在将多个 Optional 对象组合到一个流中时特别有用。
  2. ifPresentOrElse():这个方法允许执行一个操作,如果 Optional 包含值,则执行一个操作,否则执行另一个操作。这提供了类似于"if-else"语句的功能。
  3. or():允许在当前的 Optional 为空时,提供一个替代的 Optional。这类似于 orElse()orElseGet(),但返回的是 Optional 对象而不是值。

更多阅读:Java 9 新特性---Optional 的增强

改进 try-with-resources

try-with-resources 是在 Java 7 中引入的,它能够完成资源的自动管理,例如文件和套接字的关闭,但是它需要在 try 语句内部声明和初始化,尽管它已经在外部声明了,这导致了代码的重复和冗余。

Java 9 对其进行了一个小的改动:允许使用在 try 语句块外部声明的资源。这意味着如果资源已经是 final 或者 effectively final(即实际上没有被后续代码修改),就可以在 try-with-resources 语句中直接使用,而无需在 try 语句内再声明一个新的局部变量。例如:

  • Java 7
java 复制代码
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (BufferedReader r = reader) {
    // 使用 reader
}
  • Java 9
java 复制代码
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) {
    // 使用 reader
}

更多阅读:Java 9 新特性---try-with-resources的升级

JEP 102:Process API

在 Java 9 之前,Java 提供的进程控制功能相对有限,很难直接获取关于系统进程的详细信息,也难以管理这些进程。这导致开发者需要依赖于特定平台的代码来完成这些任务,降低了代码的可移植性和易用性。

为了使 Java 应用能够更方便、更有效地管理和控制操作系统级别的进程,Java 9 引入 Process API,其目的是为了提供更好的控制和管理操作系统进程的能力,并使其在不同操作系统上的行为更加一致。其主要内容包括:

  • 增强的 Process 类 :Java 9 增强了 Process 类,提供了更多方法来管理和控制进程。
  • ProcessHandle 接口 :引入了 ProcessHandle 接口,它提供了获取进程的 PID(进程标识符)、父进程、子进程、进程状态等信息的能力。
  • 流式 API:利用流式 API,可以更方便地处理进程的信息和状态。

下面是获取本地所有进程的相关信息:

less 复制代码
    @Test
    public void processHandleTest() {
        ProcessHandle.allProcesses() // 获取所有进程
                .forEach(processHandle -> {
                    System.out.printf("Process ID: %s, Command: %s, Start Time: %s, User: %s%n",
                            processHandle.pid(),                                                  // 获取进程ID
                            processHandle.info().command().orElse("Unknown"),               // 获取进程的命令信息
                            processHandle.info().startInstant()
                                    .map(i -> i.toString()).orElse("Unknown"),              // 获取进程的启动时间
                            processHandle.info().user().orElse("Unknown"));                 // 获取运行进程的用户
                });

    }

执行结果(部分):

JEP 264:平台日志 API 和 服务

在 Java 9 之前,JDK 内部使用了多种日志记录机制,比如 System.out.printlnjava.util.logging,这种方法缺乏统一性,使得维护和控制日志变得复杂。因此,需要一个统一的日志系统,使 JDK 自身的日志更加一致和可管理。

Java 9 引入该特性其主要目的是为 JDK 提供一个统一的日志系统,它能够通过不同的日志框架来捕获 JDK 内部的日志信息。这不仅简化了 JDK 自身的日志处理,也为开发者提供了更大的灵活性和控制力,使得他们能够更好地管理和监控 JDK 产生的日志信息。

主要内容:

  1. 新的日志API :引入了一组新的日志API,称为 System.Logger API,用于 JDK 内部日志记录。
  2. 日志级别支持:支持不同的日志级别,例如 ERROR, WARNING, INFO, DEBUG, 和 TRACE。
  3. 日志服务接口:定义了一个服务接口,允许替换JDK的日志记录系统,或者将其桥接到其他日志框架。

下面代码是该日志 API 的示例:

arduino 复制代码
    @Test
    public void loggerTest() {
        System.Logger logger = System.getLogger("Java9Test");
        logger.log(System.Logger.Level.INFO, "这是 INFO 级别");
        logger.log(System.Logger.Level.WARNING, "这是 WARNING 级别");
        logger.log(System.Logger.Level.ERROR, "这是 ERROR 级别");
    }

它带来如下几个好处:

  1. 更好的日志管理:统一的API使日志管理变得更加简单和一致。
  2. 灵活性和可扩展性:开发者可以根据需要选择不同的日志实现,提高了灵活性和可扩展性。
  3. 更好的集成:使得JDK与现代日志框架(如 SLF4J, Log4j)之间的集成更加容易和无缝。

JEP 266: 反应式流(Reactive Streams)

在传统的阻塞 IO 和同步处理模式中,处理大量数据流或异步操作时常常面临效率和响应性问题。反应式编程是一种更有效地处理异步数据流的编程范式。Java 9 之前,并没有标准的方式在 Java 中实现反应式编程。因此 Java 9 引入反应式流,其目的是提供一种在 Java 中处理异步数据流的标准方式,同时保证高效率、低延迟,并支持背压(back-pressure),即允许接收者控制数据流的速度,防止被快速生产的数据淹没。

主要内容为:

  • 引入了 java.util.concurrent.Flow 类,它包含了几个嵌套的静态接口:PublisherSubscriberSubscriptionProcessor

    • Publisher:一个数据流的生产者。
    • Subscriber:订阅 Publisher 并处理数据的消费者。
    • Subscription:连接 PublisherSubscriber,允许 Subscriber 控制数据流。
    • Processor:充当生产者和消费者的中间人,即 PublisherSubscriber 的组合。

JEP 224: HTML5 Javadoc

在 Java 9 之前,Javadoc 主要使用较老的 HTML 4 格式。随着 Web 标准的发展,特别是 HTML5 的普及,有必要更新 Javadoc 以支持更现代的 Web 技术和标准。

Java 9 引入 HTML5 Javadoc,主要目的是将 Javadoc 文档更新为使用 HTML5,从而提高文档的兼容性、可用性和可访问性。这包括支持更多现代浏览器的特性,提供更好的布局和样式,以及改善搜索功能。

JEP 238: 多版本兼容 JAR 文件

在 Java 9 之前,一个 JAR 文件只能包含针对一个特定 Java 版本编译的类文件。随着 Java 平台的不断发展和版本的迭代,这限制了库和应用程序在不同 Java 版本间的兼容性。

Java 9 引入该特性的主要目的是提供一种机制,使得库开发者可以在单个 JAR 文件中包含针对不同 Java 版本编译的类文件。这样,应用程序可以在不同的 Java 运行时环境中运行,而无需更改或重新打包。

JEP 277:改进的弃用注解 @Deprecated

@Deprecated 注解用于标记过时的 API,但它并没有提供关于 API 为何过时、何时过时以及替代方案等信息。这导致开发者在使用或维护这些 API 时缺乏足够的信息。Java 9 对其进行了改进,增加了两个的属性:

  • since 属性用于指明从哪个版本开始 API 被弃用。
  • forRemoval:指出这个 API 是否计划在未来的版本中被移除。

该项特性可以让开发者能够更清晰地了解 API 的状态和未来规划,比如是否继续使用该 API、寻找替代方案。

JEP213:改进钻石操作符(Diamond Operator)

在 Java 7 中引入的钻石操作符简化了泛型实例的创建,但它不能用于匿名内部类。由于这个限制,开发者不得不在使用匿名内部类时指定泛型参数,这增加了代码的冗余和复杂性。

在 Java 9 中改进了钻石操作符,它可以与匿名内部类一起使用。这意味着当我们在创建一个匿名内部类的实例,并且该类具有泛型参数时,我们可以省略这些参数,Java 编译器会根据上下文推断出正确的类型。

增强 CompletableFuture

CompletableFuture 是在 Java 8 中引入的,它是一个非常强大的用于异步编程的工具,但是在实际使用过程中,发现它还有一些改进空间。故而,Java 9 对其进行了一些增强,内容如下:

  • 新增方法

    • completeAsync():允许异步地完成 CompletableFuture。它受一个 Supplier 函数和可选的 Executor,用于异步生成结果。
    • orTimeout():为 CompletableFuture 添加超时功能。如果在指定的时间内未完成,CompletableFuture 将会被异常地完成。
    • completeOnTimeout():类似于 orTimeout(),但在超时发生时,它会使用提供的值来完成 CompletableFuture,而不是抛出异常。
  • 改进异常处理

    • exceptionallyCompose():它允许在 CompletableFuture 遇到异常时,构建并返回一个新的 CompletionStage,这为异常处理提供了更多的灵活性。
  • 增强的组合操作

    • delayedExecutor(): 这是一个工具方法,用于创建一个延迟执行任务的 Executor。它可以和其他 CompletableFuture 方法结合使用,实现延迟执行的效果。
    • minimalCompletionStage()** 和 **completeMinimalFuture(): 这两个方法分别用于创建一个具有最小完成状态的 CompletionStage,以及从 CompletionStage 创建一个 CompletableFuture。这些方法有助于在不需要 CompletableFuture 完整功能的场景中减少资源消耗。

更多阅读:Java 9 新特性---改进 CompletableFuture

相关推荐
寻梦人121381 分钟前
缓存工具类
java·redis·缓存
摆烂工程师25 分钟前
Grok3 API 已经全网上线了!并且 Grok3 API 可以免费使用
前端·后端·程序员
增强39 分钟前
腾讯云人脸核身服务端实现(一)
后端
Alan5215941 分钟前
Java 使用 Quartz 实现定时任务(超简单入门)
java
Emma歌小白1 小时前
DataFrame 是否不为空
后端
拉丁解牛说技术1 小时前
AI大模型 prompt 工程指南实战
后端·程序员·mcp
有来技术1 小时前
全栈架构后端攻坚:基于 youlai - boot(开源)、Spring Boot 3 与 Spring Security 6 实现企业级权限系统全功能实战手册
java·spring boot·后端
烁3471 小时前
每日一题(小白)暴力娱乐篇23
java·开发语言·算法·娱乐
helloworld_工程师2 小时前
Spring AI应用:利用DeepSeek+嵌入模型+Milvus向量数据库实现检索增强生成--RAG应用(超详细)
java·后端
uhakadotcom2 小时前
使用Cloudflare Workers打造全栈应用:从静态网站到复杂后端
后端·面试·github