Java 9 新特性
Java 9 发布于 2017 年 9 月 22 日,带来了很多新特性,其中最主要的变化是已经实现的模块化系统。接下来我们会详细介绍 Java 9 的新特性。
Java 9 新特性
- 模块系统:模块是一个包的容器,Java 9 最大的变化之一是引入了模块系统(Jigsaw 项目)。
- REPL (JShell):交互式编程环境。
- HTTP 2 客户端:
- HTTP/2标准是HTTP协议的最新版本,
- 新的 HTTPClient API 支持 **WebSocket **和 HTTP2 流以及服务器推送特性。
- 改进的 Javadoc:Javadoc 现在支持在 API 文档中的进行搜索。另外,Javadoc 的输出现在符合兼容 HTML5 标准。
- 多版本兼容 JAR 包:多版本兼容 JAR 功能能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本。
- 集合工厂方法:
- List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例。
- 私有接口方法:
- 在接口中使用private私有方法。我们可以使用 private 访问修饰符在接口中编写私有方法。
- 进程 API: 改进的 API 来控制和管理操作系统进程。引进 java.lang.ProcessHandle 及其嵌套接口 Info 来让开发者逃离时常因为要获取一个本地进程的 PID 而不得不使用本地代码的窘境。
- 改进的 Stream API:
- 改进的 Stream API 添加了一些便利的方法,使流处理更容易,并使用收集器编写复杂的查询。
- 改进 try-with-resources:
- 如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。
- 改进的弃用注解 @Deprecated:注解 @Deprecated 可以标记 Java API 状态,可以表示被标记的 API 将会被移除,或者已经破坏。
- 改进钻石操作符(Diamond Operator) :匿名类可以使用钻石操作符(Diamond Operator)。
- 改进 Optional 类:
- java.util.Optional 添加了很多新的有用方法,
- Optional 可以直接转为 stream。
- 多分辨率图像 API:定义多分辨率图像API,开发者可以很容易的操作和展示不同分辨率的图像了。
- 改进的 CompletableFuture API : CompletableFuture 类的异步机制可以在 ProcessHandle.onExit 方法退出时执行操作。
- 轻量级的 JSON API:
- 内置了一个轻量级的JSON API
- 响应式流(Reactive Streams) API:
- Java 9中引入了新的响应式流 API 来支持 Java 9 中的响应式编程。
更多的新特性可以参阅官网:What’s New in JDK 9
JDK 9 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk9-doc-downloads-3850606.html
在关于 Java 9 文章的实例,我们均使用 jdk 1.9 环境,你可以使用以下命令查看当前 jdk 的版本:
接下来我们将详细为大家简介 Java 9 的新特性:
序号 | 特性 |
---|---|
1 | 模块系统 |
2 | REPL (JShell) |
3 | 改进的 Javadoc |
4 | 多版本兼容 JAR 包 |
5 | 集合工厂方法 |
6 | 私有接口方法 |
7 | 进程 API |
8 | Stream API |
9 | try-with-resources |
10 | @Deprecated |
11 | 内部类的钻石操作符(Diamond Operator) |
12 | Optional 类 |
13 | 多分辨率图像 API |
14 | CompletableFuture API |
Java 9 模块系统
Java 9 最大的变化之一是引入了模块系统(Jigsaw 项目)。
模块就是代码和数据的封装体。
- 模块的代码被组织成多个包,每个包中包含Java类和接口;
- 模块的数据则包括资源文件和其他静态信息。
Java 9 模块的重要特征
- 是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。
- 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。
- 这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。
在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,如下所示。下面给出了一个模块com.mycompany.mymodule的最基本的模块声明。
module com.runoob.mymodule {
}
创建模块
接下来我们创建一个 com.runoob.greetings 的模块。
第一步
创建文件夹 C:>JAVA\src,然后在该目录下再创建与模块名相同的文件夹 com.runoob.greetings。
第二步
在 C:>JAVA\src\com.runoob.greetings 目录下创建 module-info.java 文件,代码如下:
module com.runoob.greetings { }
module-info.java 用于创建模块。这一步我们创建了 com.runoob.greetings 模块。
第三步
在模块中添加源代码文件,在目录 C:>JAVA\src\com.runoob.greetings\com\runoob\greetings 中创建文件 Java9Tester.java,代码如下:
package com.runoob.greetings;
public class Java9Tester {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
第四步
创建文件夹 C:>JAVA\mods,然后在该目录下创建 com.runoob.greetings 文件夹,编译模块到这个目录下:
C:/>JAVA> javac -d mods/com.runoob.greetings src/com.runoob.greetings/module-info.java src/com.runoob.greetings/com/runoob/greetings/Java9Tester.java
第五步
执行模块,查看输出结果:
C:/>JAVA> java --module-path mods -m com.runoob.greetings/com.runoob.greetings.Java9Tester
Hello World!
module-path 指定了模块所在的路径。
-m 指定主要模块。
-
创建目录 src/com.runoob.greetings
- 在此目录下创建文件 module-info.java
- 在此目录下创建文件夹 \com\runoob\greetings
- 在greetings目录下创建:Java9Tester
-
创建目录 mods/com.runoob.greetings
-
javac -d 输出目录 module-info.java目录 Java9Tester.java目录
-
java --module-path mods -m com.runoob.greetings/com.runoob.greetings.Java9Tester
- 最后运行 C:\JAVA\mods\ com.runoob.greetings \com\runoob\greetings\ Java9Tester.class
Java 9 REPL (JShell)
REPL(Read Eval Print Loop)意为交互式的编程环境。
JShell 是 Java 9 新增的一个交互式的编程环境工具。它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接 输入表达式并查看其执行结果。
执行 JSHELL
$ jshell
| Welcome to JShell -- Version 9-ea
| For an introduction type: /help intro
jshell>
查看 JShell 命令
输入 /help 可以查看 JShell相关的命令:
C:\Users\you>jshell
| 欢迎使用 JShell -- 版本 14.0.2
| 要大致了解该版本, 请键入: /help intro
jshell> /help
| 键入 Java 语言表达式, 语句或声明。
| 或者键入以下命令之一:
| /list [<名称或 id>|-all|-start]
| 列出您键入的源
| /edit <名称或 id>
| 编辑源条目
| /drop <名称或 id>
| 删除源条目
| /save [-all|-history|-start] <文件>
| 将片段源保存到文件
| /open <file>
| 打开文件作为源输入
| /vars [<名称或 id>|-all|-start]
| 列出已声明变量及其值
| /methods [<名称或 id>|-all|-start]
| 列出已声明方法及其签名
| /types [<名称或 id>|-all|-start]
| 列出类型声明
| /imports
| 列出导入的项
| /exit [<integer-expression-snippet>]
| 退出 jshell 工具
| /env [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>] ...
| 查看或更改评估上下文
| /reset [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>]...
| 重置 jshell 工具
| /reload [-restore] [-quiet] [-class-path <路径>] [-module-path <路径>]...
| 重置和重放相关历史记录 -- 当前历史记录或上一个历史记录 (-restore)
| /history [-all]
| 您键入的内容的历史记录
| /help [<command>|<subject>]
| 获取有关使用 jshell 工具的信息
| /set editor|start|feedback|mode|prompt|truncation|format ...
| 设置配置信息
| /? [<command>|<subject>]
| 获取有关使用 jshell 工具的信息
| /!
| 重新运行上一个片段 -- 请参阅 /help rerun
| /<id>
| 按 ID 或 ID 范围重新运行片段 -- 参见 /help rerun
| /-<n>
| 重新运行以前的第 n 个片段 -- 请参阅 /help rerun
|
| 有关详细信息, 请键入 '/help', 后跟
| 命令或主题的名称。
| 例如 '/help /list' 或 '/help intro'。主题:
|
| intro
| jshell 工具的简介
| keys
| 类似 readline 的输入编辑的说明
| id
| 片段 ID 以及如何使用它们的说明
| shortcuts
| 片段和命令输入提示, 信息访问以及
| 自动代码生成的按键说明
| context
| /env /reload 和 /reset 的评估上下文选项的说明
| rerun
| 重新评估以前输入片段的方法的说明
jshell> /imports
| import java.io.*
| import java.math.*
| import java.net.*
| import java.nio.file.*
| import java.util.*
| import java.util.concurrent.*
| import java.util.function.*
| import java.util.prefs.*
| import java.util.regex.*
| import java.util.stream.*
执行 JShell 命令
/imports 命令用于查看已导入的包:
jshell> /imports
| import java.io.*
| import java.math.*
| import java.net.*
| import java.nio.file.*
| import java.util.*
| import java.util.concurrent.*
| import java.util.function.*
| import java.util.prefs.*
| import java.util.regex.*
| import java.util.stream.*
jshell>
JShell 执行计算
以下实例执行 JShell 简单计算:
jshell> 3+1
$1 ==> 4
jshell> 13%7
$2 ==> 6
jshell> $2
$2 ==> 6
jshell>
JShell 创建与使用函数
创建一个函数 doubled() ,将传入的整型参数乘于 2 后返回:
jshell> int doubled(int i){ return i*2;}
| created method doubled(int)
jshell> doubled(6)
$3 ==> 12
jshell>
退出 JShell
输入 /exit 命令退出 jshell:
jshell> /exit
| Goodbye
Java 9 改进 Javadoc
javadoc 工具可以生成 Java 文档, Java 9 的 javadoc 的输出现在符合兼容 HTML5 标准。
Java 9 之前的旧版本文档
考虑以下文件代码 C:/JAVA/Tester.java:
实例
/**
* @author MahKumar
* @version 0.1
*/
public class Tester {
/**
* Default method to be run to print
* <p>Hello world</p>
* @param args command line arguments
*/
public static void main(String []args) {
System.out.println("Hello World");
}
}
使用 jdk 7 的 javadoc 生成文档:
C:\JAVA>javadoc -d C:/JAVA Tester.java
Loading source file tester.java...
执行以上命令会再 C:/JAVA 命令下生成文档页面,如下图所示:
Java 9 生成的文档兼容 HTML5 标准
使用 jdk 9 javadoc 命令中的 -html5 参数可以让生成的文档支持 HTML5 标准:
C:\JAVA> javadoc -d C:/JAVA -html5 Tester.java
Loading source file Tester.java...
- 去 C盘 ,就能直接找到这些文件。
执行以上命令会再 C:/JAVA 命令下生成文档页面,如下图所示:
Java 9 集合工厂方法
Java 9 List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例。
这些工厂方法可以以更简洁的方式来创建集合。
旧方法创建集合
实例
public class Tester {
public static void main(String []args) {
Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("C");
set = Collections.unmodifiableSet(set);
System.out.println(set);
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list = Collections.unmodifiableList(list);
System.out.println(list);
Map<String, String> map = new HashMap<>();
map.put("A","Apple");
map.put("B","Boy");
map.put("C","Cat");
map = Collections.unmodifiableMap(map);
System.out.println(map);
}
}
//unmodifiableMap 无法改变的
//如果运行了这个方法后,在改变值,就会抛异常。
Exception in thread "main" java.lang.UnsupportedOperationException
执行输出结果为:
[A, B, C]
[A, B, C]
{A=Apple, B=Boy, C=Cat}
新方法创建集合
Java 9 中,以下方法被添加到 List,Set 和 Map 接口以及它们的重载对象。
static <E> List<E> of(E e1, E e2, E e3);
static <E> Set<E> of(E e1, E e2, E e3);
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3);
static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries)
- List 和 Set 接口, of(…) 方法重载了 0 ~ 10 个参数的不同方法 。
- Map 接口, of(…) 方法重载了 0 ~ 10 个参数的不同方法 。
- Map 接口如果超过 10 个参数, 可以使用 ofEntries(…) 方法。
新方法创建集合
实例
public class Tester {
public static void main(String []args) {
Set<String> set = Set.of("A", "B", "C");
System.out.println(set);
List<String> list = List.of("A", "B", "C");
System.out.println(list);
Map<String, String> map = Map.of("A","Apple","B","Boy","C","Cat");
System.out.println(map);
Map<String, String> map1 = Map.ofEntries (
new AbstractMap.SimpleEntry<>("A","Apple"),
new AbstractMap.SimpleEntry<>("B","Boy"),
new AbstractMap.SimpleEntry<>("C","Cat"));
System.out.println(map1);
}
}
//Exception in thread "main" java.lang.UnsupportedOperationException 如果改变集合的对象也会报错。
[A, B, C]
[A, B, C]
{A=Apple, B=Boy, C=Cat}
{A=Apple, B=Boy, C=Cat}
Java 9 私有接口方法
在 Java 8之前,接口可以有常量变量和抽象方法。
我们不能在接口中提供方法实现。如果我们要提供抽象方法和非抽象方法(方法与实现)的组合,那么我们就得使用抽象类。
实例
fatal
英 /ˈfeɪtl/ 美 /ˈfeɪtl/ 全球(英国)
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
adj. 致命的;重大的;毁灭性的;命中注定的
public class Tester {
public static void main(String []args) {
LogOracle log = new LogOracle();
log.logInfo("");
log.logWarn("");
log.logError("");
log.logFatal("");
LogMySql log1 = new LogMySql();
log1.logInfo("");
log1.logWarn("");
log1.logError("");
log1.logFatal("");
}
}
//编写接口 日志
interface Logging {
/*String ORACLE = "Oracle_Database";
String MYSQL = "MySql_Database";*/
//定义了4个接口
void logInfo(String message);
void logWarn(String message);
void logError(String message);
void logFatal(String message);//致命的
//获取连接
void getConnection();
//关闭连接
void closeConnection();
}
//oracle 继承日志, 实现所有方法
final class LogOracle implements Logging {
@Override
public void logInfo(String message) {
getConnection();
System.out.println("Log Message : " + "INFO");
closeConnection();
}
@Override
public void logWarn(String message) {
getConnection();
System.out.println("Log Message : " + "WARN");
closeConnection();
}
@Override
public void logError(String message) {
getConnection();
System.out.println("Log Message : " + "ERROR");
closeConnection();
}
@Override
public void logFatal(String message) {
getConnection();
System.out.println("Log Message : " + "FATAL");
closeConnection();
}
//获取连接
@Override
public void getConnection() {
System.out.println("Open Database connection");
}
//关闭连接
@Override
public void closeConnection() {
System.out.println("Close Database connection");
}
}
final class LogMySql implements Logging {
@Override
public void logInfo(String message) {
getConnection();
System.out.println("Log Message : " + "INFO");
closeConnection();
}
@Override
public void logWarn(String message) {
getConnection();
System.out.println("Log Message : " + "WARN");
closeConnection();
}
@Override
public void logError(String message) {
getConnection();
System.out.println("Log Message : " + "ERROR");
closeConnection();
}
@Override
public void logFatal(String message) {
getConnection();
System.out.println("Log Message : " + "FATAL");
closeConnection();
}
@Override
public void getConnection() {
System.out.println("Open Database connection");
}
@Override
public void closeConnection() {
System.out.println("Close Database connection");
}
}
以上实例执行输出结果为:
Open Database connection
Log Message : INFO
Close Database connection
Open Database connection
Log Message : WARN
Close Database connection
Open Database connection
Log Message : ERROR
Close Database connection
Open Database connection
Log Message : FATAL
Close Database connection
在上面的例子中,每个日志方法都有自己的实现。
在 Java 8 接口引入了一些新功能——
- 默认方法和静态方法。我们可以在Java SE 8的接口中编写方法实现,仅仅需要使用 default 关键字来定义它们。
在 Java 8 中,一个接口中能定义如下几种变量/方法:
- 常量
- 抽象方法
- 默认方法
- 静态方法
实例
public class Tester {
public static void main(String []args) {
LogOracle log = new LogOracle();
log.logInfo("");
log.logWarn("");
log.logError("");
log.logFatal("");
LogMySql log1 = new LogMySql();
log1.logInfo("");
log1.logWarn("");
log1.logError("");
log1.logFatal("");
}
}
final class LogOracle implements Logging {
}
final class LogMySql implements Logging {
}
//接口是 Logging
interface Logging {
String ORACLE = "Oracle_Database";
String MYSQL = "MySql_Database";
//默认方法
default void logInfo(String message) {
getConnection();
System.out.println("Log Message : " + "INFO");
closeConnection();
}
default void logWarn(String message) {
getConnection();
System.out.println("Log Message : " + "WARN");
closeConnection();
}
default void logError(String message) {
getConnection();
System.out.println("Log Message : " + "ERROR");
closeConnection();
}
default void logFatal(String message) {
getConnection();
System.out.println("Log Message : " + "FATAL");
closeConnection();
}
// 静态方法
static void getConnection() {
System.out.println("Open Database connection");
}
static void closeConnection() {
System.out.println("Close Database connection");
}
}
以上实例执行输出结果为:
Open Database connection
Log Message : INFO
Close Database connection
Open Database connection
Log Message : WARN
Close Database connection
Open Database connection
Log Message : ERROR
Close Database connection
Open Database connection
Log Message : FATAL
Close Database connection
Java 9 不仅像 Java 8 一样支持接口默认方法,同时还支持私有方法。
在 Java 9 中,一个接口中能定义如下几种变量/方法:
- 常量
- 抽象方法
- 默认方法
- 静态方法
- 私有方法
- 私有静态方法
以下实例提取了冗余到通用方法,看起来明显更简洁:
实例
public class Tester {
public static void main(String []args) {
LogOracle log = new LogOracle();
log.logInfo("");
log.logWarn("");
log.logError("");
log.logFatal("");
LogMySql log1 = new LogMySql();
log1.logInfo("");
log1.logWarn("");
log1.logError("");
log1.logFatal("");
}
}
final class LogOracle implements Logging {
}
final class LogMySql implements Logging {
}
interface Logging {
//String ORACLE = "Oracle_Database";
//String MYSQL = "MySql_Database";
//私有方法。只能自己调用。
private void log(String message, String prefix) {
getConnection();
System.out.println("Log Message : " + prefix);
closeConnection();
}
//默认方法。可以调用私有方法
default void logInfo(String message) {
log(message, "INFO");
}
default void logWarn(String message) {
log(message, "WARN");
}
default void logError(String message) {
log(message, "ERROR");
}
default void logFatal(String message) {
log(message, "FATAL");
}
//私有 静态方法。自己调用
private static void getConnection() {
System.out.println("Open Database connection");
}
private static void closeConnection() {
System.out.println("Close Database connection");
}
}
以上实例执行输出结果为:
Open Database connection
Log Message : INFO
Close Database connection
Open Database connection
Log Message : WARN
Close Database connection
Open Database connection
Log Message : ERROR
Close Database connection
Open Database connection
Log Message : FATAL
Close Database connection
ava 9 改进的进程 API
在 Java 9 之前,Process API 仍然缺乏对使用本地进程的基本支持,例如获取进程的 PID 和所有者,进程的开始时间,进程使用了多少 CPU 时间,多少本地进程正在运行等。
Java 9 向 Process API 添加了一个名为 ProcessHandle 的接口来增强 java.lang.Process 类。
ProcessHandle 接口的实例标识一个本地进程,它允许查询进程状态并管理进程。
ProcessHandle 嵌套接口 Info 来让开发者逃离时常因为要获取一个本地进程的 PID 而不得不使用本地代码的窘境。
我们不能在接口中提供方法实现。如果我们要提供抽象方法和非抽象方法(方法与实现)的组合,那么我们就得使用抽象类。
ProcessHandle 接口中声明的 onExit() 方法可用于在某个进程终止时触发某些操作。
实例
public class Tester {
public static void main(String[] args) throws IOException {
//获取 notepad
ProcessBuilder pb = new ProcessBuilder("notepad.exe");
String np = "Not Present";
//启动他
Process p = pb.start();
/*Process ID : 5096
Command name : C:\Windows\System32\notepad.exe
Command line : Not Present
Start time: 2021-01-14T17:53:06.218
Arguments : Not Present
User : LAPTOP-01EONIAD\张三*/
ProcessHandle.Info info = p.info();
//pid
System.out.printf("Process ID : %s%n", p.pid());
//Command name : C:\Windows\System32\notepad.exe
System.out.printf("Command name : %s%n", info.command().orElse(np));
//Command line : Not Present
System.out.printf("Command line : %s%n", info.commandLine().orElse(np));
//Start time: 2021-01-14T17:53:06.218
System.out.printf("Start time: %s%n",
info.startInstant().map(i -> i.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()).orElse(np));
//Arguments : Not Present
System.out.printf("Arguments : %s%n",
info.arguments().map(a -> Stream.of(a).collect(
Collectors.joining(" "))).orElse(np));
//User : LAPTOP-01EONIAD\张三
System.out.printf("User : %s%n", info.user().orElse(np));
}
}
以上实例执行输出结果为:
Process ID : 5800
Command name : C:\Windows\System32\notepad.exe
Command line : Not Present
Start time: 2017-11-04T21:35:03.626
Arguments : Not Present
User: administrator
Java 9 改进的 Stream API
Java 9 改进的 Stream API 添加了一些便利的方法,使流处理更容易,
- 并使用收集器编写复杂的查询。
Java 9 为 Stream 新增了几个方法:
- dropWhile、
- takeWhile、
- ofNullable,
- 为 iterate 方法新增了一个重载方法。
takeWhile 方法
语法
default Stream<T> takeWhile(Predicate<? super T> predicate)
takeWhile() 方法使用一个 断言 作为参数,
- 返回给定 Stream 的子集直到断言语句第一次返回 false。
- 如果第一个值不满足断言条件,将返回一个空的 Stream。
takeWhile() 方法在有序的 Stream 中,
- takeWhile 返回从开头开始的尽量多的元素;
- 在无序的 Stream 中,takeWhile 返回从开头开始的符合 Predicate 要求的元素的子集。
实例
Predicate
英 /ˈprɛdɪˌkeɪt/ 美 /ˈpredɪkət; ˈpredɪkeɪt/ 全球(英国)
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
n. 谓语,述语
v. 使……基于;断言;暗示
adj. 谓语的,述语的
import java.util.stream.Stream;
public class Tester {
public static void main(String[] args) {
Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty())
.forEach(System.out::print);
}
}
以上实例 takeWhile 方法在碰到空字符串时停止循环输出,执行输出结果为:
abc
dropWhile 方法
语法
default Stream<T> dropWhile(Predicate<? super T> predicate)
dropWhile 方法和 takeWhile 作用相反的,使用一个断言作为参数,直到断言语句第一次返回 false 才返回给定 Stream 的子集。
实例
import java.util.stream.Stream;
public class Tester {
public static void main(String[] args) {
Stream.of("a","b","c","","e","f").dropWhile(s-> !s.isEmpty())
.forEach(System.out::print);
}
}
以上实例 dropWhile 方法在碰到空字符串时开始循环输出,执行输出结果为:
ef
iterate 方法
语法
static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
- 方法允许使用初始种子值创建顺序(可能是无限)流,
- 并迭代应用指定的下一个方法。 当指定的 hasNext 的 predicate 返回 false 时,迭代停止。
实例
java.util.stream.IntStream;
public class Tester {
public static void main(String[] args) {
IntStream.iterate(3, x -> x < 10, x -> x+ 3).forEach(System.out::println);
}
}
ofNullable 方法
语法
static <T> Stream<T> ofNullable(T t)
ofNullable 方法可以预防 NullPointerExceptions 异常, 可以通过检查流来避免 null 值。
- 如果指定元素为非 null,则获取一个元素并生成单个元素流,
- 元素为 null 则返回一个空流。
实例
import java.util.stream.Stream;
public class Tester {
public static void main(String[] args) {
long count = Stream.ofNullable(100).count();
System.out.println(count);
count = Stream.ofNullable(null).count();
System.out.println(count);
}
}
执行输出结果为:
1 有一个不为null的 stream
0 有0个
Java 9 改进的 try-with-resources
try-with-resources 是 JDK 7 中一个新的异常处理机制,
- 它能够很容易地关闭在 try-catch 语句块中使用的资源。
- 所谓的资源(resource)是指在程序完成后,必须关闭的对象。
- try-with-resources 语句确保了每个资源在语句结束时关闭。
- 所有实现了 java.lang.AutoCloseable 接口(其中,它包括实现了 java.io.Closeable 的所有对象),可以使用作为资源。
try-with-resources 声明在 JDK 9 已得到改进。
- 如果你已经有一个资源是 final 或等效于 final 变量,
- 您可以在 try-with-resources 语句中使用该变量,
- 而无需在 try-with-resources 语句中声明一个新变量。
实例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class Tester {
public static void main(String[] args) throws IOException {
System.out.println(readData("test"));
}
static String readData(String message) throws IOException {
Reader inputString = new StringReader(message);
BufferedReader br = new BufferedReader(inputString);
try (BufferedReader br1 = br) {
return br1.readLine();
}
}
}
输出结果为:
test
以上实例中我们需要在 try 语句块中声明资源 br1,然后才能使用它。
在 Java 9 中,我们不需要声明资源 br1 就可以使用它,并得到相同的结果。
实例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class Tester {
public static void main(String[] args) throws IOException {
System.out.println(readData("test"));
}
static String readData(String message) throws IOException {
Reader inputString = new StringReader(message);
BufferedReader br = new BufferedReader(inputString);
try (br) {
return br.readLine();
}
}
}
执行输出结果为:
test
在处理必须关闭的资源时,使用try-with-resources语句替代try-finally语句。 生成的代码更简洁,更清晰,并且生成的异常更有用。 try-with-resources语句在编写必须关闭资源的代码时会更容易,也不会出错,而使用try-finally语句实际上是不可能的。
Java 9 改进的 @Deprecated 注解
注解 @Deprecated 可以标记 Java API 状态,可以是以下几种:
- 使用它存在风险,可能导致错误
- 可能在未来版本中不兼容
- 可能在未来版本中删除
- 一个更好和更高效的方案已经取代它。
Java 9 中注解增加了两个新元素:since 和 forRemoval。
- since: 元素指定已注解的API元素已被弃用的版本。
- forRemoval: 元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API。
以下实例为 Java 9 中关于 Boolean 类的说明文档,文档中 @Deprecated 注解使用了 since 属性:Boolean Class。
以下实例为在 Java 9 中关于系统类的说明文档,文档中 @Deprecated 注解使用了 forRemoval 属性:System Class。
Java 9 钻石操作符(Diamond Operator)
钻石操作符是在 java 7 中引入的,可以让代码更易读,但它不能用于匿名的内部类。
在 java 9 中, 它可以与匿名的内部类一起使用,从而提高代码的可读性。
考虑以下 Java 9 之前的代码:
实例
//定义一个抽象类,泛型是T
abstract class Handler<T> {
//定义 T 类型的 content
public T content;
//对 content 赋值
public Handler(T content) {
this.content = content;
}
//定义抽象方法 handle
abstract void handle();
}
public class Tester {
public static void main(String[] args) {
//创建一个 Integer 类型的 handle ,并重写其方法。
Handler<Integer> intHandler = new Handler<Integer>(1) {
@Override
public void handle() {
System.out.println(content);
}
};
intHandler.handle();
Handler<? extends Number> intHandler1 = new Handler<Number>(2) {
@Override
public void handle() {
System.out.println(content);
}
};
intHandler1.handle();
Handler<?> handler = new Handler<Object>("test") {
@Override
public void handle() {
System.out.println(content);
}
};
handler.handle();
}
}
执行输出结果为:
1
2
Test
在 Java 9 中,我们可以在匿名类中使用 <在9之前 这里有内容> 操作符,如下所示:
实例
public class Tester {
public static void main(String[] args) {
Handler<Integer> intHandler = new Handler<>(1) { //<>直接省略中间的
@Override
public void handle() {
System.out.println(content);
}
};
intHandler.handle();
Handler<? extends Number> intHandler1 = new Handler<>(2) {
@Override
public void handle() {
System.out.println(content);
}
};
intHandler1.handle();
Handler<?> handler = new Handler<>("test") {
@Override
public void handle() {
System.out.println(content);
}
};
handler.handle();
}
}
abstract class Handler<T> {
public T content;
public Handler(T content) {
this.content = content;
}
abstract void handle();
}
执行输出结果为:
1
2
Test
Java 9 改进的 Optional 类
Optional 类在 Java 8 中引入,Optional 类的引入很好的解决空指针异常。。在 java 9 中, 添加了三个方法来改进它的功能:
- stream()
- if Present Or Else()
- or()
stream() 方法
语法
public Stream<T> stream()
- stream 方法的作用就是将 Optional 转为一个 Stream,
- 如果该 Optional 中包含值,那么就返回包含这个值的 Stream,
- 否则返回一个空的 Stream(Stream.empty())。
实例
public class TestMain {
public static void main(String[] args) {
List<Optional<String>> list = Arrays.asList(
Optional.empty(),
Optional.of("A"),
Optional.empty(),
Optional.of("B"));
//filter the list based to print non-empty values
//if optional is non-empty, get the value in stream, otherwise return empty
List<String> filteredList = list.stream()
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty() )
.collect(Collectors.toList());
//Optional::stream method will return a stream of either one
//or zero element if data is present or not.
List<String> filteredListJava9 = list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
System.out.println(filteredList);
System.out.println(filteredListJava9);
/*[A, B]
[A, B]*/
}
}
执行输出结果为:
[A, B]
[A, B]
ifPresentOrElse() 方法
语法
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
ifPresentOrElse 方法的改进就是有了 else,接受两个参数 Consumer 和 Runnable。
ifPresentOrElse 方法的用途是,
- 如果一个 Optional 包含值,
- 则对其包含的值调用函数 action,即 action.accept(value),
- 这与 ifPresent 一致;与 ifPresent 方法的区别在于,
- ifPresentOrElse 还有第二个参数 emptyAction
- —— 如果 Optional 不包含值,
- 那么 ifPresentOrElse 便会调用 emptyAction,即 emptyAction.run()。
实例
import java.util.Optional;
public class Tester {
public static void main(String[] args) {
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("Not Present."));
optional = Optional.empty();
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("Not Present."));
}
}
执行输出结果为:
Value: 1
Not Present.
or() 方法
语法
public Optional<T> or( Supplier< ? extends Optional<? extends T> > supplier)
如果值存在,返回 Optional 指定的值,否则返回一个预设的值。
实例
public class Tester {
public static void main(String[] args) {
//定义 Optional 赋值 Mahesh
Optional<String> optional1 = Optional.of("Mahesh");
//定义一个 没有赋值的 Supplier
Supplier<Optional<String>> supplierString = () -> Optional.of("Not Present");
//optional1 进行调用
optional1 = optional1.or( supplierString);
//进行打印。Value: Mahesh
optional1.ifPresent( x -> System.out.println("Value: " + x));
//重新赋值 为null
optional1 = Optional.empty();
//再次调用。 必须再次调用
optional1 = optional1.or( supplierString);
//执行。 结果为:Value: Not Present
optional1.ifPresent( x -> System.out.println("Value: " + x));
}
}
执行输出结果为:
Value: Mahesh
Value: Not Present
Java 9 多分辨率图像 API
Java 9 定义多分辨率图像 API,开发者可以很容易的操作和展示不同分辨率的图像了。
以下是多分辨率图像的主要操作方法:
- Image getResolutionVariant(double destImageWidth, double destImageHeight) − 获取特定分辨率的图像变体-表示一张已知分辨率单位为DPI的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。。
- List getResolutionVariants() − 返回可读的分辨率的图像变体列表。
实例
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.awt.Image;
import java.awt.image.MultiResolutionImage;
import java.awt.image.BaseMultiResolutionImage;
import javax.imageio.ImageIO;
public class Tester {
public static void main(String[] args) throws IOException, MalformedURLException {
List<String> imgUrls = List.of("http://www.runoob.com/wp-content/themes/runoob/assets/img/runoob-logo@2x.png",
"http://www.runoob.com/wp-content/themes/runoob/assets/img/runoob-logo.png",
"http://www.runoob.com/wp-content/themes/runoob/assets/images/qrcode.png");
List<Image> images = new ArrayList<Image>();
for (String url : imgUrls) {
images.add(ImageIO.read(new URL(url)));
}
// 读取所有图片
MultiResolutionImage multiResolutionImage =
new BaseMultiResolutionImage(images.toArray(new Image[0]));
// 获取图片的所有分辨率
List<Image> variants = multiResolutionImage.getResolutionVariants();
System.out.println("Total number of images: " + variants.size());
for (Image img : variants) {
System.out.println(img);
}
// 根据不同尺寸获取对应的图像分辨率
Image variant1 = multiResolutionImage.getResolutionVariant(156, 45);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]",
156, 45, variant1.getWidth(null), variant1.getHeight(null));
Image variant2 = multiResolutionImage.getResolutionVariant(311, 89);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]", 311, 89,
variant2.getWidth(null), variant2.getHeight(null));
Image variant3 = multiResolutionImage.getResolutionVariant(622, 178);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]", 622, 178,
variant3.getWidth(null), variant3.getHeight(null));
Image variant4 = multiResolutionImage.getResolutionVariant(300, 300);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]", 300, 300,
variant4.getWidth(null), variant4.getHeight(null));
}
}
Java 9 改进的 CompletableFuture API
Java 8 引入了 CompletableFuture 类,
- 可能是 java.util.concurrent.Future 明确的完成版(设置了它的值和状态),
- 也可能被用作java.util.concurrent.CompleteStage 。支持 future 完成时触发一些依赖的函数和动作。Java 9 引入了一些CompletableFuture 的改进:
Java 9 对 CompletableFuture 做了改进:
- 支持 delays 和 timeouts
- 提升了对子类化的支持
- 新的工厂方法
支持 delays 和 timeouts
public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)
在 timeout(单位在 java.util.concurrent.Timeunits units 中,比如 MILLISECONDS )前以给定的 value 完成这个 CompletableFutrue。返回这个 CompletableFutrue。
public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)
如果没有在给定的 timeout 内完成,就以 java.util.concurrent.TimeoutException 完成这个 CompletableFutrue,并返回这个 CompletableFutrue。
增强了对子类化的支持
做了许多改进使得 CompletableFuture 可以被更简单的继承。比如,你也许想重写新的 public Executor defaultExecutor() 方法来代替默认的 executor。
另一个新的使子类化更容易的方法是:
public <U> CompletableFuture<U> newIncompleteFuture()
新的工厂方法
Java 8引入了 CompletableFuture completedFuture(U value) 工厂方法来返回一个已经以给定 value 完成了的 CompletableFuture。Java 9以 一个新的 CompletableFuture failedFuture(Throwable ex) 来补充了这个方法,可以返回一个以给定异常完成的 CompletableFuture。
除此以外,Java 9 引入了下面这对 stage-oriented 工厂方法,返回完成的或异常完成的 completion stages:
- CompletionStage completedStage(U value): 返回一个新的以指定 value 完成的CompletionStage ,并且只支持 CompletionStage 里的接口。
- CompletionStage failedStage(Throwable ex): 返回一个新的以指定异常完成的CompletionStage ,并且只支持 CompletionStage 里的接口。