1. 程式人生 > >3.Java高階教程_12.Java 9 新特性

3.Java高階教程_12.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 中的響應式程式設計。

在關於 Java 9 文章的例項,我們均使用 jdk 1.9 環境,你可以使用以下命令檢視當前 jdk 的版本:

$ java -version
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+163)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+163, mixed mode)

接下來我們將詳細為大家簡介 Java 9 的新特性:

1. 模組系統

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 指定主要模組。

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相關的命令:

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

/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...
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 9 生成的文件相容 HTML5 標準

使用 jdk 9 javadoc 命令中的 -html5 引數可以讓生成的文件支援 HTML5 標準:

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

執行以上命令會再 C:/JAVA 命令下生成文件頁面,如下圖所示:

Java 9 多版本相容 jar 包

多版本相容 JAR 功能能讓你建立僅在特定版本的 Java 環境中執行庫程式時選擇使用的 class 版本。

通過 --release 引數指定編譯版本。

具體的變化就是 META-INF 目錄下 MANIFEST.MF 檔案新增了一個屬性:

Multi-Release: true

然後 META-INF 目錄下還新增了一個 versions 目錄,如果是要支援 java9,則在 versions 目錄下有 9 的目錄。

multirelease.jar
├── META-INF
│   └── versions
│       └── 9
│           └── multirelease
│               └── Helper.class
├── multirelease
    ├── Helper.class
    └── Main.class

在以下例項中,我們使用多版本相容 JAR 功能將 Tester.java 檔案生成了兩個版本的 jar 包, 一個是 jdk 7,另一個是 jdk 9,然後我們再不同環境下執行。

第一步

建立資料夾 c:/test/java7/com/runoob,並在該資料夾下建立 Test.java 檔案,程式碼如下:

package com.runoob;

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

第二步

建立資料夾 c:/test/java9/com/runoob,並在該資料夾下建立 Test.java 檔案,程式碼如下:

package com.runoob;

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

編譯原始碼:

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

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

建立多版本相容 jar 包

C:\JAVA > jar -c -f test.jar -C java7 . --release 9 -C java9.
Warning: entry META-INF/versions/9/com/runoob/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]
[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(...) 方法。

新方法建立集合

例項

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]
{A=Apple, B=Boy, C=Cat}
{A=Apple, B=Boy, C=Cat}

Java 9 私有介面方法

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

7. 程序 API

Java 9 改進的程序 API

在 Java 9 之前,Process API 仍然缺乏對使用本地程序的基本支援,例如獲取程序的 PID 和所有者,程序的開始時間,程序使用了多少 CPU 時間,多少本地程序正在執行等。

Java 9 向 Process API 添加了一個名為 ProcessHandle 的介面來增強 java.lang.Process 類。

ProcessHandle 介面的例項標識一個本地程序,它允許查詢程序狀態並管理程序。

ProcessHandle 巢狀介面 Info 來讓開發者逃離時常因為要獲取一個本地程序的 PID 而不得不使用原生代碼的窘境。

我們不能在介面中提供方法實現。如果我們要提供抽象方法和非抽象方法(方法與實現)的組合,那麼我們就得使用抽象類。

ProcessHandle 介面中宣告的 onExit() 方法可用於在某個程序終止時觸發某些操作。

例項

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 改進的 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 要求的元素的子集。

例項

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 作用相反的,使用一個斷言作為引數,直到斷言語句第一次返回 true 才返回給定 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);
   } 
}

執行輸出結果為:

3
6
9

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
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 之前的程式碼:

例項

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 中,我們可以在匿名類中使用 <> 操作符,如下所示:

例項

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()
  • ifPresentOrElse()
  • or()

stream() 方法

語法

public Stream<T> stream()

stream 方法的作用就是將 Optional 轉為一個 Stream,如果該 Optional 中包含值,那麼就返回包含這個值的 Stream,否則返回一個空的 Stream(Stream.empty())。

例項

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)

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 指定的值,否則返回一個預設的值。

例項

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,開發者可以很容易的操作和展示不同解析度的影象了。

以下是多解析度影象的主要操作方法:

  • Image getResolutionVariant(double destImageWidth, double destImageHeight) − 獲取特定解析度的影象變體-表示一張已知解析度單位為DPI的特定尺寸大小的邏輯影象,並且這張影象是最佳的變體。。

  • 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.runoob.com/wp-content/themes/runoob/assets/img/[email protected]",
         "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<T> 類,可能是 java.util.concurrent.Future<T> 明確的完成版(設定了它的值和狀態),也可能被用作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引入了 <U> CompletableFuture<U> completedFuture(U value) 工廠方法來返回一個已經以給定 value 完成了的 CompletableFuture。Java 9以 一個新的 <U> CompletableFuture<U> failedFuture(Throwable ex) 來補充了這個方法,可以返回一個以給定異常完成的 CompletableFuture。

除此以外,Java 9 引入了下面這對 stage-oriented 工廠方法,返回完成的或異常完成的 completion stages:

  • <U> CompletionStage<U> completedStage(U value): 返回一個新的以指定 value 完成的CompletionStage ,並且只支援 CompletionStage 裡的介面。
  • <U> CompletionStage<U> failedStage(Throwable ex): 返回一個新的以指定異常完成的CompletionStage ,並且只支援 CompletionStage 裡的介面。

相關推薦

3.Java高階教程_12.Java 9 特性

