2. JDK93 新特性
2.1 模块化
JDK 9 引入了一个重要的特性,即模块化系统(Module System),也称为 Java 平台模块系统(Java Platform Module System,JPMS)。该特性的目标是改善 Java 平台的可伸缩性、安全性和性能。 模块化系统的主要思想是将 Java 平台分解为一系列模块,每个模块都有自己的封装和依赖关系。这些模块可以被组合在一起,以构建更大的应用程序或库。 下面是一些 JDK 9 模块化的关键概念:
- 模块(Module):模块是一个独立的单元,它由一组相关的包和类组成,并且具有清晰的边界。模块可以指定它所依赖的其他模块,并且可以明确地声明它对外部世界提供的公共 API。
- 模块化源代码(Module Source Code):在 JDK 9 中,源代码被组织为一组模块。每个模块都有自己的目录结构,其中包含模块定义文件
module-info.java
,用于声明模块的名称、依赖关系和导出的包等信息。 - 模块路径(Module Path):模块路径是一组目录或 JAR 文件的集合,用于加载和链接模块。与传统的类路径不同,模块路径允许显式地指定模块之间的依赖关系。
- 模块化 JAR 文件(Modular JAR File):在 JDK 9 中,可以使用新的 JAR 文件格式来创建模块化的 JAR 文件。这些 JAR 文件包含模块定义文件以及其他模块所需的类和资源。
- 模块化命令(Module-Related Commands):JDK 9 引入了一些新的命令,用于处理模块化系统。例如,
jdeps
命令用于分析模块之间的依赖关系,jlink
命令用于构建自定义运行时镜像。
模块化系统带来了许多好处,包括:
- 更好的可维护性:模块的边界清晰,易于理解和维护。
- 更好的可扩展性:模块之间的依赖关系更明确,使得应用程序和库的组合更加灵活。
- 更好的性能:模块化系统可以进行更精确的依赖分析和懒加载,从而提高应用程序的启动时间和运行时性能。
- 更好的安全性:模块可以明确声明它们的对外部世界的公共 API,从而提供更细粒度的访问控制。
需要注意的是,尽管 JDK 9 引入了模块化系统,但并不是所有的 Java 库和应用程序都需要立即迁移到模块化系统。在 JDK 9 中,仍然可以使用传统的类路径来运行非模块化的代码。但是,模块化系统为那些需要更好的可伸缩性和安全性的项目提供了一个强大的工具。
案例一
来个案例:首先在模块化中和需要再跟目录下定义module-info.java,里面上面你需要导入或者暴露的模块
/**
* @description:
* @author: shu
* @createDate: 2023/6/30 11:20
* @version: 1.0
*/
module Model {
// TODO: 2023/6/30 导入我们需要的模块
requires cn.hutool;
// TODO: 2023/6/30 导出我们需要的模块
exports com.shu.model;
}
我们调用方法,如果没有导入模块你会发现没有发现该类
import cn.hutool.core.convert.Convert;
/**
* @description:
* @author: shu
* @createDate: 2023/6/30 11:21
* @version: 1.0
*/
public class Test01 {
public static void main(String[] args) {
int a = 1;
//aStr为"1"
String aStr = Convert.toStr(a);
long[] b = {1,2,3,4,5};
//bStr为:"[1, 2, 3, 4, 5]"
String bStr = Convert.toStr(b);
}
}
案例二
当一个案例来说明 JDK 9 的模块化系统如何工作。 假设我们正在开发一个简单的图书管理应用程序,它由几个模块组成:bookmanagement.core
、bookmanagement.ui
和 bookmanagement.data
。其中,bookmanagement.core
模块提供核心业务逻辑,bookmanagement.ui
模块提供用户界面,bookmanagement.data
模块提供数据访问功能。 每个模块都有自己的目录结构,并且包含一个模块定义文件 module-info.java
。 首先,让我们看一下 bookmanagement.core
模块的 module-info.java
文件:
module bookmanagement.core {
// 需要的模块
requires bookmanagement.data;
// 暴露的模块
exports com.example.bookmanagement.core;
}
在这个文件中,我们声明了 bookmanagement.core
模块的名称,并且指定了它依赖于 bookmanagement.data
模块。我们还通过 exports
关键字明确声明了 com.example.bookmanagement.core
包对外的公共 API。 接下来,我们来看一下 bookmanagement.ui
模块的 module-info.java
文件:
module bookmanagement.ui {
requires bookmanagement.core;
exports com.example.bookmanagement.ui;
// 使用那个接口
uses com.example.bookmanagement.core.BookService;
}
在这个文件中,我们声明了 bookmanagement.ui
模块的名称,并且指定了它依赖于 bookmanagement.core
模块。我们通过 exports
关键字声明了 com.example.bookmanagement.ui
包对外的公共 API,并且使用 uses
关键字声明了它使用了 com.example.bookmanagement.core.BookService
接口。 最后,我们来看一下 bookmanagement.data
模块的 module-info.java
文件:
module bookmanagement.data {
exports com.example.bookmanagement.data;
provides com.example.bookmanagement.core.BookService
with com.example.bookmanagement.data.DefaultBookService;
}
在这个文件中,我们声明了 bookmanagement.data
模块的名称,并且通过 exports
关键字声明了 com.example.bookmanagement.data
包对外的公共 API。我们还使用 provides
关键字声明了 com.example.bookmanagement.core.BookService
接口的实现类为 com.example.bookmanagement.data.DefaultBookService
。 通过这些模块定义文件,我们明确了每个模块的名称、依赖关系和对外的公共 API。现在,我们可以使用 JDK 9 提供的命令进行编译、打包和运行。 例如,我们可以使用以下命令编译 bookmanagement.core
模块:
javac -d out/bookmanagement.core src/bookmanagement.core/module-info.java src/bookmanagement.core/com/example/bookmanagement/core/*.java
然后,我们可以使用 jar
命令将 bookmanagement.core
模块打包为一个模块化 JAR 文件:
jar --create --file=bookmanagement.core.jar --module-version=1.0 -C out/bookmanagement.core .
类似地,我们可以编译和打包 bookmanagement.ui
和 bookmanagement.data
模块。 最后,我们可以使用 java
命令来运行我们的应用程序,指定模块路径和主模块:
java --module-path bookmanagement.core.jar;bookmanagement.ui.jar;bookmanagement.data.jar --module bookmanagement.ui/com.example.bookmanagement.ui.Main
这样,我们就可以通过模块化系统构建和运行我们的图书管理应用程序。模块化系统帮助我们管理模块之间的依赖关系,确保模块的封装性和对外公共 API 的可控性。它提供了更好的可维护性、可扩展性和安全性。
JShell
JDK 9 引入了一个名为 jshell 的交互式命令行工具,它提供了一个方便的方式来进行 Java 代码的实时交互式探索和实验。jshell 允许您在不需要编写完整的 Java 类或应用程序的情况下,直接在命令行中编写和执行代码片段。 以下是一些关键特性和用法说明:
- 交互式编程:jshell 提供了一个交互式的命令行环境,您可以直接在命令行中输入和执行 Java 代码。您可以逐行输入代码片段,并立即查看结果。
- 即时反馈:每次您输入一行代码,jshell 都会立即执行并显示结果。这样可以快速验证代码并获得实时反馈,无需编译和运行完整的 Java 程序。
- 自动补全:jshell 提供了自动补全功能,可以帮助您快速输入代码和方法名。按下 Tab 键可以自动补全命令、类、方法等。
- 历史记录:jshell 会记录您在交互式会话中输入的代码,并允许您在以后的会话中检索和重复使用之前的代码。
- 异常处理:jshell 具有内置的异常处理机制,可以捕获并显示代码中的异常。它会提供有关异常的详细信息,以帮助您调试和修复问题。
- 定义变量和方法:您可以在 jshell 中定义变量和方法,并在后续代码片段中使用它们。这使得您可以逐步构建复杂的逻辑和功能。
- 多行输入:jshell 支持多行输入,您可以使用换行符(``)将一段代码分成多行。
要启动 jshell,您可以在命令行中输入 jshell
命令。然后,您可以开始输入 Java 代码并立即查看结果。以下是一个简单的示例会话:
$ jshell
| Welcome to JShell -- Version 9
| For an introduction type: /help intro
jshell> int x = 10;
x ==> 10
jshell> int y = 20;
y ==> 20
jshell> int sum = x + y;
sum ==> 30
jshell> System.out.println("The sum is: " + sum);
The sum is: 30
在这个示例中,我们定义了两个变量 x
和 y
,并计算它们的和。然后,我们使用 System.out.println
方法打印结果,jshell 对于快速测试代码片段、尝试新功能或进行教学和演示非常有用,它提供了一个轻量级且便捷的方式来进行 Java 代码的交互式探索和验证。
2.3 改进 Javadoc
Javadoc 工具可以生成 Java 文档, Java 9 的 javadoc 的输出现在符合兼容 HTML5 标准。
2.4 多版本兼容JAR
多版本兼容 JAR 是 JDK 9 中引入的一个功能,它允许您创建仅在特定版本的 Java 环境中运行的库程序,并通过使用 --release
参数来指定编译版本。 在 JDK 9 之前,使用旧版本的 JDK 编译的 JAR 文件在较新的 Java 环境中可能会遇到兼容性问题。通过多版本兼容 JAR,您可以使用较新的 JDK 编译您的库程序,并指定兼容的目标 Java 版本,以确保在该版本及更高版本的 Java 环境中运行。 以下是使用 --release
参数创建多版本兼容 JAR 的步骤:
- 编写您的库程序代码,并使用适当的 JDK 版本进行编译。假设您正在使用 JDK 11 编译您的库程序。
- 使用以下命令创建多版本兼容 JAR 文件:
javac --release <target-version> -d <output-directory> <source-files>
其中:
<target-version>
是您希望兼容的目标 Java 版本。例如,如果您希望兼容 Java 8,可以将<target-version>
设置为8
。<output-directory>
是输出目录,用于存放编译生成的类文件。<source-files>
是您的源代码文件或源代码目录。
例如,要创建一个兼容 Java 8 的 JAR 文件,您可以运行以下命令:
javac --release 8 -d output mylibrary/*.java
这将使用 JDK 11 编译您的库程序,并生成与 Java 8 兼容的类文件。
- 使用以下命令创建 JAR 文件:
其中:
<jar-file>
是要创建的 JAR 文件的名称。<input-directory>
是包含编译生成的类文件的目录。
例如,要创建名为 mylibrary.jar
的 JAR 文件,您可以运行以下命令:
jar --create --file mylibrary.jar -C output .
这将创建一个包含编译生成的类文件的 JAR 文件,现在,您可以将生成的多版本兼容 JAR 文件提供给其他开发人员,以便他们可以在特定的 Java 环境中使用您的库程序。 需要注意的是,使用多版本兼容 JAR 并不会自动提供对较新 Java 版本的新功能和改进的支持。它仅确保您的库程序可以在指定的目标 Java 版本及更高版本的环境中运行,但不会利用较新版本的语言特性。因此,您需要根据您的目标 Java 版本的要求进行相应的编码和功能选择。
2.5 集合工厂方法
在 Java 9 中,为集合框架引入了一组新的工厂方法,使创建和初始化集合对象更加简洁和方便。这些工厂方法属于 java.util
包中的 List
、Set
和 Map
接口,用于创建不可变的集合对象。 下面是 Java 9 中引入的集合工厂方法的示例用法:
- List.of() 方法用于创建不可变的列表对象。例如:
List<String> fruits = List.of("Apple", "Banana", "Orange");
在这个示例中,我们使用 List.of()
方法创建一个包含三个元素的不可变列表。
- Set.of() 方法用于创建不可变的集合对象。例如:
Set<Integer> numbers = Set.of(1, 2, 3, 4, 5);
在这个示例中,我们使用 Set.of()
方法创建一个包含五个元素的不可变集合。
- Map.of() 方法用于创建不可变的键值对映射。例如:
Map<String, Integer> studentScores = Map.of("Alice", 95, "Bob", 80, "Charlie", 90);
在这个示例中,我们使用 Map.of()
方法创建一个包含三对键值对的不可变映射。 这些集合工厂方法的特点是它们创建的集合对象都是不可变的,即无法修改集合中的元素。这种不可变性有助于编写更加健壮和可靠的代码,并提供更好的线程安全性。 需要注意的是,这些集合工厂方法创建的集合对象是不可变的,因此不能对其进行添加、删除或修改元素的操作。如果需要对集合进行修改操作,仍然可以使用传统的构造函数或其他方法来创建可变的集合对象。 另外,Java 9 中的集合工厂方法还提供了一系列的重载方法,用于处理不同数量的元素。您可以根据自己的需求选择适合的方法来创建集合对象。
2.6 私有接口方法
Java 9 引入了一项新的特性,即私有接口方法。这意味着接口可以包含私有的方法实现,这些方法只能在接口内部被调用,对于接口的实现类和其他类是不可见的。 私有接口方法可以帮助解决以下问题:
- 代码重用:通过在接口中定义私有方法,可以在多个默认方法之间共享代码逻辑,避免代码重复。
- 代码组织:私有接口方法可以将复杂的逻辑分解为更小的、可重用的方法,提高代码的可读性和维护性。
下面是一个示例,演示了如何在接口中定义和使用私有接口方法:
public interface Calculator {
// 公共的默认方法
int add(int a, int b);
default int subtract(int a, int b) {
return add(a, negate(b));
}
default int multiply(int a, int b) {
return a * b;
}
// 私有接口方法
private int negate(int number) {
return -number;
}
}
在上面的示例中,Calculator
接口定义了三个默认方法 add()
、subtract()
和 multiply()
,以及一个私有接口方法 negate()
。subtract()
方法内部调用了 add()
和 negate()
方法,而 negate()
方法是一个私有接口方法,只能在接口内部被调用。 通过私有接口方法,我们可以将 negate()
方法作为辅助方法来共享逻辑,并在多个默认方法中重复使用。 需要注意的是,私有接口方法不能被实现接口的类或其他类直接调用。它们仅用于在接口内部共享代码逻辑,提供更好的代码组织和代码重用。
2.7 该进的进程API
在 Java 9 中,对进程 API 进行了一些改进,以提供更好的控制和管理应用程序的进程。这些改进主要集中在 java.lang.Process
类和相关类的增强。 以下是 Java 9 中进程 API 的一些改进:
ProcessHandle
类:引入了一个新的ProcessHandle
类,它代表一个本地操作系统进程的句柄。通过ProcessHandle
类,您可以获取进程的 PID(进程标识符)、父进程的句柄、子进程的句柄以及其他进程相关的信息。ProcessHandle.Info
类:ProcessHandle
类中的info()
方法返回一个ProcessHandle.Info
对象,它提供了有关进程的详细信息,如进程的命令行参数、启动时间、累计 CPU 时间等。ProcessBuilder
类的改进:ProcessBuilder
类用于创建和启动新的进程。在 Java 9 中,ProcessBuilder
类添加了一些新的方法,例如inheritIO()
方法,它允许子进程继承父进程的标准输入、输出和错误流。还添加了redirectInput()、redirectOutput() 和 redirectError()
方法,用于重定向子进程的标准输入、输出和错误流。destroyForcibly()
方法:Process
类中的destroyForcibly()
方法用于强制终止进程,无论进程是否响应。这与destroy()
方法的区别在于,destroy()
方法会尝试优雅地终止进程,而destroyForcibly()
方法会强制终止进程。
这些改进使得在 Java 中管理和控制进程更加灵活和方便。您可以获取和操作正在运行的进程的信息,获取进程的 PID,以及更好地控制子进程的输入、输出和错误流。这些改进为进程管理和监控提供了更多的功能和选项。 请注意,Java 9 中的进程 API 的改进主要集中在进程管理方面,并没有引入类似于进程间通信的新功能。如果您需要进行进程间通信,可以使用其他 Java 平台提供的库,如 java.nio.channels
或第三方库。
/**
* @description:
* @author: shu
* @createDate: 2023/7/1 11:46
* @version: 1.0
*/
public class ProcessInfoDemo {
public static void main(String[] args) {
// 获取当前进程的 ProcessHandle
ProcessHandle currentProcess = ProcessHandle.current();
// 获取当前进程的PID
long pid = currentProcess.pid();
System.out.println("当前进程的PID:" + pid);
// 获取当前进程的信息
ProcessHandle.Info processInfo = currentProcess.info();
System.out.println("命令行:" + processInfo.command().orElse(""));
System.out.println("启动时间:" + processInfo.startInstant().orElse(null));
System.out.println("累计CPU时间:" + processInfo.totalCpuDuration().orElse(null));
}
}
2.8 改进的Stream API
在Java 9中,Stream API得到了一些改进,以提供更多的操作和增强功能。下面是Java 9中改进的一些Stream API功能:
takeWhile()
和dropWhile()
方法:引入了takeWhile()
和dropWhile()
两个新的Stream方法。takeWhile()
方法返回从流的开头开始的连续元素,直到遇到第一个不满足给定条件的元素。dropWhile()
方法返回从流的开头开始跳过满足给定条件的连续元素,直到遇到第一个不满足条件的元素。ofNullable()
方法:Stream接口中新增了一个ofNullable()
静态方法,它允许创建一个包含一个非空元素或空的Stream。如果提供的元素是非空的,将创建一个包含该元素的Stream;如果提供的元素为空,则创建一个空的Stream。iterate()
方法的重载:Stream.iterate()
方法现在支持一个谓词(Predicate)作为第二个参数。这样,您可以定义在生成迭代元素时应该终止迭代的条件。Stream
接口中的新方法:Stream
接口中添加了一些新的方法,如dropWhile()
、takeWhile()
、iterate()
的重载版本,以及forEachOrdered()
和toArray()
方法的重载版本。
这些改进使得Stream API更加灵活和功能更强大。您可以使用新的方法来处理更多的流操作场景,例如根据条件获取部分元素、创建包含空元素的流等。 以下是一个示例,展示了Java 9中改进的Stream API的一些用法:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @description:
* @author: shu
* @createDate: 2023/7/1 11:52
* @version: 1.0
*/
public class StreamApiDemo {
public static void main(String[] args) {
// takeWhile() 方法示例
List<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6)
.takeWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println("takeWhile 示例:" + numbers); // 输出:[1, 2, 3]
// dropWhile() 方法示例
List<Integer> numbers2 = Stream.of(1, 2, 3, 4, 5, 6)
.dropWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println("dropWhile 示例:" + numbers2); // 输出:[4, 5, 6]
// ofNullable() 方法示例
String name = null;
List<String> names = Stream.ofNullable(name)
.collect(Collectors.toList());
System.out.println("ofNullable 示例:" + names); // 输出:[]
// iterate() 方法的重载示例
List<Integer> evenNumbers = Stream.iterate(0, n -> n < 10, n -> n + 2)
.collect(Collectors.toList());
System.out.println("iterate 重载示例:" + evenNumbers); // 输出:[0, 2, 4, 6, 8]
//
//Stream 接口中的新方法示例
Stream.of("Java", "Python", "C++")
.forEachOrdered(System.out::println); // 输出:Java Python C++
}
}
这个示例展示了如何使用Java 9中改进的Stream API的一些方法,包括takeWhile()
、dropWhile()
、ofNullable()
、iterate()
和forEachOrdered()
等。您可以运行这个示例并观察输出结果,以便更好地理解这些改进的Stream API功能。
2.9 改进的 try-with-resources
在Java 7中引入了try-with-resources
语句,用于在代码块结束时自动关闭实现AutoCloseable
接口的资源。Java 9对try-with-resources
进行了改进,使其更加便利和灵活。 Java 9中改进的try-with-resources
语句具有以下特点:
- 支持资源的匿名变量:在Java 9之前,
try-with-resources
语句中的资源必须是事先声明的变量。在Java 9中,可以在try
关键字之后声明资源的匿名变量,并在try
语句块中使用。 - 资源的效率改进:在Java 9中,如果资源实现了
Closeable
接口,编译器会生成更高效的字节码,以减少关闭资源的开销。
下面是一个示例,演示了Java 9中改进的try-with-resources
语句的用法:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
* @description:
* @author: shu
* @createDate: 2023/7/1 16:04
* @version: 1.0
*/
public class TryWithResourcesDemo {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个示例中,我们使用try-with-resources
语句来自动关闭BufferedReader
资源。在try
关键字之后,我们创建了一个匿名的BufferedReader
对象,并将其包装在FileReader
中。在try
语句块中,我们可以使用reader
对象读取文件的内容。 当代码块结束时,无论是否发生异常,reader
资源都会自动关闭。这样可以确保资源的正确释放,而无需显式调用close()
方法。 需要注意的是,try-with-resources
语句可以处理多个资源,只需将它们用分号分隔即可。所有的资源都会在代码块结束时自动关闭,按照资源的声明顺序逆序关闭。 通过使用Java 9中改进的try-with-resources
语句,您可以更方便地处理资源的释放,并使代码更加简洁和易读。
2.10 改进的 @Deprecated 注解
在Java 9中,@Deprecated
注解得到了一些改进,以提供更多的注释功能和精确的描述。以下是Java 9中改进的@Deprecated
注解的特点:
forRemoval
属性:@Deprecated
注解新增了一个forRemoval
属性,用于指示该元素是否被打算用于移除。设置forRemoval=true
表示该元素将来会被移除,而设置forRemoval=false
表示该元素被废弃但将保留。since
属性:@Deprecated
注解中的since
属性用于指定元素被废弃的版本。通过指定since
属性,您可以明确说明从哪个版本开始该元素被废弃。- 更多注释说明:Java 9为
@Deprecated
注解添加了更多的注释说明,使得可以提供更详细和精确的描述,以便开发人员了解为什么该元素被废弃以及推荐使用的替代方法。
下面是一个示例,演示了Java 9中改进的@Deprecated
注解的用法:
public class DeprecatedExample {
@Deprecated(since = "1.5", forRemoval = true)
public void oldMethod() {
// 旧的方法实现
}
@Deprecated(since = "2.0", forRemoval = false)
public void deprecatedMethod() {
// 废弃的方法实现
}
public static void main(String[] args) {
DeprecatedExample example = new DeprecatedExample();
// 调用旧的方法,会收到编译器警告
example.oldMethod();
// 调用废弃的方法,不会收到编译器警告
example.deprecatedMethod();
}
}
在这个示例中,我们定义了一个DeprecatedExample
类,其中包含了两个方法:oldMethod()
和deprecatedMethod()
。我们使用改进的@Deprecated
注解对这两个方法进行注释。 在oldMethod()
方法上,我们设置了@Deprecated
注解的since
属性为"1.5",forRemoval
属性为true
,表示该方法从版本1.5开始被废弃,并且将来会被移除。 在deprecatedMethod()
方法上,我们设置了@Deprecated
注解的since
属性为"2.0",forRemoval
属性为false
,表示该方法从版本2.0开始被废弃,但是会保留。 在main()
方法中,我们实例化DeprecatedExample
对象,并分别调用了旧的方法和废弃的方法。编译器会对调用旧的方法产生警告,而对调用废弃的方法不会产生警告。 通过使用Java 9中改进的@Deprecated
注解,您可以提供更多的注释信息,包括指定废弃的版本和是否打算 移除,以便开发人员了解废弃元素的详细情况,并采取适当的行动。
2.11 钻石操作符
钻石操作符(Diamond Operator)是Java 7中引入的语法糖,用于在实例化泛型类时省略类型参数的重复声明。Java 9对钻石操作符进行了改进,使其在更多的情况下可以使用。 在Java 9之前,钻石操作符只能用于变量声明的右侧,并且只能省略类型参数的声明,不能省略类型参数的具体实例化。例如:
List<String> list = new ArrayList<>(); // 使用钻石操作符,省略了类型参数的声明
在Java 9中,钻石操作符的适用范围得到了扩展。现在,钻石操作符不仅可以用于变量声明的右侧,还可以用于匿名类的实例化、显式的构造函数调用、显式的方法调用等更多的情况。例如:
// 在匿名类的实例化中使用钻石操作符
Map<String, Integer> map = new HashMap<>() {
// 匿名类的实现
};
// 在显式的构造函数调用中使用钻石操作符
MyClass obj = new MyClass<>();
// 在显式的方法调用中使用钻石操作符
myMethod(new ArrayList<>());
通过在实例化时使用钻石操作符,可以使代码更简洁、更易读。编译器会根据上下文推断出类型参数,并自动进行类型推断。 需要注意的是,钻石操作符只能用于具有泛型类型的类的实例化。对于原始类型或无类型参数的类,仍需要显式地声明类型参数。 总结来说,Java 9对钻石操作符进行了改进,使其可以在更多的情况下使用,包括变量声明、匿名类的实例化、显式的构造函数调用、显式的方法调用等。这使得代码更加简洁和易读,同时不影响类型安全性。
2.12 改进的 Optional 类
在Java 8中引入的Optional类提供了一种用于处理可能为null的值的封装。Java 9对Optional类进行了一些改进,以提供更多的实用方法和增强功能 下面是Java 9中改进的Optional类的特点:
ifPresentOrElse()
方法:新增了ifPresentOrElse()
方法,用于在Optional对象有值时执行一个操作,否则执行一个备选操作。这样可以避免使用传统的if-else
语句来处理Optional对象的值。stream()
方法:Optional类中新增了stream()
方法,用于将Optional对象转换为一个包含单个元素的Stream。如果Optional对象有值,则返回一个包含该值的Stream,否则返回一个空Stream。or()
方法的重载:Optional.or()
方法现在支持Supplier函数接口,用于提供备选值。如果Optional对象为空,则使用Supplier提供的备选值。ifPresentOrElse()
方法的重载:ifPresentOrElse()
方法现在支持两个Consumer函数接口,分别用于处理Optional对象有值时的情况和没有值时的情况。这样可以提供更多的灵活性和定制化的处理逻辑。
下面是一个示例,演示了Java 9中改进的Optional类的用法:
import java.util.Optional;
/**
* @description:
* @author: shu
* @createDate: 2023/7/1 16:04
* @version: 1.0
*/
public class OptionalDemo {
public static void main(String[] args) {
Optional<String> optionalValue = Optional.of("Hello");
// 使用ifPresentOrElse()方法执行操作
optionalValue.ifPresentOrElse(
value -> System.out.println("Value: " + value),
() -> System.out.println("No value present")
);
// 使用stream()方法将Optional转换为Stream
optionalValue.stream()
.forEach(value -> System.out.println("Stream value: " + value));
// 使用or()方法提供备选值
Optional<String> emptyOptional = Optional.empty();
String result = emptyOptional.or(() -> Optional.of("Default Value"))
.orElse("Fallback Value");
System.out.println("Result: " + result);
}
}
在这个示例中,我们使用Optional类创建了一个包含值的Optional对象。然后,我们使用改进的方法对Optional对象进行操作。 使用ifPresentOrElse()
方法,我们指定了一个Consumer函数来处理Optional对象有值时的情况,并指定一个备选操作来处理Optional对象为空的情况。 使用stream()
方法,我们将Optional对象转换为一个包含单个元素的Stream,并对每个元素执行操作。 在or()
方法示例中,我们创建了一个空的Optional对象,并使用Supplier函数提供了一个备选值。如果Optional对象为空,则使用备选值。 通过使用Java 9中改进的Optional类,我们可以更方便地处理Optional对象,提供更多的处理选项,并使代码更简洁和可读。这些改进使得Optional类更加实用和强大。