油墨香^_^头像
关注

再见 Java 8,Java 17 来了!2万字详解升级指南与新特性盛宴

前言

2021年9月,Java 17 正式发布,作为继 Java 11 之后的又一个长期支持(LTS)版本,它带来了无数令人兴奋的新特性、性能改进和安全增强。对于仍停留在 Java 8 的开发者而言,是时候挥手告别这个服役近十年的经典版本,拥抱 Java 17 的现代 Java 开发体验了。本文将用 2 万字的篇幅,全方位解析从 Java 8 到 Java 17 的演进之路,涵盖语言特性、API 更新、JVM 优化、迁移实战等核心内容,助你平滑升级,领略 Java 新纪元的魅力。


第一部分:为什么必须升级到 Java 17?

1.1 Java 8 的辉煌与局限

Java 8 发布于 2014 年,引入了 Lambda 表达式、Stream API、新的日期时间 API 等革命性特性,极大地提升了开发效率。然而,随着时间的推移,Java 8 的局限性日益凸显:

  • 安全漏洞:官方已于 2019 年 1 月停止对 Java 8 的免费商用更新(Oracle JDK),社区版 OpenJDK 8 虽然仍在维护,但安全补丁滞后。

  • 性能瓶颈:缺乏现代 JVM 优化,如 GraalVM、ZGC 等新一代垃圾回收器。

  • 语言表达能力不足:缺少模式匹配、密封类、文本块等语法糖,代码冗长。

  • 生态落后:众多新框架和工具已全面转向 Java 11+,如 Spring Boot 2.7+ 要求 Java 17。

1.2 Java 17 的核心优势

  • 长期支持:Oracle 承诺为 Java 17 提供至少到 2029 年的安全更新,稳定性有保障。

  • 语法现代化:var、switch 表达式、文本块、记录类、密封类等大幅提升代码简洁性。

  • 性能飞跃:ZGC 和 Shenandoah GC 实现亚毫秒级停顿,G1 改进,JIT 编译优化。

  • 安全增强:默认禁用不安全的 TLS 1.0/1.1,改进加密算法,加强模块化封装。

  • 云原生就绪:更好的容器支持(CPU 和内存限制识别)、GraalVM 原生镜像支持。


第二部分:Java 9~17 主要特性全景图

从 Java 9 到 Java 17,每个版本都带来了一系列新特性。我们按版本梳理核心变化,重点关注对开发者的直接影响。

2.1 Java 9:模块化系统(Project Jigsaw)

2.1.1 模块化简介

Java 9 最大的变革是引入模块系统(JPMS),将 JDK 本身模块化,开发者也可以创建模块化应用。模块通过 module-info.java 描述依赖和导出包,解决类路径混乱和封装性问题。

java

module com.example.myapp {
    requires java.sql;
    exports com.example.myapp.api;
}
2.1.2 其他重要特性
  • JShell 交互式编程工具:快速测试 Java 代码片段。

  • 集合工厂方法:List.of(), Set.of(), Map.of() 创建不可变集合。

  • 接口私有方法:接口中允许定义私有方法,实现代码复用。

  • 改进的 Stream API:takeWhile、dropWhile、ofNullable。

  • HTTP/2 Client(孵化):新的 HTTP 客户端 API,支持 HTTP/2 和 WebSocket。

2.2 Java 10:局部变量类型推断(var)

引入 var 关键字,允许编译器根据上下文推断局部变量类型,减少样板代码:

java

// Java 8
List<String> list = new ArrayList<>();

// Java 10+
var list = new ArrayList<String>();

注意:var 仅适用于局部变量(包括 for 循环索引),不能用于字段、方法参数等。

2.3 Java 11:LTS 版本,多项实用更新

2.3.1 标准 HTTP Client

Java 9 孵化的 HTTP Client 在 Java 11 正式成为标准 API,支持同步/异步请求、HTTP/2、WebSocket:

java

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com"))
        .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
2.3.2 字符串新方法
  • isBlank(): 判断字符串是否为空白(包括空、空格、制表符)。

  • lines(): 按行分割为 Stream。

  • strip(), stripLeading(), stripTrailing(): 去除空白(比 trim() 更全面)。

  • repeat(int): 重复字符串。

2.3.3 文件读写简化

Files.writeString() 和 Files.readString() 便捷读写小文件:

