Java 11中的新功能和API詳解系列1

Java 11中的新功能和API詳解系列1

  • 2018.9.27
  • 版權聲明:本文爲博主chszs的原創文章,未經博主允許不得轉載。

JDK 11在語言語法方面有一個小改動,增加了相當數量的新API,以及運行單文件應用程序而無需使用編譯器的能力。此外,可以看到刪除了java.se.ee聚合器模塊,這可能會影響將現有應用程序遷移到JDK 11。

JEP-323:Lambda參數的本地變量語法

Java 10中引入了局部變量類型推斷(JEP-286)。這簡化了代碼編寫,因爲不再需要顯式聲明局部變量的類型,而是可以使用關鍵字var。JEP-323將此語法的使用擴展到Lambda表達式的參數。看下面的例子:

list.stream()
    .map((var s) -> s.toLowerCase())
    .collect(Collectors.toList());

當然,精明的Java程序員會指出Lambda表達式已經有類型推斷了,因此在這種情況下使用var會是多餘的。我們可以輕鬆地編寫相同的代碼:

list.stream()
    .map(s -> s.toLowerCase())
    .collect(Collectors.toList());

那麼爲什麼要添加var支持呢?答案是,它針對一種特殊情況,即在想要爲Lambda參數添加註釋時的情況。如果沒有涉及類型,那麼無法執行此操作。爲了避免使用顯式類型,我們可以使用var來簡化這一點,因此:

list.stream()
    .map((@Notnull var s) -> s.toLowerCase())
    .collect(Collectors.toList());

此特性需要更改Java語言規範(JLS,Java Language Specification),具體如下:

  • 第24頁:var特殊標識符的描述
  • 第627頁-30:Lambda參數
  • 第636頁:Lambda表達式的運行時評估
  • 第746頁:Lambda語法

JEP-330:啓動單文件源代碼程序

Java經常面臨的一個批評是它的語法可能很冗長,與運行一個簡單的應用程序相關的“儀式”可能會使初學者感覺難以接近。要編寫一個只打印“Hello World”的應用程序,它需要用戶先編寫一個類,編寫public static void main方法,在方法中使用System.out.println方法。完成此操作後,還必須使用javac編譯源代碼。最後,使用java執行編譯後的字節碼程序,然後才能看到“Hello World”的輸出。而在當前的大多數腳本語言中,要打印“Hello World”,操作非常簡單快捷。

JEP-330消除了編譯單個文件應用程序的需要,因此現在可以直接鍵入:

java HelloWorld.java

Java啓動程序會自動識別HelloWorld.java文件包含了Java源代碼,並在執行之前將源代碼編譯爲字節碼文件,省去了編譯這一步驟。

源文件名後放置的內容,在程序執行時作爲參數傳遞。源文件名前放置的內容,在程序編譯時和執行時作爲參數傳遞給Java啓動程序。這允許在命令行上設置類路徑。與編譯器相關的參數(例如類路徑)將傳遞給javac進行編譯。比如:

java -classpath /home/foo/java Hello.java Bonjour

相當於:

javac -classpath /home/foo/java Hello.java
java -classpath /home/foo/java Hello Bonjour

這個JEP還提供了’Shebang’支持。爲了減少在命令行上提及Java啓動程序的需要,可以在源文件的第一行包含如下內容。例如:

#!/usr/bin/java --source 11
    public class HelloWorld {
        ...

注:Shebang也叫Hashbang,在計算領域,它是由井號和感嘆號(#!)開頭的字符序列組成。在類Unix操作系統中,當使用帶有shebang的文本文件時,就好像它是可執行文件那樣,程序加載器會將除文件首行的其餘部分作爲解釋器指令進行解析,執行指定的解釋器程序,並將嘗試運行腳本時最初使用的路徑作爲參數傳遞給它,以便程序可以將該文件用作輸入數據。shebang行通常被解釋器忽略,因爲符號“#”是許多腳本語言中的註釋標記;一些不使用哈希標記開始的語言解釋器仍然可能會忽略shebang行以識別其目的。

必須使用–-source標識指定要使用的Java版本。

JEP-321:HTTP客戶端(標準)

Java 9引入了一個新的API來提供對HTTP客戶端協議(JEP-110)的支持。由於Java 9引入了Java平臺模塊系統(JPMS,Java Platform Module System),因此該API作爲孵化器模塊包含在內。孵化器模塊旨在提供新API,而不使它們成爲Java SE標準的一部分。開發人員可以嘗試這些API並提供反饋。一旦進行了任何必要的更改(此API已在JDK 10中更新),就可以移動這些API,使之以成爲標準庫的一部分。

HTTP Client API現在是Java SE 11標準庫的一部分。這向JDK引入了一個新的模塊和包。此API定義的主要類型有:

  • HttpClient
  • HttpRequest
  • HttpResponse
  • WebSocket

此API可以用於同步或異步。異步模式需使用CompletableFutures和CompletionStages。

JEP-320:移除了Java EE和CORBA模塊

通過在Java 9中引入JPMS,可以將單片rt.jar文件拆分爲多個模塊。JPMS的另一個優點是現在可以創建一個僅包含應用程序所需模塊的Java運行時,從而大大減小了程序的尺寸。通過清晰定義的模塊邊界,現在可以更輕鬆地刪除過時的Java API部分。這是JEP的作用;java.se.ee元模塊包含六個子模塊,這些模塊不再是Java SE 11標準庫的一部分,不包含在JDK中。受影響的模塊有:

  • CORBA
  • corba
  • transaction
  • activation
  • xml.bind
  • xml.ws
  • xml.ws.annotation

自Java 9以來,這些模塊已被棄用,默認情況下不包含在編譯或運行時中。如果您嘗試在JDK 9或JDK 10上編譯或運行使用這些模塊的API的應用程序,則程序會失敗。如果在代碼中使用這些模塊中的API,則需要將它們作爲單獨的模塊或庫提供。

新API

Java 11中的許​​多新API都來自HTTP客戶端模塊,現在它已成爲標準的一部分,以及包含Flight Recorder。

在這裏列出的是除了java.net.http和jdk.jfr模塊之外的所有新方法。另外也沒有列出java.security模塊中的新方法和類,它們非常特定於JEP-324和JEP-329的更改(有六個新類和八個新方法)。

java.io.ByteArrayOutputStream

  • void writeBytes(byte[]):將參數的所有字節寫入輸出流

java.io.FileReader

兩個允許指定Charset的新構造函數

java.io.FileWriter

四個允許指定Charset的新構造函數

java.io.InputStream

  • io.InputStream nullInputStream():返回InputStream未讀取任何字節的內容。當你第一次看這個方法(以及那些OutputStream,Reader,和Writer)時,可以將它們視爲/dev/null,以丟棄不需要的輸出或提供始終返回零字節的輸入。

java.io.OutputStream

  • io.OutputStream nullOutputStream()

java.io.Reader

  • io.Reader nullReader()

java.io.Writer

  • io.Writer nullWriter()

java.lang.Character

  • String toString(int):這是現有方法的重載形式,但採用int而不是char。int是Unicode代碼點。

java.lang.CharSequence

  • int compare(CharSequence, CharSequence):按CharSequence字典順序比較兩個實例。如果第一個序列按字典順序小於、等於或大於第二個序列,則返回負值、零或正值。

java.lang.ref.Reference

  • lang.Object clone():這個令人很困惑。此Reference類不實現Cloneable接口和此方法將總是拋出CloneNotSupportedException。必須有一個理由將其包含在內,大概是爲了將來的某些東西。

java.lang.Runtime

java.lang.System

這裏沒有新方法,但值得一提的是,該runFinalizersOnExit()方法現已從這兩個類中刪除(這可能是兼容性問題)。

java.lang.String

我認爲這是JDK 11中新API的亮點之一。這裏有幾個有用的新方法。

  • boolean isBlank():如果字符串爲空或僅包含空格代碼點(codepoints),則返回true,否則返回false。
  • Stream lines():返回從此字符串中提取的行的流,由行終止符分隔。
  • String repeat(int):返回一個字符串,其值是此字符串重複計數次數的串聯。
  • String strip():返回一個字符串,其值爲此字符串,刪除了所有首部和尾部的空格。
  • String stripLeading():返回一個字符串,其值爲此字符串,並刪除所有首部空格。
  • String stripTrainling():返回一個字符串,其值爲此字符串,並刪除所有尾部空格。

那麼strip()方法與現有的trim()方法有何不同?答案是兩者之間的空格定義有所不同。

java.lang.StringBuffer

java.lang.StringBuilder

這兩個類都有一個新compareTo()方法,它接受一個StringBuffer/StringBuilder並返回一個int。此方法與CharSequence的compareTo()方法用法相同。

java.lang.Thread

沒有其他方法,但已刪除destroy()方法和stop(Throwable)方法。在stop()不帶參數的方法仍然保留。這可能會出現兼容性問題。

java.nio.ByteBuffer

java.nio.CharBuffer

java.nio.DoubleBuffer

java.nio.FloatBuffer

java.nio.LongBuffer

java.nio.ShortBuffer

所有這些類現在都有一個mismatch()方法,用於查找並返回此緩衝區與給定緩衝區之間第一個不匹配的相對索引。

java.nio.channels.SelectionKey

  • int interestOpsAnd(int):以原子方式將此鍵的興趣集設置爲現有興趣集和給定值的按位取交集(“與操作”)
  • int interestOpsOr(int):以原子方式將此鍵的興趣集設置爲現有興趣集和給定值的按位取合集(“或操作”)

### java.nio.channels.Selector中

  • int select(java.util.function.Consumer, long):爲相應通道準備好進行I/O操作的鍵選擇並執行操作。long參數是超時timeout。
  • int select(java.util.function.Consumer):如上所述,除非沒有超時。
  • int selectNow(java.util.function.Consumer):如上所述,除非是非阻塞。

java.nio.file.Files

  • String readString(Path):將文件中的所有內容讀入字符串,使用UTF-8字符集從字節解碼爲字符。
  • String readString(Path, Charset):如上所述,除了使用指定的字符集Charset從字節到字符的解碼。
  • Path writeString(Path, CharSequence, java.nio.file.OpenOption[]:將CharSequence寫入文件。使用UTF-8字符集將字符編碼爲字節。
  • Path writeString(Path, CharSequence, java.nio.file.Charset, OpenOption[]:如上所述,除了Characters使用指定的Charset編碼爲字節。

java.nio.file.Path

  • Path of(String, String[]):通過轉換路徑字符串或字符串數組連接時形成的路徑字符串,返回一個Path。
  • Path of(net.URI):通過轉換URI返回一個Path。

java.util.Collection

  • Object[] toArray(java.util.function.IntFunction):返回一個包含此集合中所有元素的數組,使用提供的生成器函數來分配返回的數組。

java.util.concurrent.PriorityBlockingQueue

java.util.PriorityQueue

  • void forEach(java.util.function.Consumer):對Iterable的每個元素執行給定操作,直到處理完所有元素或操作拋出異常爲止。
  • boolean removeAll(java.util.Collection):刪除也包含在指定集合中的所有此集合的元素(可選操作)。
  • boolean removeIf(java.util.function.Predicate):刪除此集合中滿足給定謂詞的所有元素。
  • boolean retainAll(java.util.Collection):僅保留此集合中包含在指定集合中的元素(可選操作)。

java.util.concurrent.TimeUnit

  • long convert(java.time.Duration):將給定的Duratio持續時間轉換爲long型。

java.util.function.Predicate

  • Predicate not(Predicate)。返回謂詞,該謂詞是提供的謂詞的否定。

這個很有趣。例如,可以轉換下面的代碼:

lines.stream()
    .filter(s -> !s.isBlank())

轉換爲:

lines.stream()
    .filter(Predicate.not(String::isBlank))

而且,如果我們使用靜態導入,還可以轉變爲:

lines.stream()
    .filter(not(String::isBlank))

這樣的代碼更簡潔,更容易理解。

java.util.Optional

java.util.OptionalInt

java.util.OptionalDouble

java.util.OptionalLong

  • boolean isEmpty():如果某個值不存在,則返回true,否則返回false。

java.util.regex.Pattern

  • Predicate asMatchPredicate():它創建一個謂詞,測試此模式是否與給定的輸入字符串匹配。

java.util.zip.Deflater

  • int deflate(ByteBuffer):壓縮輸入數據並使用壓縮數據填充指定的緩衝區。
  • int deflate(ByteBuffer, int):壓縮輸入數據並使用壓縮數據填充指定的緩衝區。返回壓縮的實際數據字節數。
  • void setDictionary(ByteBuffer):將壓縮的預設字典設置爲給定緩衝區中的字節。這是現有方法的重載形式,現在可以接受ByteBuffer而不是字節數組。
  • void setInput(ByteBuffer):設置壓縮的輸入數據。也是現有方法的重載形式。

java.util.zip.Inflater

  • int inflate(ByteBuffer):將字節解壓縮到指定的緩衝區中。返回未壓縮的實際字節數。
  • void setDictionary(ByteBuffer):將預設字典設置爲給定緩衝區中的字節。現有方法的重載形式。
  • void setInput(ByteBuffer):設置解壓縮的輸入數據。現有方法的重載形式。

javax.print.attribute.standard.DialogOwner

這是JDK 11中的一個新類,它是一個屬性類,用於支持請求打印或頁面設置對話框保持顯示在所有窗口或某個特定窗口的頂部。

javax.swing.DefaultComboBoxModel

javax.swing.DefaultListModel

  • void addAll(Collection):添加集合中存在的所有元素。
  • void addAll(int, Collection):從指定的索引開始添加集合中存在的所有元素。

javax.swing.ListSelectionModel

  • int[] getSelectedIndices():按遞增順序返回選擇模型中所有選定索引的數組。
  • int getSelectedItemsCount():返回所選項目的數量。

jdk.jshell.EvalException

  • jshell.JShellException getCause():返回在執行客戶端中由EvalException表示的throwable包裝的原因cause,如果原因不存在(non-existent)或未知(unknown),則返回null。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章