Java 教程

Java 控制語句

面向物件程式設計

Java 內建類

Java 檔案處理

Java 錯誤和異常

Java 多執行緒

Java 同步

Java 網路

Java 集合

Java 介面

Java 資料結構

Java 集合演算法

高階 Java

Java 雜項

Java API 和框架

Java 類參考

Java 有用資源

Java 9 - 新特性



JAVA 9(也稱為 jdk 1.9)是JAVA 程式語言開發的一個主要版本。其初始版本於 2017 年 9 月 21 日釋出。Java 9 版本的主要目標是:

  • 使 JDK 和 Java 標準版平臺基於模組化,這意味著它可以縮減到小型計算裝置。

  • 提高 JDK 和 Java 實現的整體安全性。

  • 使 Java 程式碼庫和大型應用程式的構建過程和維護對於 JAVA SE 和 EE 平臺來說更容易。

  • 為 Java 平臺設計和實現一個標準的模組系統,該系統可以輕鬆地應用於平臺和 JDK。

以下是 Java 9 支援的新特性列表

模組系統

模組系統旨在將 Java 程式碼中的模組化提升到一個新的水平。模組是程式碼和資料的自描述集合。模組可以包含包,以及特定於特定功能的配置。模組提供了對自身內容的更好的訪問控制。從 Java 9 開始,Java 庫被分成多個模組,可以使用以下命令檢視。

C:\Users\Mahesh>java --list-modules
java.base@20.0.2
java.compiler@20.0.2
java.datatransfer@20.0.2
java.desktop@20.0.2
...
jdk.xml.dom@20.0.2
jdk.zipfs@20.0.2

示例 - 使用模組

以下程式碼片段定義了一個在應用程式根資料夾中的 module-info.java 檔案中宣告的模組。

module com.tutorialspoint.greetings { 
   requires com.tutorialspoint.util;
   requires static com.tutorialspoint.logging;
   requires transitive com.tutorialspoint.base;
   
   exports com.tutorialspoint.greetings.HelloWorld;
   opens com.tutorialspoint.greetings.HelloWorld;
}

在這裡,我們聲明瞭我們的模組依賴於三個模組,並匯出了一個供外部世界使用的公共類,並允許反射檢查特定類。預設情況下,模組的私有成員無法透過反射訪問。

REPL

REPL 代表 讀取-求值-列印-迴圈。Java 9 中引入了一個 REPL 引擎JShell,它是一個互動式控制檯,用於在控制檯中執行任意 Java 程式碼片段,無需儲存和編譯 Java 程式碼檔案。JShell 讀取輸入的每一行,對其求值,然後列印結果,然後再次準備接收下一組輸入。

示例 - 使用 JShell 作為 REPL

以下程式碼片段展示瞭如何在 JShell 中建立變數。分號是可選的。我們也可以在 JShell 中建立物件。如果變數未初始化,則會賦予預設值,如果是物件引用則為 null。建立變數後,就可以使用它,如最後一個語句所示,我們使用了字串變數來列印其值。

示例

在以下示例中,我們建立了變數,求值表示式,建立了日期物件。

jshell> int i = 10
i ==> 10

jshell> String name = "Mahesh";
name ==> "Mahesh"

jshell> Date date = new Date()
date ==> Fri Feb 02 14:52:49 IST 2024

jshell> String.format("%d pages read.", 10);
$9 ==> "10 pages read."

jshell> $9
$9 ==> "10 pages read."
jshell> name
name ==> "Mahesh"

改進的 JavaDocs

從 Java 9 開始,Java 現在支援 HTML5 輸出生成,並在生成的 API 文件中提供搜尋框。

示例

在這個例子中,我們正在建立一個相容 HTML5 的 javadoc。

考慮 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 9 的 javadoc 工具,並使用 -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...

它將在 D:/test 目錄中建立更新的 Java 文件頁面,您將看到以下輸出。

javadoc output in java 9

多版本 JAR

Java 9 中的多版本 JAR 功能增強了 JAR 格式,以便多個特定於 Java 版本的類檔案可以共存於單個歸檔檔案中。

在多版本 Jar 格式中,一個 jar 檔案可以包含不同版本的 Java 類或資源,可以根據平臺進行維護和使用。在 JAR 中,檔案 MANIFEST.MF 檔案在其主部分中有一個條目 Multi-Release: true。META-INF 目錄還包含一個 versions 子目錄,其子目錄(從 Java 9 開始以 9 開頭)儲存特定於版本的類和資原始檔。