java

Files.writeString(Path.of("test.txt"), "Hello Java 11");
String content = Files.readString(Path.of("test.txt"));
2.3.4 其他亮点
  • 局部变量类型推断用于 Lambda 参数:(var s1, var s2) -> s1 + s2。

  • Collection.toArray(IntFunction):list.toArray(String[]::new)。

  • TLS 1.3 默认支持。

  • 飞行记录器(Flight Recorder)开源,可用于性能分析。

2.4 Java 12:Switch 表达式预览与改进

2.4.1 Switch 表达式(预览)

允许使用箭头语法和返回值,避免 break:

java

// 旧语法
String result;
switch (day) {
    case MONDAY:
    case FRIDAY:
        result = "Work";
        break;
    default:
        result = "Other";
}

// 新语法(Java 12 预览,14 正式)
String result = switch (day) {
    case MONDAY, FRIDAY -> "Work";
    default -> "Other";
};
2.4.2 其他改进
  • Collectors.teeing(): 合并两个 Collector 的结果。

  • 紧凑的数字格式化:NumberFormat.getCompactNumberInstance()。

2.5 Java 13:文本块预览

文本块(Text Blocks)简化多行字符串编写(预览),避免转义:

java

// 旧方式
String json = "{\n" +
              "  \"name\": \"Java\",\n" +
              "  \"version\": 17\n" +
              "}";

// 文本块(Java 13 预览,15 正式)
String json = """
    {
      "name": "Java",
      "version": 17
    }
    """;

2.6 Java 14:Records、Pattern Matching 预览

2.6.1 Records(预览)

Record 是一种透明地持有数据的类,自动生成构造器、equals、hashCode、toString:

java

public record Point(int x, int y) {}

使用:var p = new Point(3, 4);

2.6.2 Pattern Matching for instanceof(预览)

简化类型检查和转换:

java

// 旧方式
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// 新方式(Java 14 预览,16 正式)
if (obj instanceof String s) {
    System.out.println(s.length());
}
2.6.3 其他
  • 友好的 NullPointerException 提示:明确指出哪个变量为 null。

  • Switch 表达式正式版(Java 12 预览,14 正式)。

  • 更好的 @Deprecated 注解。

2.7 Java 15:密封类预览、EdDSA 算法

2.7.1 密封类(Sealed Classes,预览)

限制哪些类或接口可以继承/实现,增强模式匹配的完整性:

java

public sealed class Shape permits Circle, Rectangle, Triangle { ... }
final class Circle extends Shape { ... }
non-sealed class Rectangle extends Shape { ... }
2.7.2 其他
  • 文本块正式版。

  • ZGC 和 Shenandoah 成为生产特性。

  • 隐式类(预览):允许 main 方法直接写在类外,简化小脚本。

2.8 Java 16:Records 正式版、模式匹配正式版

2.8.1 Records 正式版

同时引入 Record 的局部变量类型推断、嵌套记录等。

2.8.2 Pattern Matching for instanceof 正式版

增强代码可读性。

2.8.3 其他
  • Stream.toList() 便捷方法,直接返回不可变列表。

  • Stream.mapMulti() 用于替代 flatMap 的场景。

  • Unix-domain socket 支持。

  • 向量 API(孵化)。

2.9 Java 17:LTS 版本,集大成者

2.9.1 密封类正式版

密封类在 Java 17 最终定型,增强了领域建模能力。

2.9.2 增强的伪随机数生成器

新的接口和实现,方便替换不同的随机算法。

2.9.3 新增 java.time 解析格式

如 DateTimeFormatter 支持解析本地化格式。

2.9.4 弃用和移除
  • 移除 RMI Activation 机制。

  • 弃用 Applet API(准备移除)。

  • 弃用 Security Manager(准备移除)。

2.9.5 性能与 JVM
  • ZGC 改进:支持并发堆栈处理,减少停顿。

  • macOS/AArch64 支持:Apple Silicon 原生支持。

  • 增强的伪随机数生成器:新的 RandomGenerator 接口,更方便地使用各种算法。

  • 外部函数和内存 API(孵化):安全高效地调用本地代码和访问堆外内存。


第三部分:语言特性深度解析

本节深入探讨从 Java 8 到 Java 17 中最重要的语言变化,并配以代码示例。

