趕快看看Java11,不然你就out了!

雙哥發表於2019-11-05

前言

趕快看看Java11,不然你就out了! 這個是個人部落格原文的連結。Java更新的太快了,都學不過來了,最近了解一些Java8以後的一些特性,寫下來希望對大家有幫助。

為什麼選擇Java11

  • 容器環境支援,GC等領域的增強。
  • 進行了瘦身,更輕量級,安裝包體積小。
  • JDK11 是一個長期支援版。

特性介紹

由於直接從Java8跨越到Java11,所以特性介紹就把Java9-Java11的部分特性一起介紹一下。想要了解Java8特性的朋友可以去我的部落格找「Java8系列」。

Jshell @since 9

Jshell在Java9中就被提出來了,可以直接在終端寫Java程式,回車就可以執行。Jshell預設會匯入下面的一些包,所以在Jshell環境中這些包的內容都是可以使用的。

import java.lang.*;
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.*;
複製程式碼
1.什麼是Jshell?

Jshell是在 Java 9 中引入的。它提供了一個互動式 shell,用於快速原型、除錯、學習 Java 及 Java API,所有這些都不需要 public static void main 方法,也不需要在執行之前編譯程式碼。

2.Jshell的使用

開啟終端,鍵入jshell進入jshell環境,然後輸入/help intro可以檢視Jshell的介紹。

 lixiaoshuang@localhost  ~  jshell
|  歡迎使用 JShell -- 版本 11.0.2
|  要大致瞭解該版本, 請鍵入: /help intro

jshell> /help intro
|
|                                   intro
|                                   =====
|
|  使用 jshell 工具可以執行 Java 程式碼,從而立即獲取結果。
|  您可以輸入 Java 定義(變數、方法、類等等),例如:int x = 8
|  或 Java 表示式,例如:x + x
|  或 Java 語句或匯入。
|  這些小塊的 Java 程式碼稱為“片段”。
|
|  這些 jshell 工具命令還可以讓您瞭解和
|  控制您正在執行的操作,例如:/list
|
|  有關命令的列表,請執行:/help

jshell>
複製程式碼

Jshell確實是一個好用的小工具,這裡不做過多介紹,我就舉一個例子,剩下的大家自己體會。比如我們現在就想隨機生成一個UUID,以前需要這麼做:

  • 建立一個類。
  • 建立一個main方法。
  • 然後寫一個生成UUID的邏輯,執行。

現在只需要,進入開啟終端鍵入jshell,然後直接輸入var uuid = UUID.randomUUID()回車。就可以看到uuid的回顯,這樣我們就得到了一個uuid。並不需要public static void main(String[] args);

 lixiaoshuang@localhost  ~  jshell
|  歡迎使用 JShell -- 版本 11.0.2
|  要大致瞭解該版本, 請鍵入: /help intro

jshell> var uuid = UUID.randomUUID();
 uuid ==> 9dac239e-c572-494f-b06d-84576212e012
jshell>
複製程式碼
3.怎麼退出Jshell?

在Jshell環境中鍵入/exit就可以退出。

 lixiaoshuang@localhost  ~ 
 lixiaoshuang@localhost  ~  jshell
|  歡迎使用 JShell -- 版本 11.0.2
|  要大致瞭解該版本, 請鍵入: /help intro

jshell> var uuid = UUID.randomUUID();
uuid ==> 9dac239e-c572-494f-b06d-84576212e012

jshell> /exit
|  再見
 lixiaoshuang@localhost  ~ 
複製程式碼

模組化(Module)@since 9

1.什麼是模組化?

模組化就是增加了更高階別的聚合,是Package的封裝體。Package是一些類路徑名字的約定,而模組是一個或多個Package組成的封裝體。

java9以前 :package => class/interface。

java9以後 :module => package => class/interface。

那麼JDK被拆為了哪些模組呢?開啟終端執行java --list-modules檢視。