使用 MANIFEST.MF,我們可以指定 Java 9 或更高版本特定類的單獨位置,如下所示:

Java 多版本 Jar 檔案目錄結構示例

jar root
   - Calculator.class
   - Util.class
   - Math.class
   - Service.class
   META-INF
      - versions
      - 9
         - Util.class
         - Math.class
      - 10
         - Util.class
         - Math.class

現在,如果 JRE 不支援多版本 jar,則它將選擇根級別的類來載入和執行,否則,將載入特定於版本的類。例如,如果上述 jar 用於 Java 8,則將使用根級別的 Util.class。如果相同的 jar 由 Java 9 執行,則將選擇 Java 9 版本的特定類,依此類推。這樣,第三方庫/框架可以在不更改其針對較低版本編寫的原始碼的情況下支援新功能。

集合工廠方法改進

在 Java 9 中,為 List、Set 和 Map 介面添加了新的靜態工廠方法,以建立這些集合的不變例項。這些工廠方法主要是為了以更簡潔的方式建立集合的便利工廠方法。

Java 9 之前 List 介面工廠方法的示例

在這裡,我們正在建立 Java 9 之前不可修改的列表。

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Tester {
   public static void main(String[] args) {
      List<String> list = new ArrayList<>();

      list.add("Java");
      list.add("HTML 5");
      list.add("C");
      list = Collections.unmodifiableList(list);
      System.out.println(list);
   }  
}

讓我們編譯並執行上述程式,這將產生以下結果:

[Java, HTML 5, C]

Java 9 中 List 介面工廠方法的示例

在這裡,我們正在建立 Java 9 中不可修改的列表。

package com.tutorialspoint;

import java.util.List;

public class Tester {
   public static void main(String[] args){
	   List<String> list =  List.of("Java","HTML 5","C");
	   System.out.println(list);
   }  
}

讓我們編譯並執行上述程式,這將產生以下結果:

[Java, HTML 5, C]

私有介面方法

Java 9 引入了私有和靜態私有介面方法。作為私有方法,此類方法無法透過實現類或子介面訪問。引入這些方法是為了允許封裝,其中某些方法的實現將僅保留在介面中。這有助於減少重複性,提高可維護性和編寫整潔的程式碼。

示例 - Java 9 中介面中的私有方法

package com.tutorialspoint;

interface util {
   public default int operate(int a, int b) {
      return sum(a, b);
   }
   private int sum(int a, int b) {
      return a + b;
   } 
}

public class Tester implements util {
   public static void main(String[] args) {
      Tester tester = new Tester();
      System.out.println(tester.operate(2, 3));
   }
}

輸出

讓我們編譯並執行上述程式,這將產生以下結果:

5

類似地,我們可以擁有私有靜態方法,該方法可以從靜態和非靜態方法中呼叫。

程序 API 改進

在 Java 9 中,負責控制和管理作業系統程序的 Process API 已經得到了相當大的改進。ProcessHandle 類現在提供了程序的原生程序 ID、啟動時間、累積 CPU 時間、引數、命令、使用者、父程序和子程序。ProcessHandle 類還提供方法來檢查程序的活動狀態並銷燬程序。它具有 onExit 方法,CompletableFuture 類可以在程序退出時非同步執行操作。

生成新程序示例

在此示例中,我們為記事本建立了一個新程序,並使用 ProcessBuilder 啟動它。使用 ProcessHandle.Info 介面,我們獲取新生成的程序的程序資訊。

package com.tutorialspoint;

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 : 5580
Command name : C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2401.26.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe
Command line : Not Present
Start time: 2024-04-02T17:07:14.305
Arguments : Not Present
User : DESKTOP\Tutorialspoint

Stream API 改進

Stream 在 Java 8 中引入,以幫助開發人員從物件序列執行聚合操作。在 Java 9 中,添加了一些方法來改進 Stream。

takeWhile(Predicate 介面) 方法

語法

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

takeWhile 方法獲取所有值,直到謂詞返回 false。在有序流的情況下,它返回一個流,該流包含從該流中獲取的與給定謂詞匹配的最長字首元素。

dropWhile(Predicate 介面)

語法

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

dropWhile 方法丟棄開頭所有值,直到謂詞返回 true。在有序流的情況下,它返回一個流,該流包含在丟棄與給定謂詞匹配的最長字首元素後該流的剩餘元素。

iterate 方法

語法

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

iterate 方法現在具有 hasNext 謂詞作為引數,一旦 hasNext 謂詞返回 false,迴圈就會停止。

ofNullable

語法

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