3.1 Lambda 表达式与 Stream API 的演进

Java 8 引入 Lambda 和 Stream,后续版本持续增强。

3.1.1 Stream 新增方法
  • takeWhile / dropWhile (Java 9)
    按条件截取流元素,有序流下表现不同。

    java

    var list = List.of(1, 2, 3, 4, 5, 6);
    var taken = list.stream().takeWhile(n -> n < 4).toList(); // [1,2,3]
    var dropped = list.stream().dropWhile(n -> n < 4).toList(); // [4,5,6]
  • ofNullable (Java 9)
    创建包含单个元素的流,元素为 null 则返回空流。

  • iterate 重载 (Java 9)
    支持带终止条件的迭代:Stream.iterate(seed, hasNext, next)

  • toList (Java 16)
    直接收集为不可变 List,比 collect(Collectors.toList()) 更简洁。

3.1.2 Optional 增强
  • orElseThrow() (Java 10):无参版本,与 get() 类似但语义更明确。

  • ifPresentOrElse() (Java 9):提供值时的消费和空值时的运行操作。

  • or() (Java 9):如果值存在返回当前 Optional,否则返回提供的 Optional。

3.2 局部变量类型推断(var)

var 并非动态类型,而是由编译器推断静态类型。适用于:

  • 局部变量初始化

  • 增强 for 循环中的索引

  • 传统 for 循环中声明局部变量

  • Lambda 参数(需与类型注解结合)

java

var list = new ArrayList<String>();  // 推断为 ArrayList<String>
for (var item : list) { ... }        // item 为 String
var path = Path.of("file.txt");      // 推断为 Path

// Lambda 中使用 var(Java 11)
BiFunction<Integer, Integer, Integer> func = (var a, var b) -> a + b;

注意事项:

  • 不能用于方法参数、返回类型、字段。

  • 必须显式初始化,不能赋值为 null。

  • 代码可读性优先,复杂类型建议显式声明。

3.3 Switch 表达式与模式匹配

3.3.1 Switch 表达式

从 Java 12 预览到 Java 14 正式,允许使用 -> 和 yield 返回值:

java

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    default -> {
        int len = day.toString().length();
        yield len;  // 使用 yield 返回值
    }
};
3.3.2 模式匹配 for instanceof

Java 16 正式,结合 && 或 || 使用:

java

if (obj instanceof String s && s.length() > 5) {
    System.out.println(s.toUpperCase());
}
3.3.3 模式匹配 for switch(预览)

Java 17 中仍在预览(后续版本可能正式),允许在 switch 中使用模式:

java

// 预览特性,需启用 --enable-preview
String formatted = switch (obj) {
    case Integer i -> String.format("int %d", i);
    case Long l    -> String.format("long %d", l);
    case Double d  -> String.format("double %f", d);
    case String s  -> String.format("String %s", s);
    default        -> obj.toString();
};

3.4 文本块

文本块简化多行字符串,自动处理缩进和转义:

java

String html = """
    <html>
        <body>
            <p>Hello, Java</p>
        </body>
    </html>
    """;
  • 三个双引号开始,换行后内容,结束双引号位置决定公共缩进。

  • 可以使用 \ 转义换行符,\s 表示空格。

  • 保留尾部空格可使用 \ 后加空格。

3.5 Records

Record 是一种透明的数据载体,极大简化 POJO 定义。

java

public record Person(String name, int age) { }

编译器自动生成:

  • 所有字段的 private final

  • 全参构造器

  • equals/hashCode/toString

  • 访问器方法(name() 和 age())

可以添加额外方法、静态工厂、验证逻辑:

java

public record Person(String name, int age) {
    // 紧凑构造器,用于验证
    public Person {
        if (age < 0) throw new IllegalArgumentException("age must be positive");
    }
    
    public String greeting() {
        return "Hello, I'm " + name;
    }
}

3.6 密封类

密封类用于限定子类型,常用于代数数据类型(ADT)建模。

java

public sealed interface Expr permits Constant, Add, Multiply { }

record Constant(int value) implements Expr { }
record Add(Expr left, Expr right) implements Expr { }
record Multiply(Expr left, Expr right) implements Expr { }

编译器知道所有可能的子类,可用于穷举检查:

java

