Java 9 - 快速指南


Java 9 - 概述

JAVA 9(又名 jdk 1.9)是 JAVA 编程语言开发的主要版本。它的初始版本于 2017 年 9 月 21 日发布。Java 9 发布的主要目标是 -

  • 使 JDK 和 Java 标准版平台模块化,以便可以很好地调用到小型计算设备。

  • 提高 JDK 和 Java 实现的整体安全性。

  • 使 Java SE 和 EE 平台的 Java 代码库和大型应用程序的构建过程和维护变得容易。

  • 设计并实现一个Java平台的标准模块系统,可以轻松地在平台和JDK上应用。

新功能

Java 8 添加了 90 多项增强功能,最重要的增强功能如下:

  • 模块- 作为模块引入的一种新型 Java 编程组件,它是一个命名的、自描述的代码和数据集合。

  • REPL (JShell) - 添加到 Java 平台的读取-求值-打印循环 (REPL) 功能。

  • HTTP 2 客户端- 新的 HTTPClient API 支持 websockets 和 HTTP 2 流以及服务器推送功能。

  • 改进的 JavaDocs - 支持 HTML5 输出生成。提供生成的 API 文档的搜索框。

  • Multirelease JAR - 增强 JAR 格式,以便多个特定于 Java 版本的类文件可以共存于单个存档中。

  • 集合工厂方法- List、Set 和 Map 接口的新静态工厂方法,用于创建这些集合的不可变实例。

  • 私有接口方法- 具有私有和私有静态方法的增强接口。

  • 进程 API 改进- 改进的 API 用于控制和管理操作系统进程。

  • 流 API 改进- 通过允许过滤传入的对象序列化数据流来增强安全性和稳健性。

  • Try With Resources 改进- 现在最终变量可以用作 try-with-resources 语句中的资源。

  • 增强的 @Deprecated 注释- 修改了 @Deprecated 注释,以提供有关 API 的状态和预期处置的更多信息。

  • 内部类菱形运算符- 如果可以表示推断类型的参数类型,则允许菱形运算符与匿名类一起使用。

  • 可选类改进- java.util.Optional 类中添加了新的有用方法。

  • 多分辨率图像 API - 支持将一组不同分辨率的图像封装到单个多分辨率图像中。

  • CompletableFuture API 改进- CompletableFuture 类的异步机制可以在进程使用 ProcessHandle.onExit 方法退出时执行操作。

  • 轻量级 JSON - 引入了一个轻量级 API,用于通过 java 9 中的 json 消费和生成文档和数据流。

  • Reactive Streams API - Java SE 9 中引入了新的 Reactive Streams API,以支持 java 9 中的反应式编程。

Java 9 - 环境设置

本地环境设置

如果您想设置自己的 Java 编程语言环境,那么本节将指导您完成整个过程。请按照以下步骤设置您的 Java 环境。

Java SE 可供免费下载。要下载,请单击此处,请下载与您的操作系统兼容的版本。

按照说明下载 Java,然后运行​​.exe以在您的计算机上安装 Java。在计算机上安装 Java 后,您需要设置环境变量以指向正确的安装目录。

设置 Windows 2000/XP 的路径

假设您已将 Java 安装在 c:\Program Files\java\jdk 目录中 -

  • 右键单击“我的电脑”并选择“属性”。

  • 单击“高级”选项卡下的“环境变量”按钮。

  • 现在,编辑“Path”变量并在其末尾添加 Java 可执行文件目录的路径。例如,如果路径当前设置为C:\Windows\System32,则按以下方式编辑它

C:\Windows\System32;c:\Program Files\java\jdk\bin

设置Windows 95/98/ME的路径

假设您已将 Java 安装在 c:\Program Files\java\jdk 目录中 -

  • 编辑“C:\autoexec.bat”文件并在末尾添加以下行 -

SET PATH = %PATH%;C:\Program Files\java\jdk\bin

设置 Linux、UNIX、Solaris、FreeBSD 的路径

应将环境变量 PATH 设置为指向 Java 二进制文件的安装位置。如果您在执行此操作时遇到问题,请参阅您的 shell 文档。

例如,如果您使用 bash 作为 shell,那么您可以在.bashrc末尾添加以下行-

export PATH = /path/to/java:$PATH'

流行的 Java 编辑器

