Java 11簡介
區域性變數型別推斷
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);
}
從Java 11開始var也可以用作lambda引數,從而能為這些引數添加註釋:
Predicate<String> predicate = (@Nullable var a) -> true;
提示:在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());
可以非同步執行相同的請求。呼叫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);
我們可以省略.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
最後一個示例演示如何通過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已經用新方法擴充套件。List.of從指定的引數建立了一個新的不可變列表。List.copyOf建立列表的不可變副本。
var list = List.of("A", "B", "C");
var copy = List.copyOf(list);
System.out.println(list == copy);// true
因為list已經是不可改變的,就沒有實際需要建立這種列表例項的一個副本,因此list和copy是相同的例項。但是,如果你複製一個可變列表,copy確實是一個新例項,所以它保證在改變原始列表時沒有副作用:
var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy);// false
建立不可變map時,你不必自己建立map條目,而是將鍵和值作為引數傳遞:
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是在Java 8中引入的,現在有三種新方法。Stream.ofNullable是從單個元素構造流:
Stream.ofNullable(null)
.count()// 0
方法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]
Optionals
Optionals還會有一些非常方便的新方法,例如,你現在可以簡單地將Optionals轉換為流,或者為空Optionals提供另一個可選的後備:
Optional.of("foo").orElseThrow();// foo
Optional.of("foo").stream().count();// 1
Optional.ofNullable(null)
.or(() -> Optional.of("fallback"))
.get();// fallback
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
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);
}