public int evaluate(Expr expr) {
    return switch (expr) {
        case Constant c -> c.value();
        case Add a      -> evaluate(a.left()) + evaluate(a.right());
        case Multiply m -> evaluate(m.left()) * evaluate(m.right());
    };
}

3.7 其他语法糖

  • 接口私有方法 (Java 9):在接口 default 方法间共享代码。

  • 钻石操作符增强 (Java 9):匿名类中允许使用 <> 推断类型。

  • _ 作为未使用变量 (Java 9 之后废弃):Java 9 开始下划线不再是合法标识符,保留用于未来 lambda 参数未使用场景。


第四部分:API 与库的演进

4.1 集合框架增强

4.1.1 不可变集合工厂 (Java 9)

java

List<String> list = List.of("a", "b", "c");
Set<String> set = Set.of("a", "b");
Map<String, Integer> map = Map.of("key1", 1, "key2", 2);
Map<String, Integer> mapEntries = Map.ofEntries(
    Map.entry("key1", 1),
    Map.entry("key2", 2)
);

这些集合不可变,不能添加或修改元素,也不能包含 null。

4.1.2 复制与复制不可变

List.copyOf, Set.copyOf, Map.copyOf 返回输入的不可变副本,如果输入已是不可变集合,直接返回自身。

java

var original = new ArrayList<String>();
var copy = List.copyOf(original); // 不可变

4.2 全新的 HTTP Client

取代旧的 HttpURLConnection,支持 HTTP/2 和 WebSocket。

同步 GET 请求:

java

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.github.com"))
        .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

异步 POST 请求:

java

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/post"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"java\"}"))
        .build();

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);

4.3 字符串实用方法

Java 11 为 String 增加的方法非常实用:

  • isBlank(): 判断是否空白(含空字符串、仅空白字符)。

  • lines(): 将字符串按行分割为 Stream<String>。

  • strip(), stripLeading(), stripTrailing(): 去除空白(识别 Unicode 空白)。

  • repeat(int): 重复字符串指定次数。

  • indent(int): 调整缩进(Java 12)。

示例:

java

String str = "  Hello\n World  ";
str.lines().map(String::strip).forEach(System.out::println);
// 输出: Hello 和 World

"*".repeat(5); // *****

4.4 文件 I/O 便捷方法

Java 11 添加了简单的读写方法:

java

// 写入字符串到文件
Files.writeString(Path.of("test.txt"), "Hello Java 17");

// 读取全部内容为字符串
String content = Files.readString(Path.of("test.txt"));

// 读取所有行
List<String> lines = Files.readAllLines(Path.of("test.txt"));

Java 12 引入 Files.mismatch(Path, Path) 比较两个文件内容,返回第一个不匹配字节的位置。

4.5 日期时间 API 增强

Java 8 已经引入了全新的 java.time API,后续版本持续增强。

  • DateTimeFormatter 本地化格式 (Java 9):DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).localizedBy(locale)

  • Period 和 Duration 支持 between (Java 9):Period.between(LocalDate, LocalDate)

  • LocalDate.datesUntil (Java 9):生成日期流。

  • Instant.now(Clock) 等增加时钟支持。

  • DayOfWeek 和 Month 枚举增强 (Java 9):DayOfWeek.values() 等。

  • DateTimeFormatter 解析 case-insensitive (Java 17):使用 parseCaseInsensitive()。

4.6 新的垃圾回收器与 JVM 工具

  • ZGC (Java 11 引入,15 生产):可扩展低延迟垃圾回收器,停顿时间不超过 10ms,堆大小从几百 MB 到 TB 级。

  • Shenandoah GC (Java 12 引入,15 生产):与 ZGC 类似,目标是低停顿。

  • Epsilon GC (Java 11):无操作垃圾回收器,用于性能测试和短生命期任务。

  • GC 调优:G1 成为默认垃圾回收器(Java 9),后续不断优化。

  • JFR 事件流 (Java 14):可以实时订阅 JFR 事件,无需转储文件。

  • JVM 常量 API (Java 12):用于操作类文件常量池。

4.7 并发增强

  • CompletableFuture 增强:Java 9 增加了 orTimeout, completeOnTimeout, delayedExecutor 等方法;Java 12 增加 exceptionallyAsync 等。

  • Reactive Streams 兼容性:Java 9 引入了 Flow API,提供发布-订阅框架。

  • Thread.onSpinWait (Java 9):提示 CPU 当前线程正在忙等待,有助于性能。

  • StampedLock 改进 (Java 17?暂未发现重大变更,但 StampedLock 自 Java 8 存在)。

