Java 11(于 2018 年 9 月发布)包含许多重要且有用的更新。 让我们看看它为开发人员和建筑师带来的新特性和改进。
1. HTTP 客户端 API
Java 长时间使用HttpURLConnection
类进行 HTTP 通信。 但是随着时间的流逝,要求变得越来越复杂,对应用程序的要求也越来越高。 在 Java 11 之前,开发人员不得不诉诸特性丰富的库,例如 Apache HttpComponents 或 OkHttp 等。
我们看到 Java 9 版本包含HttpClient
实现作为实验特性。 随着时间的流逝,它已经成为 Java 11 的最终特性。现在,Java 应用程序可以进行 HTTP 通信,而无需任何外部依赖。
1.1 如何使用HttpClient
与java.net.http
模块的典型 HTTP 交互看起来像:
- 创建
HttpClient
的实例,并根据需要对其进行配置。 - 创建
HttpRequest
的实例并填充信息。 - 将请求传递给客户端,执行请求,并检索
HttpResponse
的实例。 - 处理
HttpResponse
中包含的信息。
HTTP API 可以处理同步和异步通信。 让我们来看一个简单的例子。
1.2 同步请求示例
请注意,http 客户端 API 如何使用构建器模式创建复杂的对象。
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
try
{
String urlEndpoint = "https://postman-echo.com/get";
URI uri = URI.create(urlEndpoint + "?foo1=bar1&foo2=bar2");
HttpRequest request = HttpRequest.newBuilder()
.uri(uri)
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Status code: " + response.statusCode());
System.out.println("Headers: " + response.headers().allValues("content-type"));
System.out.println("Body: " + response.body());
1.2 异步请求示例
如果我们不想等待响应,那么异步通信很有用。 我们提供了回调处理程序,可在响应可用时执行。
注意使用sendAsync()
方法发送异步请求。
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
final List<URI> uris = Stream.of(
"https://www.google.com/",
"https://www.github.com/",
"https://www.yahoo.com/"
).map(URI::create).collect(toList());
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
CompletableFuture[] futures = uris.stream()
.map(uri -> verifyUri(httpClient, uri))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();
private CompletableFuture<Void> verifyUri(HttpClient httpClient,
URI uri)
{
HttpRequest request = HttpRequest.newBuilder()
.timeout(Duration.ofSeconds(5))
.uri(uri)
.build();
return httpClient.sendAsync(request,HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::statusCode)
.thenApply(statusCode -> statusCode == 200)
.exceptionally(ex -> false)
.thenAccept(valid ->
{
if (valid) {
System.out.println("[SUCCESS] Verified " + uri);
} else {
System.out.println("[FAILURE] Could not " + "verify " + uri);
}
});
}
2. 启动不编译的单文件程序
传统上,对于我们要执行的每个程序,我们都需要先对其进行编译。 出于测试目的,小型程序似乎不必要地耗时。
Java 11 对其进行了更改,现在我们可以执行单个文件中包含的 Java 源代码,而无需先对其进行编译。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
要执行上述类,请直接使用java
命令运行它。
$ java HelloWorld.java
Hello World!
注意,程序不能使用
java.base module
之外的任何外部依赖项。 并且该程序只能是单文件程序。
3. 字符串 API 的更改
3.1 String.repeat(int)
此方法仅重复字符串n
次。 它返回一个字符串,其值是重复 N 次给定字符串的连接。
如果此字符串为空或count
为零,则返回空字符串。
public class HelloWorld
{
public static void main(String[] args)
{
String str = "1".repeat(5);
System.out.println(str); //11111
}
}
3.2 String.isBlank()
此方法指示字符串是空还是仅包含空格。 以前,我们一直在 Apache 的StringUtils.java
中使用它。
public class HelloWorld
{
public static void main(String[] args)
{
"1".isBlank(); //false
"".isBlank(); //true
" ".isBlank(); //true
}
}
3.3 String.strip()
此方法需要除去前导和尾随空白。 通过使用String.stripLeading()
仅删除开头字符,或者使用String.stripTrailing()
仅删除结尾字符,我们甚至可以更加具体。
public class HelloWorld
{
public static void main(String[] args)
{
" hi ".strip(); //"hi"
" hi ".stripLeading(); //"hi "
" hi ".stripTrailing(); //" hi"
}
}
3.4 String.lines()
此方法有助于将多行文本作为流来处理。
public class HelloWorld
{
public static void main(String[] args)
{
String testString = "hello\nworld\nis\nexecuted";
List<String> lines = new ArrayList<>();
testString.lines().forEach(line -> lines.add(line));
assertEquals(List.of("hello", "world", "is", "executed"), lines);
}
}
4. Collection.toArray(IntFunction)
在 Java 11 之前,将集合转换为数组并不容易。 Java 11 使转换更加方便。
public class HelloWorld
{
public static void main(String[] args)
{
List<String> names = new ArrayList<>();
names.add("alex");
names.add("brian");
names.add("charles");
String[] namesArr1 = names.toArray(new String[names.size()]); //Before Java 11
String[] namesArr2 = names.toArray(String[]::new); //Since Java 11
}
}
5. Files.readString()
和Files.writeString()
使用这些重载方法,Java 11 的目标是减少大量样板代码,从而使文件读写更加容易。
public class HelloWorld
{
public static void main(String[] args)
{
//Read file as string
URI txtFileUri = getClass().getClassLoader().getResource("helloworld.txt").toURI();
String content = Files.readString(Path.of(txtFileUri),Charset.defaultCharset());
//Write string to file
Path tmpFilePath = Path.of(File.createTempFile("tempFile", ".tmp").toURI());
Path returnedFilePath = Files.writeString(tmpFilePath,"Hello World!",
Charset.defaultCharset(), StandardOpenOption.WRITE);
}
}
6. Optional.isEmpty()
Optional
是一个容器对象,可能包含也可能不包含非null
值。 如果不存在任何值,则该对象被认为是空的。
如果存在值,则先前存在的方法isPresent()
返回true
,否则返回false
。 有时,它迫使我们编写不可读的负面条件。
isEmpty()
方法与isPresent()
方法相反,如果存在值,则返回false
,否则返回true
。
因此,在任何情况下我们都不会写负面条件。 适当时使用这两种方法中的任何一种。
public class HelloWorld
{
public static void main(String[] args)
{
String currentTime = null;
assertTrue(!Optional.ofNullable(currentTime).isPresent()); //It's negative condition
assertTrue(Optional.ofNullable(currentTime).isEmpty()); //Write it like this
currentTime = "12:00 PM";
assertFalse(!Optional.ofNullable(currentTime).isPresent()); //It's negative condition
assertFalse(Optional.ofNullable(currentTime).isEmpty()); //Write it like this
}
}
向我提供有关 Java 11 中这些新 API 更改的问题。
学习愉快!
参考: Java 11 发行文档