Java 11簡介

banq發表於2018-09-26
本教程涵蓋了Java 9到11中最重要的語言和API特性。

區域性變數型別推斷
Java 10引入了一個新的語言關鍵字var,它可以在宣告區域性變數時替換型別資訊(本地意味著方法體內的變數宣告)。

在Java 10之前,你將如下宣告變數:

String text = "Hello Java 9";

現在,你可以替換String使用var。編譯器從變數的賦值中推斷出正確的型別。在這種情況下text是型別String:

var text = "Hello Java 10";

宣告的變數var仍然是靜態型別。你無法將不相容的型別重新分配給此類變數。下面程式碼段無法編譯:

var text = "Hello Java 11";
text = 23; // Incompatible types

你還可以結合final禁止將var變數重新分配給另一個值:

final var text = "Banana";
text = "Joe"; // Cannot assign a value to final variable 'text'

如果編譯器無法推斷出var正確的變數型別時,這是無法透過編譯的。以下所有程式碼示例都會導致編譯器錯誤:

// 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);
}
<p class="indent">


從Java 11開始var也可以用作lambda引數,從而能為這些引數新增註釋:

Predicate<String> predicate = (@Nullable var a) -> true;
<p class="indent">


提示:在Intellij IDEA中,你可以將滑鼠懸停在變數上,同時按CMD/CTRL顯示變數的感應型別(按鍵盤愛好者按下CTRL + J)。

HTTP客戶端
Java 9引入了一個HttpClient用於處理HTTP請求的新的API。從Java 11開始,這個API現在是最終完成,可以在標準庫包java.net中找到。讓我們來探索一下我們可以用這個API做些什麼。

新的HttpClient可以同步或非同步使用。同步請求會阻止當前執行緒,直到響應可用。BodyHandlers定義響應體的預期型別(例如,字串,位元組陣列或檔案):

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


可以非同步執行相同的請求。呼叫sendAsync不會阻塞當前執行緒,而是返回一個CompletableFuture構造非同步操作管道。

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://winterbe.com"))
    .build();
var client = HttpClient.newHttpClient();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println);
<p class="indent">

我們可以省略.GET()呼叫,因為它是預設的請求方法。

下一個示例透過傳送資料到指定的URL進行POST。類似BodyHandlers你可以用BodyPublishers來定義要作為請求主體傳送的資料型別,如字串,位元組陣列,檔案或輸入流:

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
<p class="indent">


最後一個示例演示如何透過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
<p class="indent">


集合
諸如集合List,Set並且Map已經用新方法擴充套件。List.of從指定的引數建立了一個新的不可變列表。List.copyOf建立列表的不可變副本。

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


因為list已經是不可改變的,就沒有實際需要建立這種列表例項的一個副本,因此list和copy是相同的例項。但是,如果你複製一個可變列表,copy確實是一個新例項,所以它保證在改變原始列表時沒有副作用:

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


建立不可變map時,你不必自己建立map條目,而是將鍵和值作為引數傳遞:

var map = Map.of("A", 1, "B", 2);
System.out.println(map);    // {B=2, A=1}
<p class="indent">


Java 11中的不可變集合仍然使用舊Collection API中的相同介面。但是,如果嘗試透過新增或刪除元素來修改不可變集合,則丟擲java.lang.UnsupportedOperationException。

幸運的是,如果你嘗試改變不可變集合,Intellij IDEA會透過檢查發出警告。


Streams是在Java 8中引入的,現在有三種新方法。Stream.ofNullable是從單個元素構造流:

Stream.ofNullable(null)
    .count()   // 0
<p class="indent">


方法dropWhile和takeWhile都接受謂詞來確定從流中放棄哪些元素:

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]
<p class="indent">



Optionals
Optionals還會有一些非常方便的新方法,例如,你現在可以簡單地將Optionals轉換為流,或者為空Optionals提供另一個可選的後備:

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


String字串
最基本的類之一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
<p class="indent">


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);
}
<p class="indent">


Java 11 Tutorial

相關文章