4.8 其他重要 API

  • java.util.HexFormat (Java 17):十六进制字符串与字节数组转换。

  • Objects.requireNonNullElse 和 requireNonNullElseGet (Java 9):更安全地处理 null 默认值。

  • Optional.orElseThrow() (Java 10):无参数版本。

  • Stream.toList() (Java 16):便捷收集不可变列表。

  • Collectors.teeing() (Java 12):同时进行两个收集并合并结果。


第五部分:模块化系统(Project Jigsaw)详解

模块化是 Java 9 引入的颠覆性改变,旨在解决类路径混乱、JAR 地狱、封装性差等问题。

5.1 模块基础概念

  • 模块:一组相关的包和资源的集合,通过 module-info.java 描述。

  • 模块路径:代替类路径,存放模块化 JAR。

  • 可访问性:包必须显式导出才能被其他模块访问。

  • 可读性:模块必须显式声明依赖(requires)才能读取其他模块的导出包。

5.2 编写模块化应用

创建 module-info.java 放在源码根目录:

java

module com.example.myapp {
    requires java.sql;                 // 依赖 java.sql 模块
    requires transitive java.logging;   // 传递依赖
    exports com.example.myapp.api;      // 导出 API 包
    exports com.example.myapp.internal to com.example.test; // 限定导出
    uses com.example.spi.MyService;     // 服务接口
    provides com.example.spi.MyService with com.example.impl.MyServiceImpl; // 提供服务实现
}

5.3 JDK 模块化

JDK 本身被拆分为约 80 个模块,例如 java.base 自动被所有模块依赖,包含核心类。开发者可以创建仅包含所需模块的运行时镜像(jlink),大幅减小应用体积。

5.4 迁移到模块化系统

对于现有 Java 8 应用,迁移到模块化可能是个挑战。通常有两种方式:

  1. 未命名模块:将传统 JAR 放在类路径上,它们属于未命名模块,可以访问所有类路径上的类型,但无法访问模块路径中的模块化 JAR 的非导出包。

  2. 模块化 JAR:逐步为每个模块添加 module-info.java,模块化 JAR 放在模块路径上。

迁移步骤建议:

  • 先确保应用在 Java 11 以上运行良好(使用类路径)。

  • 使用 jdeps 工具分析依赖,生成模块描述建议。

  • 逐步模块化,或仅将关键部分模块化。

  • 利用 --add-reads, --add-exports, --add-opens 等标志解决迁移中的访问问题。

5.5 封装 JDK 内部 API

Java 9 开始强烈封装 JDK 内部 API(如 sun.misc.Unsafe 的部分,com.sun.* 等),默认无法访问。若需使用,需使用 --add-exports 等参数。许多依赖内部 API 的库(如反射库、字节码框架)已适配。


第六部分:JVM 性能与监控进化

6.1 垃圾回收器

6.1.1 G1(Garbage First)成为默认

Java 9 中将 G1 设为默认垃圾回收器,替代了 Parallel GC。G1 是一个分区式收集器,支持大堆、可预测停顿,适合多核大内存环境。

6.1.2 ZGC(Z Garbage Collector)

Java 11 引入实验性 ZGC,Java 15 成为生产特性。特点:

  • 并发执行,停顿极短(<10ms)

  • 支持 8MB 到 16TB 的堆

  • 使用染色指针和读屏障技术

  • 启动参数:-XX:+UseZGC

6.1.3 Shenandoah GC

Java 12 引入实验性 Shenandoah,Java 15 成为生产特性。类似 ZGC,低停顿,与 ZGC 竞争。

  • 启动参数:-XX:+UseShenandoahGC

6.1.4 Epsilon GC(No-Op GC)

Java 11 引入,不做任何垃圾回收,适用于短暂运行的任务、性能测试。参数:-XX:+UseEpsilonGC。

6.1.5 GC 调优建议
  • 使用最新 LTS 版本的默认 GC(G1)通常足够好。

  • 对于延迟敏感应用,考虑 ZGC 或 Shenandoah。

  • 对于吞吐量优先(如批处理),可考虑 Parallel GC(-XX:+UseParallelGC)。

  • 监控 GC 日志,使用 jstat、VisualVM、JFR 等工具分析。

6.2 编译系统改进

6.2.1 分层编译(Tiered Compilation)

Java 7 引入,Java 8 默认开启。结合 C1(客户端编译器)和 C2(服务端编译器),快速启动并最终达到高性能。

6.2.2 提前编译(AOT,Java 9)

Java 9 实验性引入 jaotc 工具,可将 Java 类编译为本机代码,减少预热时间。但实际应用较少,GraalVM 提供了更完善的 AOT 方案。

6.2.3 GraalVM 与原生镜像

GraalVM 是一个高性能 JDK 发行版,支持将 Java 应用编译为原生可执行文件(Native Image),启动极快、内存占用低。Spring Native 和 Quarkus 等框架已支持。

6.3 监控与诊断工具增强

6.3.1 Java Flight Recorder(JFR)

Java 11 开源 JFR,可用于生产环境持续记录 JVM 事件(方法采样、GC、锁竞争等),对性能影响小。通过 jcmd 或 JFR API 控制。

6.3.2 Java Mission Control(JMC)

JMC 是 JFR 的可视化客户端,Java 11 后与 JDK 分开发布,需单独下载。

6.3.3 统一 JVM 日志(Java 9)

使用 -Xlog 统一配置所有 JVM 日志,替代以往的分散参数。例如:

bash

-Xlog:gc*:file=gc.log:time,uptime,level,tags
6.3.4 诊断命令增强
  • jcmd:功能强大的诊断工具,支持 JFR、VM 信息、线程 dump 等。

  • jhsdb:适用于 core dump 的调试。

6.4 容器环境适配

从 Java 10 开始,JVM 能够识别容器(如 Docker)的 CPU 和内存限制,自动调整堆大小和 GC 线程数。

  • Java 8u131+ 开始支持 -XX:+UseCGroupMemoryLimitForHeap 等选项,但需要手动配置。

  • Java 10+ 默认支持容器,自动识别 --cpus、--memory 等限制。

  • Java 15+ 进一步改进,支持 cgroup v2。

建议容器中运行 Java 应用时,设置 -XX:MaxRAMPercentage=75.0 等参数,让 JVM 根据容器内存自动计算堆大小。


第七部分:从 Java 8 迁移到 Java 17 实战指南

7.1 准备工作

  1. 确认依赖库兼容性:检查项目使用的第三方库是否支持 Java 17(至少 Java 11+)。重点关注:Spring、Hibernate、Netty、Guava、Jackson、Log4j 等。

  2. 搭建测试环境:准备好开发、测试、预发环境,安装 JDK 17。

  3. 代码仓库备份:确保可以回退。

7.2 迁移步骤

7.2.1 升级构建工具和插件
  • Maven:使用 3.6.3+,更新 maven-compiler-plugin 至 3.10.1+,设置 source/target 为 17。

    xml

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
  • Gradle:使用 7.3+,设置 sourceCompatibility = '17'。

  • 检查其他插件(如 surefire、jar、war)的兼容性。

7.2.2 处理编译警告和错误
  • 移除过时的 API,如 finalize() 方法、某些 SecurityManager 用法。

  • 更新反射访问:如果使用了深度反射访问内部 API,可能需要添加 --add-opens。

  • 处理废弃的包:如 java.xml.bind(JAXB)在 Java 9 被标记为废弃,Java 11 已移除,需添加依赖 javax.xml.bind:jaxb-api 或改用替代方案。

7.2.3 解决模块化相关问题(可选)

如果未完全模块化,应用可继续以传统方式运行(所有 JAR 在类路径上)。但如果希望利用模块化优势,或遇到某些包无法访问,可以:

  • 使用 jdeps 分析 JAR 依赖,生成模块描述建议。

  • 对于无法模块化的遗留 JAR,继续放在类路径。

  • 添加 --add-reads、--add-exports 等参数到启动脚本。

7.2.4 测试策略
  • 单元测试:确保所有测试通过,注意有些测试可能依赖于特定的 GC 行为或时间精度。

  • 集成测试:验证与外部系统(数据库、缓存、消息队列)的交互。

  • 性能测试:对比升级前后的吞吐量、延迟、GC 表现,调整 JVM 参数。

