Java 11 新特性概览与实战教程
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()); } } }