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