7.2.5 部署与监控
  • 逐步灰度发布,监控应用日志、指标。

  • 使用 JFR 收集性能数据,分析是否有异常。

  • 如果出现内存泄漏或性能下降,检查是否因为某些新特性(如 Records 的不可变性导致对象增多)引起。

7.3 常见迁移问题及解决方案

7.3.1 访问限制:无法访问某些内部 API

错误示例:cannot access class sun.misc.Unsafe。
解决方案:添加 JVM 参数 --add-exports java.base/sun.misc=ALL-UNNAMED(如果确实需要访问,但建议改用标准 API)。

7.3.2 包不存在:如 javax.xml.bind

Java 11 移除了 Java EE 模块,需手动添加依赖:

xml

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>

并可能需要实现(如 com.sun.xml.bind:jaxb-impl)。

7.3.3 反射操作警告

Java 16 开始默认强封装反射访问,非法反射会发出警告并可能在未来抛出异常。解决:升级库版本,或添加 --add-opens 授权。

7.3.4 默认字符集变化

Java 17 中 Charset.defaultCharset() 行为取决于环境,但一般在容器中稳定。若应用依赖特定字符集,建议显式指定。

7.3.5 语法不兼容
  • var 不能用于 Lambda 参数类型推断(Java 11+ 允许,但需注意)。

  • 如果使用了未命名模式变量 _,需移除或替换(Java 9+ 不允许 _ 作为标识符)。

7.3.6 构建工具兼容性
  • Maven 的 maven-compiler-plugin 需升级。

  • 若使用 Lombok,需升级到支持 Java 17 的版本(1.18.22+)。

  • 若使用 AspectJ,需升级到 1.9.7+。

7.4 逐步采用新特性

迁移完成后,可以在代码库中逐步使用 Java 17 新特性,提升代码质量和开发效率:

  1. 使用 var 简化局部变量声明,但注意保持可读性。

  2. 使用 record 定义数据传输对象,减少样板代码。

  3. 使用 text blocks 定义多行字符串(SQL、JSON、HTML)。

  4. 使用 switch 表达式替代复杂的 if-else 链。

  5. 使用 instanceof 模式匹配简化类型检查和转换。

  6. 使用 sealed 类设计领域模型。

  7. 使用新的 HttpClient 替换 HttpURLConnection 或 Apache HttpClient。

  8. 使用 Stream.toList() 简化集合收集。


第八部分:未来展望——Java 18 及以后

Java 发布周期已改为每半年一次小版本,每三年一次 LTS。Java 18 于 2022 年 3 月发布,引入了简单的 Web 服务器、UTF-8 作为默认字符集、代码片段等。后续版本将继续完善:

  • Project Loom:虚拟线程(已在 Java 19 预览),简化并发编程。

  • Project Panama:外部函数与内存 API(Java 17 孵化,后续正式),提升与本地代码交互的效率和安全性。

  • Pattern Matching for switch 正式化。

  • Record 模式匹配 等。

Java 17 作为 LTS,将是未来几年应用的主流版本,学习并掌握其新特性是 Java 开发者的必备技能。


总结

从 Java 8 到 Java 17,语言和平台经历了巨大的演进。模块化、新语法、API 增强、GC 改进、云原生支持等变化,让 Java 重新焕发活力。虽然迁移过程可能面临一些挑战,但带来的长期收益——更简洁的代码、更高的性能、更好的安全性——是值得投入的。

告别 Java 8,拥抱 Java 17,意味着踏上 Java 发展的快车道。希望本文能帮助你顺利完成升级,并在新的 Java 世界中游刃有余。


附录:Java 版本主要特性时间线

版本发布时间LTS主要特性
Java 82014.03✅Lambda, Stream, Date/Time API
Java 92017.09❌模块化, JShell, 集合工厂方法
Java 102018.03❌局部变量类型推断 (var)
Java 112018.09✅HTTP Client, 字符串方法, ZGC 实验
Java 122019.03❌Switch 表达式预览, 微基准套件
Java 132019.09❌文本块预览, Switch 表达式增强
Java 142020.03❌Records 预览, Pattern matching 预览
Java 152020.09❌密封类预览, 文本块正式, ZGC 生产
Java 162021.03❌Records 正式, Pattern matching 正式
Java 172021.09✅密封类正式, 伪随机数增强

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_41581588/article/details/159041393

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--