Java 9 釋出於 2017 年 9 月 22 日,帶來了很多新特性,其中最主要的變化是已經實現的模組化系統。接下來我們會詳細介紹 Java 9 的新特性。 Java 9 新特性 模組系統:模組是一個包的容器,Java 9 最大的變化之一是引入了模組系統(Jigsa

千鋒Java高階教程+分散式+springcloud+微信支付課程

課程目錄: ├─千鋒Java高階教程-cas單點登入(完結-8集) │ 01單點登入介紹 │ 02cas介紹 │ 03tomcat搭建https │ 04cas server搭建 │ 05Cas 配置 jdbc 連線資料庫 │ 06Cas 密碼 MD5值 │ 07 Cas 整合shiro1

Java 9 特性快速預覽

Java 8 已經出來三年多的時間了,原本計劃2016年七月份release Java 9,但是基於種種原因,Java 9 被推遲到了2017年的3月份,本人也在Open JDK的官網上看到了Java 10的標準也在制定當中,Java的發展真的越來越快了,在Java 9正式

千鋒《Java高階教程+分散式+springcloud+微信支付》

課程目錄:├─千鋒Java高階教程-cas單點登入(完結-8集)│      01單點登入介紹│      02cas介紹│      03tomcat搭建https│  &nbs

Java 9 特性! 看過才知道強大

1、Java9 新特性之---目錄結構包含jdk8及以前的jdk版本,所有目錄結構以及目錄含義如圖:     jdk9之後,目錄結構發生變化如圖:這個新特性只要瞭解下就可以了,這個目錄結構是方便為了接下來新特性做保證2、Java9新特性之---JShell工具怎麼理解,怎麼用

重溫Java經典教程(The Java™ Tutorials)第三篇-Java語言-第二章-2.3介面

Defining an Interface An interface declaration consists of modifiers, the keyword interface, the interface name, a comma-separated list of pa

java-1.5-1.9特性

———————————————————————————————————————————— 加快OpenJDK的開發速度:繼2014年3月份釋出了Java 8之後,我們進入下一個兩年的釋出週期。 Java 9預計在2016年釋出,並且已經公佈了JEP(JDK改進提議)中的前期列表。同時,我們已經把一些新特性整理

Java 高階教程

//轉載自菜鳥教程Java 資料結構Java工具包提供了強大的資料結構。在Java中的資料結構主要包括以下幾種介面和類:列舉(Enumeration)位集合(BitSet)向量(Vector)棧(Stack)字典(Dictionary)雜湊表(Hashtable)屬性(Pro

Java基礎教程(2)--Java開發環境

特殊 .html 完成 能夠 println 個數 過程 intel 免費試用 一.JVM、JRE和JDK的概念 ??對於初學者來說,這三個術語出現的頻率很高,而且有關這它們的問題在面試題中也會經常出現。因此,理解它們的定義、區別和聯系就顯得尤為重要。在學習這幾個專業術語之

Java單元測試工具JUnit 5特性一覽

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Java基礎入門之jdk1.8特性

Lamda 表示式(目標型別) 簡介 語法糖,也叫糖衣語法 指的是計算機中 新增某種語法 這種語法 ,能使程式設計師更加方便的使用語言開發程式,同時,增強了程式碼的可讀性 避免了出錯的機會,但是,這種語法對於語言的功能並且有增強 例如: 泛型 自動裝箱拆箱 增強

JAVA秒會技術之Java8特性】利用流快速處理集合的常見操作

例子1:對集合進行排序 List<Integer> list = Lists.newArrayList(1,1,2,2,5,3,4,6,6,5,2,7); list.sort(null); list.forEach(e -> System.out.prin

Java 5~11各個版本特性史上最全總結

Java 5 Java5開發代號為Tiger(老虎),於2004-09-30發行 特性列表 泛型 列舉 自動裝箱拆箱 可變引數 註解 foreach迴圈(增強for、for/in) 靜態匯入

Java之JDK1.8推出的特性

介面定義增強 JDK1.8之後接口出現以下兩類方法: a.在介面中使用default定義的普通方法,需要通過介面物件來呼叫 b.在介面中使用static定義的靜態方法,直接使用介面呼叫。 範例: int

Java NIO教程Java NIO Path

原文連結     作者:Jakob Jenkov     譯者:黃國鵬 Java的Path介面是Java NIO2 的一部分,是對Java6 和Java7的 NIO的更新。Java的Path介面在Java7 中被新增到Java NIO,位於java.nio.file包中, 其全路徑是java.

JDK/JAVA 1.5到1.9版本特性對比

update:2017/9/25 JDK發展歷史:     96年SUN JDK 1.0 class VMWare、Visual     97年JDK 1.1:具有AWT、內部類、JDBC、RMI、反射     98年JDK 1.2:有JIT解析器

Java高階篇-1-Java基礎知識快速過一遍

      在學習Java高階部分知識之前,我們來簡單梳理一下Java基礎語法部分的知識,不會太全,但是基本的,常用到的一一複習一下。 1.Java編碼規範        在這裡,還是要強調Java編碼標準或者規範。以前,我寫程式碼或者指令碼不多,也不知道這個重要性。突然

重溫Java經典教程(The Java™ Tutorials)第三篇-Java語言-第二章-2.6.2泛型

Wildcards(萬用字元) In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variet

重溫Java經典教程(The Java™ Tutorials)第三篇-Java語言-第二章-2.6.1泛型

Generic Types A generic type is a generic class or interface that is parameterized over types. The following Box class will be

重溫Java經典教程(The Java™ Tutorials)第三篇-Java語言-第二章-2.5數字與字串

Converting Between Numbers and Strings Converting Strings to Numbers Frequently, a program ends up with numeric data in a string object—a value