JDK 11 是继 JDK 8 之后最受关注的长期支持版本,也是目前企业级应用升级的首选目标。相比 JDK 8,JDK 11 不仅带来了更现代化的语法糖(如局部变量推断),还在核心类库(String、HTTP Client)和垃圾回收器(G1、ZGC)上进行了重大升级。
本教程将带你深入掌握 JDK 11 的核心特性,并通过实战示例让你快速上手。
一、 语法层面的现代化:更简洁的代码
JDK 11 在语法上的改进主要集中在减少样板代码,提升代码的可读性。
1. Lambda 表达式中的局部变量类型推断
在 JDK 10 中引入了 var 关键字用于局部变量推断,而在 JDK 11 中,这一特性扩展到了 Lambda 表达式的参数中。
- 核心价值:允许在 Lambda 参数上使用注解,同时保持代码简洁。
示例:
import java.util.List;
import java.util.stream.Collectors;
public class LambdaVarExample {
public static void main(String[] args) {
List<String> names = List.of("Alice", "Bob", "Charlie");
// JDK 11 写法:使用 var,且可以添加注解(如 @Nonnull)
// 这在以前必须显式写出类型才能加注解
names.forEach((@Deprecated var s) -> System.out.println(s));
}
}
2. 直接运行单文件 Java 代码
对于简单的脚本或测试,不再需要显式执行 javac 编译。
- 命令 :
java Hello.java - 场景:快速验证逻辑、编写一次性脚本。
二、 核心类库增强:告别工具类依赖
JDK 11 对 String、Files 等常用类进行了大幅增强,许多原本依赖 Apache Commons 或 Guava 的功能现在可以直接使用原生 API。
1. String 类的"超级进化"
这是开发中最常用的新特性,极大简化了字符串处理。
| 方法 | 描述 | 实战示例 |
|---|---|---|
isBlank() |
判断字符串是否为空或仅包含空白符(包括全角空格) | " ".isBlank() 返回 true |
strip() |
去除首尾空白符(支持 Unicode 10 标准) | " Java ".strip() 返回 "Java" |
stripLeading() |
仅去除前导空白符 | - |
stripTrailing() |
仅去除尾部空白符 | - |
repeat(int n) |
字符串重复拼接 | "=".repeat(10) 返回 "==========" |
lines() |
按行分割,返回 Stream<String> |
处理多行文本配置 |
示例代码:
String text = " Hello \n World ";
// 1. 精准去除空白(支持全角空格)
if (!text.strip().isBlank()) {
System.out.println("有效文本: " + text.strip());
}
// 2. 字符串重复(生成分隔线等)
String separator = "-".repeat(20);
// 3. 按行处理
text.lines().forEach(line -> System.out.println("行内容: " + line.strip()));
2. Files 类增强:一行读写
以前读写文件需要繁琐的 IO 流,现在可以一行代码搞定。
import java.nio.file.Files;
import java.nio.file.Path;
// 写入文件
Files.writeString(Path.of("test.txt"), "Hello JDK 11");
// 读取文件
String content = Files.readString(Path.of("test.txt"));
3. 集合工厂方法与转数组增强
- 不可变集合 :
List.of(),Set.of(),Map.of()提供了快速创建不可变集合的方式。 - 转数组 :
list.toArray(String[]::new)语法更加优雅,无需手动指定数组大小。
三、 重量级新特性:标准化 HTTP Client
JDK 11 正式引入了标准化的 java.net.http.HttpClient,旨在替代老旧的 HttpURLConnection,并具备与 OkHttp 等第三方库竞争的能力。
核心优势:
- 支持 HTTP/2 协议(多路复用)。
- 原生支持异步非阻塞请求。
- 支持 WebSocket。
实战代码:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpClientExample {
public static void main(String[] args) throws Exception {
// 1. 构建客户端
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
// 2. 构建请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/get"))
.GET()
.build();
// 3. 发送同步请求
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("状态码: " + response.statusCode());
System.out.println("响应体: " + response.body());
// 4. 发送异步请求
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}
}
四、 性能与底层:GC 与诊断工具
1. G1 成为默认垃圾回收器
在 JDK 11 中,G1 GC 正式成为默认的垃圾回收器,替代了 JDK 8 中的 Parallel GC。
- 优势:G1 在吞吐量和延迟之间取得了更好的平衡,特别适合大内存(8GB+)服务器。
- 配置 :默认开启,无需额外参数。如果需要调整停顿时间目标,可使用
-XX:MaxGCPauseMillis=200。
2. ZGC 实验性引入
JDK 11 引入了 ZGC(Z Garbage Collector),这是一个可扩展的低延迟垃圾回收器。
- 目标:停顿时间不超过 10ms。
- 适用场景:超大堆内存(TB 级别)且对延迟极其敏感的应用。
- 开启方式 :
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC(注意:在 JDK 11 中 ZGC 仍为实验性特性,生产环境建议谨慎评估,JDK 15+ 后已转正)。
3. 诊断工具升级
- JFR (Java Flight Recorder):JDK 11 开源了 JFR,这是一个强大的低开销性能分析工具,可用于生产环境监控。
- jcmd :整合了
jstack,jmap,jstat等功能,成为故障排查的首选命令。
五、 生产环境最佳实践与避坑指南
1. 字符串处理的陷阱
trim()vsstrip():trim()只能处理 ASCII 码范围内的空白符(<= U+0020)。如果你的业务涉及国际化(如全角空格),必须使用strip()。
2. 升级兼容性
- 移除模块 :JDK 11 移除了 Java EE 和 CORBA 模块(如
javax.xml.ws)。如果你的项目依赖这些,需要引入第三方库(如 Jakarta EE)替代,否则启动会报错。 - Oracle JDK 收费 :Oracle JDK 11 商用需付费。生产环境强烈建议使用 OpenJDK 11(如 Adoptium/Eclipse Temurin, Amazon Corretto 等),它们在功能上与 Oracle JDK 一致且免费。
3. 性能调优建议
- GC 选择 :对于 8GB 内存以下的机器,如果追求极致吞吐量,可以切回 Parallel GC (
-XX:+UseParallelGC);对于 8GB 以上,保持默认的 G1 即可。 - HTTP Client :新的
HttpClient是线程安全的,建议作为单例复用,避免频繁创建实例导致连接池资源浪费。
六、 实战工具类封装
为了更好地利用 JDK 11 新特性,这里提供两个开箱即用的工具类。
1. 字符串清洗工具类 (StringUtils)
利用 strip() 处理国际化空白字符,利用 isBlank() 进行空值校验。
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 基于 JDK 11+ 的字符串工具类
*/
public class StringUtils {
/**
* 安全去除首尾空白符(支持全角空格等 Unicode 空白符)
* 如果输入为 null,返回空字符串
*/
public static String clean(String input) {
if (input == null) {
return "";
}
// 使用 JDK 11 strip() 替代 trim()
return input.strip();
}
/**
* 检查字符串是否为空或仅包含空白符
*/
public static boolean isBlank(String input) {
return input == null || input.isBlank();
}
/**
* 清洗字符串列表:去除每一项的首尾空白,并过滤掉空项
* 例如:[" a ", " ", "b"] -> ["a", "b"]
*/
public static List<String> cleanList(List<String> input) {
if (input == null) {
return List.of();
}
return input.stream()
.map(StringUtils::clean) // 清洗
.filter(s -> !s.isBlank()) // 过滤空项
.collect(Collectors.toList());
}
/**
* 重复字符串生成器
*/
public static String repeat(String pattern, int count) {
if (pattern == null || count <= 0) return "";
return pattern.repeat(count);
}
}
2. HTTP Client 封装工具类 (HttpUtil)
封装 JDK 11 原生 HttpClient,支持同步 GET/POST 请求,处理 JSON 数据。
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;
/**
* 基于 JDK 11 HttpClient 的简易 HTTP 工具类
*/
public class HttpUtil {
private final HttpClient client;
// 单例模式或静态工厂方法推荐
private static final HttpUtil INSTANCE = new HttpUtil();
private HttpUtil() {
this.client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10)) // 设置连接超时
.build();
}
public static HttpUtil getInstance() {
return INSTANCE;
}
/**
* 发送 GET 请求
* @param url 请求地址
* @return 响应体字符串
*/
public String get(String url) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return response.body();
} else {
throw new IOException("HTTP Error: " + response.statusCode());
}
}
/**
* 发送 POST 请求 (JSON)
* @param url 请求地址
* @param jsonBody 请求体 JSON 字符串
* @return 响应体字符串
*/
public String post(String url, String jsonBody) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200 || response.statusCode() == 201) {
return response.body();
} else {
throw new IOException("HTTP Error: " + response.statusCode());
}
}
}