JAVA JDK 11 特性详解

9 阅读12分钟

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可以获得更好的性能、更丰富的功能和更长的支持周期,是一个值得考虑的选择。