要编写 Java 程序,您需要一个文本编辑器。市场上还有更复杂的 IDE。最流行的简要描述如下 -

  • 记事本- 在 Windows 计算机上,您可以使用任何简单的文本编辑器,例如记事本(本教程推荐)或写字板。Notepad++ 也是一个免费的文本编辑器,增强了功能。

  • Netbeans - 它是一个开源且免费的 Java IDE,可以从https://www.netbeans.org/index.html下载。

  • Eclipse - 它也是由 Eclipse 开源社区开发的 Java IDE,可以从https://www.eclipse.org/下载。

IDE或集成开发环境,提供所有常见的工具和设施来帮助编程,例如源代码编辑器、构建工具和调试器等。

Java 9 - 模块系统

Java 9 引入了一种称为模块的新型编程组件。模块是代码和数据的自描述集合,并有一个名称来标识它。

特征

通过 Modules 组件,Java 9 中添加了以下增强功能 -

  • 引入了一个新的可选阶段,即链接时间。该阶段位于编译时和运行时之间。在此阶段,可以组装和优化一组模块,使用 jlink 工具制作自定义运行时映像。

  • javac、jlink 和 java 有附加选项来指定模块路径,进一步定位模块的定义。

  • JAR 格式更新为模块化 JAR,其根目录中包含 module-info.class 文件。

  • 引入了JMOD格式,一种打包格式(类似于JAR),可以包含本机代码和配置文件。

创建模块

按照创建模块的步骤,例如 com.tutorialspoint.greetings。

步骤1

创建文件夹 C:\>JAVA\src。现在创建一个文件夹 com.tutorialspoint.greetings,它与我们正在创建的模块的名称相同。

第2步

使用以下代码在 C:\>JAVA\src\com.tutorialspoint.greetings 文件夹中创建 module-info.java 。

模块信息.java

module com.tutorialspoint.greetings { }

module-info.java 是用于创建模块的文件。在此步骤中,我们创建了一个名为 com.tutorialspoint.greetings 的模块。按照约定,该文件应驻留在名称与模块名称相同的文件夹中。

步骤3

在模块中添加源代码。使用以下代码在 C:\>JAVA\src\com.tutorialspoint.greetings\com\tutorialspoint\greetings 文件夹中创建 Java9Tester.java。

Java9测试器.java

package com.tutorialspoint.greetings;

public class Java9Tester {
   public static void main(String[] args) {
      System.out.println("Hello World!");
   }
}

按照惯例,模块的源代码位于与模块名称相同的目录中。

步骤4

创建文件夹 C:\>JAVA\mods。现在创建一个文件夹 com.tutorialspoint.greetings,它与我们创建的模块的名称相同。现在将模块编译到 mods 目录。

C:/ > JAVA > javac -d mods/com.tutorialspoint.greetings 
   src/com.tutorialspoint.greetings/module-info.java 
   src/com.tutorialspoint.greetings/com/tutorialspoint/greetings/Java9Tester.java

步骤5

让我们运行该模块来查看结果。运行以下命令。

C:/ > JAVA > java --module-path mods -m com.tutorialspoint.greetings/com.tutorialspoint.greetings.Java9Tester

这里 module-path 提供了 mods 的模块位置,-m 表示主模块。

输出

它将在控制台上打印以下输出。

Hello World!

Java 9 - REPL(JShell)

REPL 代表读取-评估-打印循环。有了JShell,java就具备了REPL能力。使用REPL,我们可以编写和测试基于java的逻辑,而无需使用javac进行编译,并直接查看计算结果。

运行 JShell

打开命令提示符并输入 jshell。

$ jshell
|  Welcome to JShell -- Version 9-ea
|  For an introduction type: /help intro
jshell>

查看 JShell 命令

jshell 命令开始运行后,键入 /help。

jshell> /help
|  Type a Java language expression, statement, or declaration.
|  Or type one of the following commands:
|  /list [<name or id>|-all|-start]
|  list the source you have typed
|  /edit <name or id>
|  edit a source entry referenced by name or id
|  /drop <name or id>
|  delete a source entry referenced by name or id
|  /save [-all|-history|-start] <file>
|  Save snippet source to a file.
|  /open <file>
|  open a file as source input
|  /vars [<name or id>|-all|-start]
|  list the declared variables and their values
|  /methods [<name or id>|-all|-start]
|  list the declared methods and their signatures
|  /types [<name or id>|-all|-start]
|  list the declared types
|  /imports 
|  list the imported items

