原文連接: 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的同時將鼠標懸停在變量上,以顯示變量的推斷類型。
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
像List
, Set
和 Map
這樣的集合,它們的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
實例的副本,所以 list
和 copy
是相同的實例。 可是,若是你複製一個可變列表,那麼 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會經過檢查發出警告。
Java8中介紹的Stream也新增了方法。Stream.ofNullable
能夠從單個元素中構造一個Stream:
Stream.ofNullable(null).count(); // 0
dropWhile
與 takeWhile
方法能夠用來決定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
轉換爲 Streams
,或者使用另外一個optional做爲一個空optional的fallback。
Optional.of("foo").orElseThrow(); // foo Optional.of("foo").stream().count(); // 1 Optional.ofNullable(null) .or(() -> Optional.of("fallback")) .get(); // fallback
最基本的類之一 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
最後但並不是最不重要的是,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); }
這些是 - 在我看來 - 從Java 8遷移到11時最有趣的語言新API功能。可是功能列表並無在這裏結束。 最新的Java版本中包含了更多內容: