Java 11 教程

原文連接: https://wangwei.one/posts/921...

Java11 已於 2018/09/25 成功發佈,不過目前 絕大多數人 在生產環境仍舊使用的是Java 8。這篇以案例爲主的教程涵蓋了從 Java 9 到 Java 11的絕大多數重要的語法與API特性。讓咱們開始吧!html

局部變量類型推斷

Java 10引入了一個新的語言關鍵字var,它能夠在聲明局部變量 時替換類型信息( 局部 意味着方法體內的變量聲明)。java

Java 10以前,變量的聲明形式以下:react

String text = "Hello Java 9";

如今,你可使用 var 替換 String 。編譯器將會從變量的賦值中推斷出它的正確類型。在這個例子裏 變量text 即爲 String 類型:shell

var text = "Hello Java 10";

不一樣於 Javascript 中的 var 關鍵字,Java中的 var 聲明的變量仍舊是靜態類型。你不能再次賦予另外一個與原類型不符的變量值。api

var text = "Hello Java 11";
text = 23;  // ERROR: Incompatible types(類型錯誤)

var 關鍵字還能夠與 final 一塊兒使用,意義同以前的版本同樣,表示不可修改。oracle

final var text = "Hello Java 10";
text = "Hello Java 11"; // Cannot assign a value to final variable 'text'

當編譯器不能正確識別出變量的數值類型時,var將不被容許使用。下面這些代碼都是無法編譯的代碼:app

// Cannot infer type:
var a;
var nothing = null;
var lambda = () -> System.out.println("Pity!");
var method = this::someMethod;

局部變量類型推斷在與複雜的泛型類型結合時,能放大它的價值。在下面這個例子中,current 是有着一個冗長的數據類型 Map<String, List<Integer>> ,不過它的類型聲明能夠被 var 這個關鍵字簡單地替換掉,讓你避免了寫一大竄的類型麻煩事。異步

var myList = new ArrayList<Map<String, List<Integer>>>();

for (var current : myList) {
    // current is infered to type: Map<String, List<Integer>>
    System.out.println(current);
}

從Java 11開始,lambda表達式的參數也容許使用var關鍵字,這樣使得你能夠爲這些參數添加註解標識:ide

Predicate<String> predicate = (@Nullable var a) -> true;
Tip:在Intellij IDEA中,你能夠在按住CMD / CTRL的同時將鼠標懸停在變量上,以顯示變量的推斷類型。

HTTP Client

Java 9引入了一個新的孵化HttpClient API來處理HTTP請求。從Java 11開始,這個API已經能夠在標準庫 java.net 中使用了。讓咱們來探索一下經過這個API咱們能夠作些什麼。post

這個新的 HttpClient 既能夠被同步使用,也能夠被異步使用。同步請求將會阻塞當前的線程,直到返回響應消息。BodyHandlers 定義了響應消息體的類型(e.g string,byte-array 或 file):

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

一樣的請求也能夠被異步執行。調用 sendAsync 方法不會阻塞當前線程,而且會返回 CompletableFuture 對象,用來構建異步執行結果的操做流。

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://wangwei.one"))
    .build();
var client = HttpClient.newHttpClient();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println);
咱們能夠省略 .GET() 的調用,由於它默認的請求方式。

下面這個列子,咱們以POST的方法向指定的URL發送數據。相似於 BodyHandlers ,你可使用 BodyPublishers 去定義請求消息體中你想要發送的數據類型,例如 strings, byte-arrays,files 或 input-streams:

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://postman-echo.com/post"))
    .header("Content-Type", "text/plain")
    .POST(HttpRequest.BodyPublishers.ofString("Hi there!"))
    .build();
var client = HttpClient.newHttpClient();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());      // 200

最後這個列子來演示如何經過 BASIC-AUTH 來執行身份認證。

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://postman-echo.com/basic-auth"))
    .build();
var client = HttpClient.newBuilder()
    .authenticator(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("postman", "password".toCharArray());
        }
    })
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());      // 200

Collections

List, SetMap 這樣的集合,它們的API也獲得了擴展。List.of 能夠從給定的參數中建立一個不可變的list。List.copyOf 能夠建立一個 list 的不可變副本。

var list = List.of("A", "B", "C");
var copy = List.copyOf(list);
System.out.println(list == copy);   // true

由於 list 已是不可變的,因此實際上不須要實際地地去建立 list 實例的副本,所以 listcopy 是相同的實例。 可是,若是你複製一個可變列表,那麼 copy 肯定就是一個新實例,所以在改變原始 list 時,要保證沒有反作用產生:

var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy);   // false

當建立不可變的 maps 時,你不須要親自去建立一個完整的 map 集合,你能夠直接經過 Map.of 來進行建立:

var map = Map.of("A", 1, "B", 2);
System.out.println(map);    // {B=2, A=1}
Java 11中的不可變集合仍然使用舊 Collection API中的相同接口。 可是,若是嘗試經過添加或刪除元素來修改不可變集合,則會拋出java.lang.UnsupportedOperationException。 幸運的是,若是你嘗試改變不可變集合,Intellij IDEA會經過檢查發出警告。

Streams

Java8中介紹的Stream也新增了方法。Stream.ofNullable 能夠從單個元素中構造一個Stream:

Stream.ofNullable(null).count(); // 0

dropWhiletakeWhile 方法能夠用來決定stream中的哪些元素能夠被拋棄:

Stream.of(1, 2, 3, 2, 1)
    .dropWhile(n -> n < 3)
    .collect(Collectors.toList());  // [3, 2, 1]

Stream.of(1, 2, 3, 2, 1)
    .takeWhile(n -> n < 3)
    .collect(Collectors.toList());  // [1, 2]
若是你對Streams不是很熟悉,你能夠看看這篇文章 Java 8 Streams Tutorial.

Optionals

Optionals 也新增了一些很是好用的方法。例如,如今你能夠簡單地將 Optionals 轉換爲 Streams,或者使用另外一個optional做爲一個空optional的fallback。

Optional.of("foo").orElseThrow();     // foo
Optional.of("foo").stream().count();  // 1
Optional.ofNullable(null)
    .or(() -> Optional.of("fallback"))
    .get();                           // fallback

Strings

最基本的類之一 String 新增了一些輔助方法,用以修剪或檢查空格以及對字符串進行流化處理:

" ".isBlank();                // true
" Foo Bar ".strip();          // "Foo Bar"
" Foo Bar ".stripTrailing();  // " Foo Bar"
" Foo Bar ".stripLeading();   // "Foo Bar "
"Java".repeat(3);             // "JavaJavaJava"
"A\nB\nC".lines().count();    // 3

InputStreams

最後但並不是最不重要的是,InputStream最終得到了一個很是有用的方法來將數據傳輸到OutputStream,這是一個在處理原始數據流時很是常見的用例。

var classLoader = ClassLoader.getSystemClassLoader();
var inputStream = classLoader.getResourceAsStream("myFile.txt");
var tempFile = File.createTempFile("myFileCopy", "txt");
try (var outputStream = new FileOutputStream(tempFile)) {
    inputStream.transferTo(outputStream);
}

其餘JVM特性

這些是 - 在我看來 - 從Java 8遷移到11時最有趣的語言新API功能。可是功能列表並無在這裏結束。 最新的Java版本中包含了更多內容:

相關文章
相關標籤/搜索