运行 JShell 命令

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() 来获取 int 并返回其双倍值。

jshell> int doubled(int i){ return i*2;}
|  created method doubled(int)
jshell> doubled(6)
$3 ==> 12
jshell>

退出 JShell

输入/退出。

jshell> /exit
| Goodbye

Java 9 - 改进的 JavaDocs

Java 文档可以使用 javadoc 工具生成。它当前生成 html 4.0 格式的文档。在java 9中,我们可以通过在命令行参数中使用-html5选项来生成html 5格式的文档。

旧式java文档

考虑 C:/JAVA 文件夹中的以下代码。

测试器.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...
Constructing Javadoc information...
Standard Doclet version 1.7.0_21
Building tree for all the packages and classes...
Generating C:\JAVA\Tester.html...
Generating C:\JAVA\package-frame.html...
Generating C:\JAVA\package-summary.html...
Generating C:\JAVA\package-tree.html...
Generating C:\JAVA\constant-values.html...
Building index for all the packages and classes...
Generating C:\JAVA\overview-tree.html...
Generating C:\JAVA\index-all.html...
Generating C:\JAVA\deprecated-list.html...
Building index for all classes...
Generating C:\JAVA\allclasses-frame.html...
Generating C:\JAVA\allclasses-noframe.html...
Generating C:\JAVA\index.html...
Generating C:\JAVA\help-doc.html...

它将在 C:/JAVA 目录中创建 java 文档页面,您将看到以下输出。

javadoc输出

具有搜索和 HTML5 支持的新 Java 文档

使用 -html5 标志运行 jdk 9 的 javadoc 工具来生成新类型的文档。

C:\JAVA> javadoc -d C:/JAVA -html5 Tester.java
Loading source file Tester.java...
Constructing Javadoc information...
Standard Doclet version 9.0.1
Building tree for all the packages and classes...
Generating C:\JAVA\Tester.html...
Generating C:\JAVA\package-frame.html...
Generating C:\JAVA\package-summary.html...
Generating C:\JAVA\package-tree.html...
Generating C:\JAVA\constant-values.html...
Building index for all the packages and classes...
Generating C:\JAVA\overview-tree.html...
Generating C:\JAVA\index-all.html...
Generating C:\JAVA\deprecated-list.html...
Building index for all classes...
Generating C:\JAVA\allclasses-frame.html...
Generating C:\JAVA\allclasses-frame.html...
Generating C:\JAVA\allclasses-noframe.html...
Generating C:\JAVA\allclasses-noframe.html...
Generating C:\JAVA\index.html...
Generating C:\JAVA\help-doc.html...

它将在 D:/test 目录中创建更新的 java 文档页面,您将看到以下输出。

java 9 中的 javadoc 输出

Java 9 - 多版本 JAR

在 java 9 中,引入了一项新功能,其中增强了 jar 格式,可以根据平台维护和使用不同版本的 java 类或资源。在 JAR 中,文件 MANIFEST.MF 文件在其主要部分中有一个条目 Multi-Release: true。META-INF 目录还包含一个 versions 子目录,其子目录(对于 Java 9 以 9 开​​头)存储特定于版本的类和资源文件。

在此示例中,我们将使用多版本 jar 来拥有两个版本的 Tester.java 文件,一种用于 jdk 7,一种用于 jdk 9,并在不同的 jdk 版本上运行它。

脚步

步骤 1 - 创建文件夹 c:/test/java7/com/tutorialspoint。使用以下内容创建 Test.java -

测试器.java

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      System.out.println("Inside java 7");
   }
}

步骤 2 - 创建文件夹 c:/test/java9/com/tutorialspoint。使用以下内容创建 Test.java -

测试器.java

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      System.out.println("Inside java 9");
   }
}

编译源代码。

C:\test > javac --release 9 java9/com/tutorialspoint/Tester.java

C:\JAVA > javac --release 7 java7/com/tutorialspoint/Tester.java

创建多版本 jar

C:\JAVA > jar -c -f test.jar -C java7 . --release 9 -C java9.
Warning: entry META-INF/versions/9/com/tutorialspoint/Tester.java, 
   multiple resources with same name