引入 ofNullable 方法是為了防止 NullPointerExceptions 並避免 Stream 的空檢查。此方法返回一個包含單個元素的順序 Stream(如果非空),否則返回一個空 Stream。

帶資源的 try 改進

在 Java 9 之前,資源需要在 try 之前或 try 語句內宣告,如下面的示例所示。在此示例中,我們將使用 BufferedReader 作為資源來讀取字串,然後關閉 BufferedReader。

Java 9 及更高版本

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

增強的 @Deprecated 註解

@Deprecated 註解是在 Java 5 版本中引入的。使用 @Deprecated 註解的程式元素意味著出於以下任何原因都不應使用它:

  • 其使用可能會導致錯誤。
  • 它可能在未來版本中不相容。
  • 它可能在未來版本中被刪除。
  • 更好的、更高效的替代方案已取代它。

每當使用已棄用的元素時,編譯器都會生成警告。在 Java 9 中,對 @Deprecated 註解進行了兩項新的增強。

  • forRemoval - 指示被註解的元素是否將在未來版本中被移除。預設值為 false。

  • since - 返回被註解的元素何時被棄用的版本。預設值為空字串。

使用 since 棄用

以下 Java 9 上 Boolean 類的 javadoc 示例說明了在 @Deprecated 註解上使用 since 屬性。

Boolean 類

Boolean Class javadoc

使用 forRemoval 棄用

以下 Java 9 上 System 類的 javadoc 示例說明了在 @Deprecated 註解上使用 forRemoval 屬性。

System 類

System Class javadoc

內部類菱形運算子

在 Java 9 中,菱形運算子也可以與匿名類一起使用,以簡化程式碼並提高可讀性。

示例

在下面的示例中,我們為抽象類 Handler 建立了匿名類,該類接受泛型引數,但在建立匿名類時沒有物件型別,因為我們不需要傳遞型別引數。編譯器本身會推斷型別。

public class Tester {
   public static void main(String[] args) {
      // create an Anonymous class to handle 1
	  // Here we do not need to pass Type arguments in diamond operator 
	  // as Java 9 compiler can infer the type automatically
      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

多解析度影像 API

多解析度影像 API 是在 Java 9 中引入的。此 API 支援具有不同解析度變體的多個影像。此 API 允許將一組具有不同解析度的影像用作單個多解析度影像。

考慮以下影像。

mini logo.png logo.png large logo.png

這些是三個不同尺寸的徽標影像。

現在,為了使用這三個影像,從 Java 9 開始,可以使用多解析度影像 API 作為單個 API 來獲取所有變體或要顯示的特定變體。

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

這裡 MultiResolutionImage 和 BaseMultiResolutionImage 類是 java.awt.image 包的一部分。

以下是多解析度影像的主要操作。

  • Image getResolutionVariant(double destImageWidth, double destImageHeight) - 獲取最適合在指定尺寸下表示此邏輯影像的特定影像。

  • List<Image> getResolutionVariants() - 獲取所有解析度變體的可讀列表。

示例 - 獲取所有變體

在此示例中,我們載入了三個影像並將它們儲存在 MultiResolutionImage 中。然後使用 getResolutionVariants() 方法,我們檢查此多解析度影像中所有可用的影像變體並列印它。

package com.tutorialspoint;

import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.MultiResolutionImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

public class Tester {
   public static void main(String[] args) throws IOException, MalformedURLException {

	  // prepare a list of urls of all images
      List<String> imgUrls = List.of("https://tutorialspoint.tw/java9/images/logo.png",
         "https://tutorialspoint.tw/java9/images/mini_logo.png",
         "https://tutorialspoint.tw/java9/images/large_logo.png");

      // create a list of Image object
      List<Image> images = new ArrayList<Image>();

      // Create image objects using image urls
      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());
     
      // print all the images
      for (Image img : variants) {
         System.out.println(img);
      }     
   }  
}

輸出

讓我們編譯並執行上述程式,這將產生以下結果:

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

CompletableFuture API 增強

CompletableFuture 類是在 Java 8 中引入的,用於表示 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)

如果在給定超時之前未完成,則此方法使用 TimeoutException 異常完成此 CompletableFuture。

改進對子類的支援

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 還對 JDK 平臺進行了大量增強。其中一些列在下面。

  • GC(垃圾收集器)改進
  • 堆疊遍歷 API
  • 過濾傳入的序列化資料
  • 棄用 Applet API
  • 最佳化字串連線
  • 增強的 Method Handles
  • Java 平臺日誌記錄 API 和服務
  • 緊湊字串
  • Nashorn 的解析器 API
廣告