lixiaoshuang@localhost  ~ java --list-modules
java.base@11.0.2
java.compiler@11.0.2
java.datatransfer@11.0.2
java.desktop@11.0.2
java.instrument@11.0.2
java.logging@11.0.2
java.management@11.0.2
java.management.rmi@11.0.2
java.naming@11.0.2
java.net.http@11.0.2
java.prefs@11.0.2
java.rmi@11.0.2
java.scripting@11.0.2
java.se@11.0.2
java.security.jgss@11.0.2
java.security.sasl@11.0.2
java.smartcardio@11.0.2
java.sql@11.0.2
java.sql.rowset@11.0.2
java.transaction.xa@11.0.2
java.xml@11.0.2
java.xml.crypto@11.0.2
jdk.accessibility@11.0.2
jdk.aot@11.0.2
jdk.attach@11.0.2
jdk.charsets@11.0.2
jdk.compiler@11.0.2
jdk.crypto.cryptoki@11.0.2
jdk.crypto.ec@11.0.2
jdk.dynalink@11.0.2
jdk.editpad@11.0.2
jdk.hotspot.agent@11.0.2
jdk.httpserver@11.0.2
jdk.internal.ed@11.0.2
jdk.internal.jvmstat@11.0.2
jdk.internal.le@11.0.2
jdk.internal.opt@11.0.2
jdk.internal.vm.ci@11.0.2
jdk.internal.vm.compiler@11.0.2
jdk.internal.vm.compiler.management@11.0.2
jdk.jartool@11.0.2
jdk.javadoc@11.0.2
jdk.jcmd@11.0.2
jdk.jconsole@11.0.2
jdk.jdeps@11.0.2
jdk.jdi@11.0.2
jdk.jdwp.agent@11.0.2
jdk.jfr@11.0.2
jdk.jlink@11.0.2
jdk.jshell@11.0.2
jdk.jsobject@11.0.2
jdk.jstatd@11.0.2
jdk.localedata@11.0.2
jdk.management@11.0.2
jdk.management.agent@11.0.2
jdk.management.jfr@11.0.2
jdk.naming.dns@11.0.2
jdk.naming.rmi@11.0.2
jdk.net@11.0.2
jdk.pack@11.0.2
jdk.rmic@11.0.2
jdk.scripting.nashorn@11.0.2
jdk.scripting.nashorn.shell@11.0.2
jdk.sctp@11.0.2
jdk.security.auth@11.0.2
jdk.security.jgss@11.0.2
jdk.unsupported@11.0.2
jdk.unsupported.desktop@11.0.2
jdk.xml.dom@11.0.2
jdk.zipfs@11.0.2
複製程式碼
2.為什麼這麼做?

大家都知道JRE中有一個超級大的rt.jar(60多M),tools.jar也有幾十兆,以前執行一個hello world也需要上百兆的環境。

  • 讓Java SE程式更加容易輕量級部署。
  • 強大的封裝能力。
  • 改進元件間的依賴管理,引入比jar粒度更大的Module。
  • 改進效能和安全性。
3.怎麼定義模組?

模組的是通過module-info.java進行定義,編譯後打包後,就成為一個模組的實體。下面來看下最簡單的模組定義。

趕快看看Java11,不然你就out了!

趕快看看Java11,不然你就out了!

4.模組的關鍵字
  • open

    用來指定開放模組,開放模組的所有包都是公開的,public的可以直接引用使用,其他型別可以通過反射得到。

    open module module.one {
        //匯入日誌包
       requires java.logging;
    
    }
    複製程式碼
  • opens

    opens 用來指定開放的包,其中public型別是可以直接訪問的,其他型別可以通過反射得到。

    module module.one {
    
        opens <package>;
    }
    複製程式碼
  • exports

    exports用於指定模組下的哪些包可以被其他模組訪問。

    module module.one {
        
        exports <package>;
        
        exports <package> to <module1>, <module2>...;
    }
    複製程式碼
  • requires

    該關鍵字宣告當前模組與另一個模組的依賴關係。

    module module.one {
    
        requires <package>;
    
    }
    複製程式碼
  • uses、provides…with…

    uses語句使用服務介面的名字,當前模組就會發現它,使用java.util.ServiceLoader類進行載入,必須是本模組中的,不能是其他模組中的.其實現類可以由其他模組提供。

    module module.one {
    
        //對外提供的介面服務 ,下面指定的介面以及提供服務的impl,如果有多個實現類,用用逗號隔開
        uses <介面名>;
    
        provides <介面名> with <介面實現類>,<介面實現類>;
    
    }
    複製程式碼

var關鍵字 @since 10

1.var是什麼?

var是Java10中新增的區域性型別變數推斷。它會根據後面的值來推斷變數的型別,所以var必須要初始化。

例:

var a;       ❌
var a = 1;   ✅
複製程式碼
2.var使用示例
  • var定義區域性變數

    var a = 1; 
    等於
    int a = 1;
    複製程式碼
  • var接收方法返回時

    var result = this.getResult();
    等於
    String result = this.getResult();
    複製程式碼
  • var迴圈中定義區域性變數

    for (var i = 0; i < 5; i++) {
       System.out.println(i);
    }
    等於
    for (int i = 0; i < 5; i++) {
       System.out.println(i);
    }
    複製程式碼
  • var結合泛型

    var list1 = new ArrayList<String>();  //在<>中指定了list型別為String
    等於
    List<String> list1 = new ArrayList<>();
    
    var list2 = new ArrayList<>();        //<>裡預設會是Object
    複製程式碼
  • var在Lambda中使用(java11才可以使用)

    Consumer<String> Consumer = (var i) -> System.out.println(i);
    等於
    Consumer<String> Consumer = (String i) -> System.out.println(i);
    複製程式碼
3.var不能再哪裡使用?
  • 類成員變數型別。
  • 方法返回值型別。
  • Java10中Lambda不能使用var,Java11中可以使用。

增強api

1.字串增強 @since 11
// 判斷字串是否為空白
" ".isBlank();                     // true

// 去除首尾空格
" Hello Java11 ".strip();          // "Hello Java11"

// 去除尾部空格 
" Hello Java11 ".stripTrailing();  // " Hello Java11"

// 去除首部空格 
" Hello Java11 ".stripLeading();   // "Hello Java11 "

// 複製字串
"Java11".repeat(3);                // "Java11Java11Java11"

// 行數統計
"A\nB\nC".lines().count();         // 3
複製程式碼
2.集合增強

從Java 9 開始,jdk裡面就為集合(List、Set、Map)增加了of和copyOf方法。它們用來建立不可變集合。

  • of() @since 9
  • copyOf() @since 10

示例一:

        var list = List.of("Java", "Python", "C"); //不可變集合
        var copy = List.copyOf(list);         //copyOf判斷是否是不可變集合型別,如果是直接返回
        System.out.println(list == copy);    // true

        var list = new ArrayList<String>();  // 這裡返回正常的集合
        var copy = List.copyOf(list);        // 這裡返回一個不可變集合
        System.out.println(list == copy);    // false
複製程式碼

示例二:

        var set = Set.of("Java", "Python", "C");
        var copy = Set.copyOf(set);
        System.out.println(set == copy);     // true

        var set1 = new HashSet<String>();
        var copy1 = List.copyOf(set1);
        System.out.println(set1 == copy1);   // false
複製程式碼

示例三:

        var map = Map.of("Java", 1, "Python", 2, "C", 3);
        var copy = Map.copyOf(map);
        System.out.println(map == copy);     // true

        var map1 = new HashMap<String, Integer>();
        var copy1 = Map.copyOf(map1);
        System.out.println(map1 == copy1);   // false
複製程式碼

注意:使用 of 和 copyOf 建立的集合為不可變集合,不能進行新增、刪除、替換、排序等操作,不然會報java.lang.UnsupportedOperationException異常,使用Set.of()不能出現重複元素、Map.of()不能出現重複key,否則回報java.lang.IllegalArgumentException。

3.Stream增強 @since 9

Stream是Java 8 中的特性,在Java 9 中為其新增了4個方法:

  • ofNullable(T t)

    此方法可以接收null來建立一個空流

    以前
    Stream.of(null);  //報錯
    現在
    Stream.ofNullable(null);
    複製程式碼
  • takeWhile(Predicate<? super T> predicate)

    此方法根據Predicate介面來判斷如果為true就 取出 來生成一個新的流,只要碰到false就終止,不管後邊的元素是否符合條件。

            Stream<Integer> integerStream = Stream.of(6, 10, 11, 15, 20);
            Stream<Integer> takeWhile = integerStream.takeWhile(t -> t % 2 == 0);
            takeWhile.forEach(System.out::println);   // 6,10
    複製程式碼
  • dropWhile(Predicate<? super T> predicate)

    此方法根據Predicate介面來判斷如果為true就 丟棄 來生成一個新的流,只要碰到false就終止,不管後邊的元素是否符合條件。

            Stream<Integer> integerStream = Stream.of(6, 10, 11, 15, 20);
            Stream<Integer> takeWhile = integerStream.dropWhile(t -> t % 2 == 0);
            takeWhile.forEach(System.out::println);  //11,15,20
    複製程式碼
  • iterate過載

    以前使用iterate方法生成無限流需要配合limit進行截斷

            Stream<Integer> limit = Stream.iterate(1, i -> i + 1).limit(5);
            limit.forEach(System.out::println);   //1,2,3,4,5
    複製程式碼

    現在過載後這個方法增加了個判斷引數

            Stream<Integer> iterate = Stream.iterate(1, i -> i <= 5, i -> i + 1);
            iterate.forEach(System.out::println);  //1,2,3,4,5
    複製程式碼