使用 JDK 7 运行

C:\JAVA > java -cp test.jar com.tutorialspoint.Tester
Inside Java 7

使用 JDK 9 运行

C:\JAVA > java -cp test.jar com.tutorialspoint.Tester
Inside Java 9

Java 9 - 集合工厂方法

在 Java 9 中,List、Set 和 Map 接口中添加了新的工厂方法来创建不可变实例。这些工厂方法是方便的工厂方法,可以以不太冗长和简洁的方式创建集合。

创建集合的旧方法

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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);
   }
}

输出

它将打印以下输出。

[甲、乙、丙]
[甲、乙、丙]
{A=苹果,B=男孩,C=猫}

新方法

在 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 个参数,其中一个参数带有 var args 参数。

  • 对于 Map 接口,of(...) 方法被重载为具有 0 到 10 个参数。

  • 如果 Map 接口的参数超过 10 个,可以使用 ofEntries(...) 方法接受 var args 参数。

创建集合的新方法

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;

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);
   }
}

输出

它将打印以下输出。

[甲、乙、丙]
[甲、乙、丙]
{A=苹果,B=男孩,C=猫}
{A=苹果,B=男孩,C=猫}

Java 9 - 私有接口方法

在 java 8 之前,接口可以具有以下类型的变量/方法。

  • 常量变量
  • 抽象方法

因此,在 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 {
   @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");
   }
}

interface Logging {
   String ORACLE = "Oracle_Database";
   String MYSQL = "MySql_Database";

   void logInfo(String message);
   void logWarn(String message);
   void logError(String message);
   void logFatal(String message);

   void getConnection();
   void closeConnection();
}

输出

您将看到以下输出。

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 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 { 
}

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 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

Java 9 - 流程 API 改进

在 Java 9 中,负责控制和管理操作系统进程的 Process API 得到了显着改进。ProcessHandle 类现在提供进程的本机进程 ID、启动时间、累积 CPU 时间、参数、命令、用户、父进程和后代。ProcessHandle 类还提供了检查进程的活动性和销毁进程的方法。它有 onExit 方法,CompletableFuture 类可以在进程退出时异步执行操作。

测试器.java

import java.time.ZoneId;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.io.IOException;

public class Tester {
   public static void main(String[] args) throws IOException {
      ProcessBuilder pb = new ProcessBuilder("notepad.exe");
      String np = "Not Present";
      Process p = pb.start();
      ProcessHandle.Info info = p.info();
      System.out.printf("Process ID : %s%n", p.pid());
      System.out.printf("Command name : %s%n", info.command().orElse(np));
      System.out.printf("Command line : %s%n", info.commandLine().orElse(np));

      System.out.printf("Start time: %s%n",
         info.startInstant().map(i -> i.atZone(ZoneId.systemDefault())
         .toLocalDateTime().toString()).orElse(np));

      System.out.printf("Arguments : %s%n",
         info.arguments().map(a -> Stream.of(a).collect(
            Collectors.joining(" "))).orElse(np));

      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 - 流 API 改进

Java 中引入了流,以帮助开发人员从一系列对象执行聚合操作。在 Java 9 中,添加了一些方法来使流变得更好。

takeWhile(谓词接口)

句法

default Stream<T> takeWhile(Predicate<? super T> predicate)

takeWhile 方法获取所有值,直到谓词返回 false。在有序流的情况下,它返回一个流,该流由从此流中获取的与给定谓词匹配的元素的最长前缀组成。

例子

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 方法获取所有 a、b 和 c 值,然后一旦字符串为空,它就会停止执行。

abc

dropWhile(谓词接口)

句法

default Stream<T> dropWhile(Predicate<? super T> predicate)

dropWhile 方法丢弃开始时的所有值,直到谓词返回 true。在有序流的情况下,它在删除与给定谓词匹配的元素的最长前缀后返回由该流的剩余元素组成的流。

例子

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);
      System.out.println();
      Stream.of("a","b","c","","e","","f").dropWhile(s-> !s.isEmpty())
      .forEach(System.out::print);
   } 
}

输出

dropWhile 方法删除 a、b 和 c 值,然后一旦字符串为空,它就会获取所有值。

ef
ef

迭代

句法

static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

