JDK 11 特性详解
1. 概述
JDK 11(2018年9月发布)是Java的长期支持(LTS)版本,引入了多项重要特性和性能改进。作为继JDK 8之后的又一个LTS版本,JDK 11在保持兼容性的同时,提供了更现代、更高效的API和工具,为Java开发者带来了更好的开发体验和性能表现。
2. 核心特性详解
2.1 HTTP Client API
特性说明:
- 标准化的HTTP客户端API,替代了旧的HttpURLConnection
- 支持HTTP/1.1和HTTP/2协议
- 提供同步和异步请求方式
- 支持WebSocket通信
- 基于CompletableFuture实现异步操作
核心组件:
HttpClient:HTTP客户端,负责发送请求和处理响应HttpRequest:HTTP请求,包含请求URI、方法、头部等HttpResponse:HTTP响应,包含状态码、头部、响应体等BodyHandlers:响应体处理器,用于处理不同类型的响应体
代码示例:
// 创建HTTP客户端
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 使用HTTP/2
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
// 创建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/json"))
.header("User-Agent", "Java11-HttpClient")
.GET() // GET请求
.build();
// 发送同步请求
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println("状态码: " + response.statusCode());
System.out.println("响应头: " + response.headers().map());
System.out.println("响应体: " + response.body().substring(0, 100) + "...");
// 发送异步请求
CompletableFuture<HttpResponse<String>> futureResponse =
client.sendAsync(request, BodyHandlers.ofString());
futureResponse.thenApply(HttpResponse::body)
.thenAccept(body -> System.out.println("异步响应: " + body.substring(0, 50) + "..."))
.join(); // 等待异步操作完成
性能优势:
- HTTP/2支持多路复用,减少连接开销
- 异步API允许高并发请求处理
- 连接池管理优化,减少TCP连接建立时间
- 内存使用更高效,减少GC压力
- 比旧版HttpURLConnection快3-5倍
适用场景:
- REST API调用
- 微服务间通信
- WebSocket应用
- 高并发HTTP客户端
2.2 局部变量类型推断
特性说明:
var关键字允许编译器根据初始化值推断局部变量类型- 仅适用于局部变量,不支持字段、方法参数或返回类型
- 保持了静态类型检查的安全性
- 支持在循环、try-with-resources等多种场景使用
使用规则:
- 必须有初始化值
- 不能用于null初始化
- 不能用于lambda表达式
- 不能用于数组初始化
代码示例:
// 传统方式 - 显式类型声明
String traditionalString = "Hello World";
List<String> traditionalList = List.of("A", "B", "C");
Map<String, Integer> traditionalMap = Map.of("A", 1, "B", 2);
// 使用var - 编译器推断类型
var inferredString = "Hello World"; // 推断为String
var inferredList = List.of("A", "B", "C"); // 推断为List<String>
var inferredMap = Map.of("A", 1, "B", 2); // 推断为Map<String, Integer>
var inferredNumber = 42; // 推断为int
System.out.println("var字符串: " + inferredString);
System.out.println("var列表: " + inferredList);
System.out.println("var映射: " + inferredMap);
System.out.println("var数字: " + inferredNumber);
// 在循环中使用var
for (var item : inferredList) {
System.out.println("循环项: " + item);
}
// 在try-with-resources中使用var
try (var input = JDK11Features.class.getResourceAsStream("/test.txt")) {
if (input != null) {
var content = new String(input.readAllBytes());
System.out.println("文件内容: " + content);
}
} catch (IOException e) {
System.out.println("文件读取错误");
}
// 注意事项:var不能用于以下情况
// var nullVar = null; // 错误:无法推断类型
// var lambda = (x, y) -> x + y; // 错误:lambda表达式需要显式类型
// var array = {1, 2, 3}; // 错误:数组初始化需要显式类型
性能优势:
- 编译时类型推断,运行时无性能开销
- 减少了冗余代码,提高了可读性
- 字节码与显式类型声明完全相同,执行效率一致
- 避免了类型名称过长带来的代码冗余
适用场景:
- 类型名称较长的变量
- 复杂的泛型类型
- 临时变量
- 提高代码可读性的场景
2.3 新的字符串方法
特性说明:
isBlank()- 检查字符串是否为空或仅包含空白字符strip()/stripLeading()/stripTrailing()- 去除前后空白,比trim()更Unicode友好repeat(int)- 重复字符串指定次数lines()- 将多行字符串转换为Stream
代码示例:
var text = " Hello Java 11 ";
var multilineText = "第一行\n第二行\n第三行";
// isBlank() - 检查字符串是否为空或仅包含空白字符
System.out.println("isBlank检查: '" + text + "' -> " + text.isBlank()); // 输出: false
System.out.println("isBlank检查: ' ' -> " + " ".isBlank()); // 输出: true
// strip() - 去除前后空白(比trim()更Unicode友好)
System.out.println("strip结果: '" + text.strip() + "'"); // 输出: 'Hello Java 11'
System.out.println("stripLeading: '" + text.stripLeading() + "'"); // 输出: 'Hello Java 11 '
System.out.println("stripTrailing: '" + text.stripTrailing() + "'"); // 输出: ' Hello Java 11'
// repeat() - 重复字符串
System.out.println("repeat: '" + "Java".repeat(3) + "'"); // 输出: 'JavaJavaJava'
// lines() - 将多行文本转换为Stream
System.out.println("lines转换:");
multilineText.lines()
.map(line -> "行: " + line)
.forEach(System.out::println);
// 输出:
// 行: 第一行
// 行: 第二行
// 行: 第三行
性能优势:
- 这些方法是内置的,比自定义实现更高效
- 底层使用优化的算法,减少了内存拷贝
lines()方法直接返回Stream,避免了中间数组创建strip()方法支持Unicode空白字符,比trim()更全面且性能相当
适用场景:
- 字符串空白检查
- 字符串清理和格式化
- 重复字符串生成
- 多行文本处理
2.4 新的文件方法
特性说明:
Files.writeString()- 直接将字符串写入文件,无需转换为字节数组Files.readString()- 直接从文件读取字符串,无需手动处理字符编码- 支持标准字符编码和自定义编码
代码示例:
// 创建临时文件
Path tempFile = Files.createTempFile("jdk11", ".txt");
// 写入文件 - Files.writeString:JDK 11新增,直接写入字符串
String content = "这是使用JDK 11新API写入的内容\n第二行内容";
Files.writeString(tempFile, content);
// 读取文件 - Files.readString:JDK 11新增,直接读取字符串
String readContent = Files.readString(tempFile);
System.out.println("读取的文件内容:\n" + readContent);
// 清理临时文件
Files.deleteIfExists(tempFile);
性能优势:
- 减少了不必要的字节数组转换,节省内存
- 底层使用优化的I/O操作,减少了系统调用次数
- 支持大文件高效处理
- 编码处理更高效,避免了额外的字符集转换开销
适用场景:
- 文本文件读写
- 配置文件处理
- 日志文件操作
- 字符串内容的文件存储
2.5 ZGC(Z Garbage Collector)
特性说明:
- 低延迟设计:暂停时间不超过10ms
- 大内存支持:可处理TB级堆内存
- 并发设计:几乎所有GC操作都与应用线程并发执行
- 颜色指针技术:使用64位地址空间中的高16位存储标记信息
- 读屏障技术:实现并发标记和重定位
- 动态堆大小调整:根据应用需求自动调整堆大小
代码示例:
// ZGC主要是JVM层面的改进,不需要代码更改
// 但可以通过以下方式验证ZGC是否启用
String gcName = System.getProperty("java.vm.gc");
System.out.println("当前GC: " + gcName);
// 获取JVM名称和版本信息
String vmName = System.getProperty("java.vm.name");
String vmVersion = System.getProperty("java.vm.version");
System.out.println("JVM名称: " + vmName);
System.out.println("JVM版本: " + vmVersion);
// 获取垃圾收集器信息
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("GC名称: " + gcBean.getName());
System.out.println("GC算法: " + gcBean.getObjectName());
}
// 创建大量对象来观察GC行为
List<byte[]> memoryChunks = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
// 分配1MB内存
memoryChunks.add(new byte[1024 * 1024]);
if (i % 100 == 0) {
System.out.println("已分配: " + i + " MB");
}
}
启用方式:
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC YourApplication
性能优势:
- 暂停时间稳定在亚毫秒级别,不受堆大小影响
- 吞吐量损失通常不超过15%(相比G1)
- 支持从几百MB到TB级的堆大小
- 适合对延迟敏感的应用,如金融交易系统、在线游戏等
- 减少了应用程序的响应时间波动
适用场景:
- 大内存应用(GB到TB级)
- 低延迟要求的应用
- 金融、游戏、实时数据处理等对响应时间敏感的场景
2.6 其他重要特性
2.6.1 Epsilon GC
- 无操作垃圾收集器,用于性能测试和特殊场景
- 不执行任何垃圾回收操作,内存使用达到上限时直接退出
- 适合性能基准测试,减少GC对测试结果的影响
2.6.2 Flight Recorder
- 低开销性能监控工具,用于问题诊断和性能分析
- 可以收集JVM和应用程序的详细信息,如内存使用、线程活动、锁竞争等
- 开销极低(通常低于1%),可以在生产环境中使用
2.6.3 单文件程序
- 支持直接运行.java文件,无需编译
- 使用
java命令直接执行源代码文件 - 简化了小型程序的开发和运行流程
3. 性能提升总结
| 特性 | 性能提升 | 适用场景 |
|---|---|---|
| HTTP Client API | 比旧版HttpURLConnection快3-5倍,支持HTTP/2多路复用 | REST API调用、微服务通信 |
| 局部变量类型推断 | 编译时类型推断,运行时无性能开销,减少代码冗余 | 提高代码可读性的场景 |
| 新的字符串方法 | 内置优化算法,减少内存拷贝,支持Unicode | 字符串处理、文本操作 |
| 新的文件方法 | 减少字节数组转换,优化I/O操作 | 文本文件读写、配置处理 |
| ZGC | 暂停时间稳定在亚毫秒级别,支持TB级堆内存 | 大内存、低延迟应用 |
| Epsilon GC | 无GC操作,适合性能测试 | 性能基准测试 |
| Flight Recorder | 低开销性能监控,低于1%的性能影响 | 生产环境监控、问题诊断 |
4. 最佳实践
4.1 HTTP Client API使用建议
- 优先使用异步API处理并发请求
- 合理设置连接超时和读取超时
- 使用HTTP/2获得更好的性能
- 重用HttpClient实例以提高性能
4.2 局部变量类型推断使用建议
- 保持代码可读性,不要过度使用var
- 对于复杂类型或长类型名称,优先使用var
- 避免在同一作用域中使用var声明不同类型的变量
- 确保初始化值能明确表达变量类型
4.3 字符串方法使用建议
- 对于需要处理Unicode空白字符的场景,使用strip()替代trim()
- 对于多行文本处理,使用lines()方法获得Stream
- 合理使用repeat()方法生成重复字符串
- 使用isBlank()检查空白字符串
4.4 文件方法使用建议
- 对于文本文件,优先使用Files.writeString()和Files.readString()
- 对于大文件,考虑使用流式处理
- 注意异常处理和资源清理
- 合理设置字符编码
4.5 ZGC使用建议
- 对于大内存应用(8GB以上),考虑使用ZGC
- 对于对延迟敏感的应用,启用ZGC
- 监控ZGC的性能表现,根据实际情况调整堆大小
- 注意ZGC对64位系统的要求
5. 代码示例
5.1 完整示例类
package com.java.learning;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpResponse.BodyHandlers;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class JDK11Features {
public static void main(String[] args) throws Exception {
demonstrateHttpClient();
demonstrateLocalVariableTypeInference();
demonstrateStringMethods();
demonstrateFileMethods();
ZGCDemo.demonstrateZGC();
}
public static void demonstrateHttpClient() throws Exception {
System.out.println("=== HTTP Client API示例 ===");
// 创建HTTP客户端
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
// 创建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/json"))
.header("User-Agent", "Java11-HttpClient")
.GET()
.build();
// 发送同步请求
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println("状态码: " + response.statusCode());
System.out.println("响应头: " + response.headers().map());
System.out.println("响应体: " + response.body().substring(0, 100) + "...");
// 发送异步请求
CompletableFuture<HttpResponse<String>> futureResponse =
client.sendAsync(request, BodyHandlers.ofString());
futureResponse.thenApply(HttpResponse::body)
.thenAccept(body -> System.out.println("异步响应: " + body.substring(0, 50) + "..."))
.join(); // 等待异步操作完成
}
public static void demonstrateLocalVariableTypeInference() {
System.out.println("\n=== 局部变量类型推断 (var) 示例 ===");
// 传统方式 - 显式类型声明
String traditionalString = "Hello World";
List<String> traditionalList = List.of("A", "B", "C");
Map<String, Integer> traditionalMap = Map.of("A", 1, "B", 2);
// 使用var - 编译器推断类型
var inferredString = "Hello World"; // 推断为String
var inferredList = List.of("A", "B", "C"); // 推断为List<String>
var inferredMap = Map.of("A", 1, "B", 2); // 推断为Map<String, Integer>
var inferredNumber = 42; // 推断为int
System.out.println("var字符串: " + inferredString);
System.out.println("var列表: " + inferredList);
System.out.println("var映射: " + inferredMap);
System.out.println("var数字: " + inferredNumber);
// 在循环中使用var
for (var item : inferredList) {
System.out.println("循环项: " + item);
}
// 在try-with-resources中使用var
try (var input = JDK11Features.class.getResourceAsStream("/test.txt")) {
if (input != null) {
var content = new String(input.readAllBytes());
System.out.println("文件内容: " + content);
}
} catch (IOException e) {
System.out.println("文件读取错误");
}
// 注意事项:var不能用于以下情况
// var nullVar = null; // 错误:无法推断类型
// var lambda = (x, y) -> x + y; // 错误:lambda表达式需要显式类型
// var array = {1, 2, 3}; // 错误:数组初始化需要显式类型
}
public static void demonstrateStringMethods() {
System.out.println("\n=== 新的字符串方法示例 ===");
var text = " Hello Java 11 ";
var multilineText = "第一行\n第二行\n第三行";
// isBlank() - 检查字符串是否为空或仅包含空白字符
System.out.println("isBlank检查: '" + text + "' -> " + text.isBlank());
System.out.println("isBlank检查: ' ' -> " + " ".isBlank());
// strip() - 去除前后空白(比trim()更Unicode友好)
System.out.println("strip结果: '" + text.strip() + "'");
System.out.println("stripLeading: '" + text.stripLeading() + "'");
System.out.println("stripTrailing: '" + text.stripTrailing() + "'");
// repeat() - 重复字符串
System.out.println("repeat: '" + "Java".repeat(3) + "'");
// lines() - 将多行文本转换为Stream
System.out.println("lines转换:");
multilineText.lines()
.map(line -> "行: " + line)
.forEach(System.out::println);
}
public static void demonstrateFileMethods() throws IOException {
System.out.println("\n=== 新的文件方法示例 ===");
// 创建临时文件
Path tempFile = Files.createTempFile("jdk11", ".txt");
// 写入文件 - Files.writeString:JDK 11新增,直接写入字符串
String content = "这是使用JDK 11新API写入的内容\n第二行内容";
Files.writeString(tempFile, content);
// 读取文件 - Files.readString:JDK 11新增,直接读取字符串
String readContent = Files.readString(tempFile);
System.out.println("读取的文件内容:\n" + readContent);
// 清理临时文件
Files.deleteIfExists(tempFile);
}
}
// ZGC演示(需要JVM参数:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC)
class ZGCDemo {
public static void demonstrateZGC() {
// ZGC主要是JVM层面的改进,不需要代码更改
// 但可以通过以下方式验证ZGC是否启用
String gcName = System.getProperty("java.vm.gc");
System.out.println("当前GC: " + gcName);
// 获取JVM名称和版本信息
String vmName = System.getProperty("java.vm.name");
String vmVersion = System.getProperty("java.vm.version");
System.out.println("JVM名称: " + vmName);
System.out.println("JVM版本: " + vmVersion);
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("GC名称: " + gcBean.getName());
System.out.println("GC算法: " + gcBean.getObjectName());
}
// 创建大量对象来观察GC行为
List<byte[]> memoryChunks = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
// 分配1MB内存
memoryChunks.add(new byte[1024 * 1024]);
if (i % 100 == 0) {
System.out.println("已分配: " + i + " MB");
}
}
}
}
6. 总结
JDK 11作为长期支持(LTS)版本,引入了多项重要特性和性能改进,为Java开发者带来了更好的开发体验和性能表现。从HTTP Client API到ZGC垃圾收集器,从局部变量类型推断到新的字符串和文件方法,这些特性不仅使代码更简洁、更易读,还显著提升了应用程序的性能。
通过合理使用JDK 11的新特性,开发者可以:
- 编写更现代、更简洁的代码
- 利用HTTP/2和异步API提高网络性能
- 处理大内存应用时获得更低的延迟
- 简化文件和字符串操作
- 使用内置工具进行性能监控和问题诊断
JDK 11的发布标志着Java平台的持续演进,为Java在现代应用开发中保持竞争力奠定了基础。对于企业应用和长期项目,升级到JDK 11可以获得更好的性能、更丰富的功能和更长的支持周期,是一个值得考虑的选择。