4.Optional增強 @since 9
  • stream()

    如果為空返回一個空流,如果不為空將Optional的值轉成一個流。

            //返回Optional值的流
            Stream<String> stream = Optional.of("Java 11").stream();
            stream.forEach(System.out::println);    // Java 11
            
            //返回空流
            Stream<Object> stream = Optional.ofNullable(null).stream();
            stream.forEach(System.out::println);    // 
    複製程式碼
  • ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

    個人感覺這個方法就是結合isPresent()對Else的增強,ifPresentOrElse 方法的用途是,如果一個 Optional 包含值,則對其包含的值呼叫函式 action,即 action.accept(value),這與 ifPresent 一致;與 ifPresent 方法的區別在於,ifPresentOrElse 還有第二個引數 emptyAction —— 如果 Optional 不包含值,那麼 ifPresentOrElse 便會呼叫 emptyAction,即 emptyAction.run()。

            Optional<Integer> optional = Optional.of(1);
            optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
                    System.out.println("Not Present."));    //Value: 1
            
            optional = Optional.empty();
            optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
                    System.out.println("Not Present."));    //Not Present.
    複製程式碼
  • or(Supplier<? extends Optional<? extends T>> supplier)

        Optional<String> optional1 = Optional.of("Java");
        Supplier<Optional<String>> supplierString = () -> Optional.of("Not Present");
        optional1 = optional1.or( supplierString);
        optional1.ifPresent( x -> System.out.println("Value: " + x));  //Value: Java

        optional1 = Optional.empty();
        optional1 = optional1.or( supplierString);
        optional1.ifPresent( x -> System.out.println("Value: " + x)); //Value: Not Present
複製程式碼
5.InputStream增強 @since 9
        String lxs = "java";
        try (var inputStream = new ByteArrayInputStream(lxs.getBytes());
             var outputStream = new ByteArrayOutputStream()) {
            inputStream.transferTo(outputStream);
            System.out.println(outputStream);    //java
        }
複製程式碼

HTTP Client API

改api支援同步和非同步兩種方式,下面是兩種方式的示例:

        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://www.baidu.com/"))
                .build();
        var client = HttpClient.newHttpClient();
        // 同步
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());

        // 非同步
        CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        //這裡會阻塞
        HttpResponse<String> response1 = sendAsync.get();
        System.out.println(response1.body());
複製程式碼

直接執行java檔案

我們都知道以前要執行一個.java檔案,首先要javac編譯成.class檔案,然後在java執行:

//編譯
javac Java11.java
//執行
java Java11
複製程式碼

在java11中,只需要通過java一個命令就可以搞定

java Java11.java
複製程式碼

移除內容

  • com.sun.awt.AWTUtilities。
  • sun.misc.Unsafe.defineClass 使用java.lang.invoke.MethodHandles.Lookup.defineClass來替代。
  • Thread.destroy() 以及 Thread.stop(Throwable) 方法。
  • sun.nio.ch.disableSystemWideOverlappingFileLockCheck 屬性。
  • sun.locale.formatasdefault 屬性。
  • jdk snmp 模組。
  • javafx,openjdk 是從java10版本就移除了,oracle java10還尚未移除javafx ,而java11版本將javafx也移除了。
  • Java Mission Control,從JDK中移除之後,需要自己單獨下載。
  • Root Certificates :Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom。
  • 在java11中將java9標記廢棄的Java EE及CORBA模組移除掉。

完全支援Linux容器(包括docker)

許多執行在Java虛擬機器中的應用程式(包括Apache Spark和Kafka等資料服務以及傳統的企業應用程式)都可以在Docker容器中執行。但是在Docker容器中執行Java應用程式一直存在一個問題,那就是在容器中執行JVM程式在設定記憶體大小和CPU使用率後,會導致應用程式的效能下降。這是因為Java應用程式沒有意識到它正在容器中執行。隨著Java 10的釋出,這個問題總算得以解決,JVM現在可以識別由容器控制組(cgroups)設定的約束。可以在容器中使用記憶體和CPU約束來直接管理Java應用程式,其中包括:

  • 遵守容器中設定的記憶體限制
  • 在容器中設定可用的CPU
  • 在容器中設定CPU約束

Java 10的這個改進在Docker for Mac、Docker for Windows以及Docker Enterprise Edition等環境均有效。

總結

Java版本特性.png

參考

segmentfault.com/a/119000001…

感謝大家的觀看,希望多多關注哦。 如有錯誤,煩請指正。

相關文章