iterate 方法现在将 hasNext 谓词作为参数,一旦 hasNext 谓词返回 false,该方法就会停止循环。

例子

import 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);
   } 
}

输出

3
6
9

可空的

句法

static <T> Stream<T> ofNullable(T t)

引入 ofNullable 方法是为了防止 NullPointerException 并避免对流进行 null 检查。此方法返回包含单个元素的顺序 Stream,如果非 null,则返回空 Stream。

例子

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
0

Java 9 - 尝试资源改进

try-with-resources 语句是一个带有正式声明的一个或多个资源的 try 语句。这里的资源是一个对象,一旦不再需要就应该关闭。try-with-resources 语句确保每个资源在需求完成后关闭。任何实现 java.lang.AutoCloseable 或 java.io.Closeable 接口的对象都可以用作资源。

在 Java 9 之前,资源应在 try 之前或 try 语句内声明,如下所示的给定示例。在此示例中,我们将使用 BufferedReader 作为资源来读取字符串,然后关闭 BufferedReader。

测试器.java

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,然后使用它。在Java9中,我们不再需要声明br1,下面的程序将给出相同的结果。

测试器.java

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

Java 9 - 增强的@Deprecated 注解

@Deprecated 注解是在 java 5 版本中引入的。用 @Deprecated 注释的程序元素意味着不应出于以下任何原因使用它 -

  • 它的使用可能会导致错误。
  • 未来版本可能不兼容。
  • 它可能会在未来版本中被删除。
  • 更好、更高效的替代方案已经取代了它。

每当使用不推荐使用的元素时,编译器都会生成警告。在 Java 9 中,@Deprecated 注释进行了两项新的增强。

  • forRemoval - 指示带注释的元素是否会在未来版本中被删除。默认值为 false。

  • 因为- 返回带注释的元素被弃用的版本。默认值为空字符串。

自此后已弃用

以下 Java 9 上的 Boolean 类 javadoc 示例说明了 @Deprecated 注释上的since属性的使用。

布尔类

布尔类 javadoc

已弃用 forRemoval

以下 Java 9 上的 System 类 javadoc 示例说明了 @Deprecated 注释上的 forRemoval 属性的使用。

系统类

系统类javadoc

Java 9 - 内部类钻石运算符

java 7 中引入了 Diamond 运算符,以使代码更具可读性,但它不能与匿名内部类一起使用。在java 9中,它也可以与匿名类一起使用,以简化代码并提高可读性。请考虑 Java 9 之前的以下代码。

测试器.java

public class Tester {
   public static void main(String[] args) {
         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();    
   }  
}

abstract class Handler<T> {
   public T content;

   public Handler(T content) {
      this.content = content; 
   }
   
   abstract void handle();
}

输出

1
2
Test

在 Java 9 中,我们可以将 <> 运算符与匿名类一起使用,如下所示。

测试器.java

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 - 可选类改进

Java 8 中引入了可选类,以避免 null 检查和 NullPointerException 问题。在java 9中,添加了三个新方法来改进其功能。

  • 溪流()
  • ifPresentOrElse()
  • 或者()

流()方法

句法

public Stream<T> stream()

如果存在值,则返回仅包含该值的顺序流,否则返回空流。

例子

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Tester {
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]

ifPresentOrElse() 方法

句法

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

如果存在值,则使用该值执行给定的操作,否则执行给定的基于空的操作。

例子

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.

或()方法

句法

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)

如果存在值,则返回描述该值的可选值,否则返回由提供函数生成的可选值。

例子

import java.util.Optional;
import java.util.function.Supplier;

public class Tester {
   public static void main(String[] args) {
      Optional<String> optional1 = Optional.of("Mahesh");

      Supplier<Optional<String>> supplierString = () -> Optional.of("Not Present");

      optional1 = optional1.or( supplierString);
  
      optional1.ifPresent( x -> System.out.println("Value: " + x));
    
      optional1 = Optional.empty();    

      optional1 = optional1.or( supplierString);
  
      optional1.ifPresent( x -> System.out.println("Value: " + x));  
   }  
}

输出

Value: Mahesh
Value: Not Present

Java 9 - 多分辨率图像 API

