Java 11(2018 年 9 月发布)包含许多重要且有用的更新。让我们看看它为开发人员和架构师带来的新功能和改进。

1. HTTP 客户端 API

Java 有HttpURLConnection很长一段时间的 HTTP 通信类。但随着时间的推移,应用程序的需求变得复杂和更加苛刻。在 Java 11 之前,开发人员不得不求助于功能丰富的库,如Apache HttpComponentsOkHttp等。
我们看到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. 而 program 只能是单文件 program

3. 字符串 API 变化

3.1. String.repeat(整数)

这个方法只是简单地重复一个字符串的 n次数。它返回一个字符串,其值是给定字符串重复 N 次的串联。
如果此字符串为空或计数为零,则返回空字符串。
public class HelloWorld 
{
    public static void main(String[] args) 
    {
        String str = "1".repeat(5);
 
        System.out.println(str);    //11111
    }
}

3.2. 字符串.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()

此方法有助于将多行文本作为Stream 处理
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是一个容器对象,它可能包含也可能不包含非空值。如果不存在任何值,则该对象被视为空。
如果存在值,则先前存在的方法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
    }
}

评论区域