Java 9 引入了新的多分辨率图像 API,它支持具有不同分辨率变体的多个图像。该 API 允许将一组不同分辨率的图像用作单个多分辨率图像。以下是多分辨率图像的主要操作。

  • Image getResolutionVariant(double destImageWidth, double destImageHeight) - 获取特定图像,该图像是以指定大小表示此逻辑图像的最佳变体。

  • List<Image> 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.tutorialspoint.com/java9/images/logo.png",
         "http://www.tutorialspoint.com/java9/images/mini_logo.png",
         "http://www.tutorialspoint.com/java9/images/large_logo.png");

      List<Image> images = new ArrayList<Image>();

      for (String url : imgUrls) {
         images.add(ImageIO.read(new URL(url)));
      }

      // read all images into one multiresolution image
      MultiResolutionImage multiResolutionImage = 
         new BaseMultiResolutionImage(images.toArray(new Image[0]));

      // get all variants of images
      List<Image> variants = multiResolutionImage.getResolutionVariants();

      System.out.println("Total number of images: " + variants.size());

      for (Image img : variants) {
         System.out.println(img);
      }

      // get a resolution-specific image variant for each indicated size
      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));
   }  
}

输出

Total number of images: 3
BufferedImage@7ce6a65d: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 
color space =java.awt.color.ICC_ColorSpace@548ad73b transparency = 3 
has alpha = true isAlphaPre = false ByteInterleavedRaster: width =311 
height = 89 #numDataElements 4 dataOff[0] = 3

BufferedImage@4c762604: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 
color space =java.awt.color.ICC_ColorSpace@548ad73b transparency = 3 
has alpha = true isAlphaPre = false ByteInterleavedRaster: width =156 
height = 45 #numDataElements 4 dataOff[0] = 3

BufferedImage@2641e737: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 
color space =java.awt.color.ICC_ColorSpace@548ad73b transparency = 3 
has alpha = true isAlphaPre = false ByteInterleavedRaster: width =622 
height = 178 #numDataElements 4 dataOff[0] = 3

Image for destination[156,45]: [311,89]
Image for destination[311,89]: [311,89]
Image for destination[622,178]: [622,178]
Image for destination[300,300]: [622,178]

CompletableFuture API 改进

Java 8 中引入了 CompletableFuture 类来表示可以通过显式设置其值和状态来完成的 Future。它可以用作java.util.concurrent.CompletionStage。它支持在 future 完成时触发的依赖函数和操作。在 java 9 CompletableFuture API 中得到了进一步增强。以下是对 API 所做的相关更改。

  • 支持延迟和超时。
  • 改进了对子类化的支持。
  • 添加了新的工厂方法。

支持延迟和超时

public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)

如果在给定超时之前未以其他方式完成,则此方法将使用给定值完成此 CompletableFuture。

public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)

如果在给定超时之前未以其他方式完成,则此方法会异常地完成此 CompletableFuture 并引发 TimeoutException。

改进了对子类化的支持

public Executor defaultExecutor()

它返回用于未指定 Executor 的异步方法的默认 Executor。可以在子类中重写此方法以返回 Executor,以至少提供一个独立线程。

public <U> CompletableFuture<U> newIncompleteFuture()

返回由 CompletionStage 方法返回的类型的新的不完整 CompletableFuture。CompletableFuture 类的子类应该重写此方法以返回与此 CompletableFuture 相同的类的实例。默认实现返回 CompletableFuture 类的实例。

新工厂方法

public static <U> CompletableFuture<U> completedFuture(U value)

此工厂方法返回一个新的 CompletableFuture,它已经使用给定值完成。

public static <U> CompletionStage<U> completedStage(U value)

此工厂方法返回一个新的 CompletionStage,它已使用给定值完成,并且仅支持接口 CompletionStage 中存在的那些方法。

public static <U> CompletionStage<U> failedStage(Throwable ex)

此工厂方法返回一个新的 CompletionStage,它已经在给定的异常下异常完成,并且仅支持接口 CompletionStage 中存在的那些方法。

Java 9 - 其他功能

除了提到的功能之外,Java 9 还对 JDK 平台进行了更多增强。下面列出了其中一些。

  • GC(垃圾收集器)改进
  • 堆栈遍历 API
  • 过滤传入的序列化数据
  • 弃用 Applet API
  • 指示字符串连接
  • 增强的方法句柄
  • Java 平台日志记录 API 和服务
  • 紧凑弦乐
  • Nashorn 的解析器 API