【转】Java 8 到 Java 24 新特性一览

2025-05-17  本文已影响0人  AlphaHinex

由于简书文章长度限制,截断了 Java 21 之后的内容。

文章说明

  1. 本文整理了 Java 8 至 Java 24 各版本的新特性,内容包括每个版本的新增功能分类(如语法增强、性能优化、工具支持等)、详细的代码示例,并结合官方文档资料,分析每项特性的应用场景及优缺点。
  2. Java 8 发布于 2014 年,此后 Java 进入快速迭代模式,每半年发布一个新版本。一路走来,Java 8 到 Java 24 带来了大量重要的新特性。
  3. 本文持续更新中…

Java 8 新特性

发行时间: 2014 年 3 月

语言特性

// 示例:使用 Lambda 表达式对列表进行过滤和映射
List<String> names = Arrays.asList("Alice", "Bob", "Ann");
List<String> filtered = names.stream()
                             .filter(s -> s.startsWith("A"))
                             .map(s -> s.toUpperCase())
                             .collect(Collectors.toList());
System.out.println(filtered); // 输出: [ALICE, ANN]
interface MyInterface {
    static void staticMethod() {
        System.out.println("接口静态方法");
    }
    default void defaultMethod() {
        System.out.println("接口默认方法");
    }
    void abstractMethod();
}
class MyClass implements MyInterface {
    @Override
    public void abstractMethod() { }
    // 未重写 defaultMethod(),将继承接口的默认实现
}
MyInterface.staticMethod();       // 调用接口静态方法
new MyClass().defaultMethod();    // 调用接口默认方法,实现类未重写则执行接口中的实现

应用场景: 默认方法解决了接口演化的问题,比如 Java 8 为 Collection 接口添加了 stream() 默认方法,从而所有实现类无需修改就自动拥有流操作能力。静态方法则方便在接口中组织工具函数。

@Schedule(day="Mon"), @Schedule(day="Tue")
void scheduledTask() { ... }

核心库新特性

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .filter(n -> n % 2 == 1)      // 筛选奇数
                 .mapToInt(n -> n * n)         // 平方映射
                 .sum();                       // 终止操作求和
System.out.println(sum); // 输出: 35(1^2 + 3^2 + 5^2)

应用场景: Stream API 大大简化了集合的复杂操作,避免了繁琐的迭代和临时集合管理。例如,可以轻松进行过滤、汇总统计、分组分区等操作。缺点是初学者需要学习 Lambda 表达式和流式思维,但一旦掌握,能编写出高可读性的流水线处理代码。

public Optional<User> findUser(String name) {
    User result = ... // 查找用户
    return Optional.ofNullable(result);
}
// 使用Optional避免显式null判断
findUser("Alice").ifPresent(user -> System.out.println(user.getId()));
User user = findUser("Bob").orElse(new User("Bob")); // 未找到则返回默认User

应用场景: Optional 常用于方法返回值,表示“可能没有结果”。这比返回 null 更具语义,也迫使调用方处理不存在的情况。需要注意不要滥用在域对象上,Optional设计主要用于返回值,而非序列化或字段类型。

LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1990, Month.APRIL, 1);
Period period = Period.between(birthday, today);
System.out.printf("年龄: %d 年 %d 月 %d 日%n", 
                  period.getYears(), period.getMonths(), period.getDays());

应用场景: 新日期API提供了清晰的方法来操作日期时间,支持时区、夏令时、安全不变。此外还提供了格式化和解析(DateTimeFormatter),大幅简化日期处理,避免了旧 API 各种不一致和缺陷。

JVM 与性能优化

工具和其他

总结: Java 8 是一个里程碑版本,引入的 Lambda 和 Stream 等特性使Java正式进入函数式编程时代。默认方法等特性改善了接口演进能力。核心库的加强和全新时间API填补了多年痛点。在性能上,永久代的移除和集合优化提升了稳定性。Java 8 的诸多新功能为之后的Java版本奠定了基础,也是目前许多项目仍在使用的版本。

Java 9 新特性

发行时间: 2017 年 9 月

语言和语法增强

// module-info.java 示例
module com.example.app {
    requires com.example.utils;    // 声明依赖模块
    exports com.example.app.api;   // 导出包供其他模块使用
}

模块系统解决了 classpath 下包命名冲突和封装不严的问题,实现强封装。只有模块导出的包才能被外部访问,未导出的内部实现包将被严格封装。这样提高了代码的可维护性和安全性。应用场景: 大型应用可以通过模块划分更好地管理依赖关系,同时在部署时使用 jlink 剔除无关模块减小体积。不过模块化也带来了学习曲线,且现有项目迁移需要调整访问限制(可以通过 --add-exports 等选项做兼容)。

// Java 8 及之前需要在 try 内新声明
try (BufferedReader br = Files.newBufferedReader(path)) { ... }
// Java 9 可以在 try 中直接使用已有的变量 br
BufferedReader br = Files.newBufferedReader(path);
try (br) {
    // 使用 br 读取...
}

这减少了不必要的代码臃肿,让语法更简洁。

核心库增强

Stream.of(1,2,3,4,5).takeWhile(n -> n < 4)  // 得到 [1,2,3]
Stream.of(1,2,3,4,5).dropWhile(n -> n < 4)  // 得到 [4,5]
Stream.iterate(1, x -> x+1).takeWhile(x -> x<=5).forEach(System.out::println);

此外,Optional 也增加了 ifPresentOrElse, or 等方法,提高 Optional 的易用性。这些改进让流操作和 Optional 更加完备,减少手动处理。

ProcessHandle self = ProcessHandle.current();
long pid = self.pid();
self.info().command().ifPresent(cmd -> System.out.println("命令: " + cmd));

这在需要监控或管理操作系统进程的应用中非常有用,如实现自定义的进程守护、获取子进程退出事件等。

JVM 和性能

工具与其他

jshell> int x = 5 * 20
x ==> 100
jshell> "hello, " + "jshell"
$2 ==> "hello, jshell"

JShell 极大地方便了试验性编程和学习。可以用它快速验证一段逻辑、探索API用法等,降低了写一个Hello World的门槛。对于教学、原型开发非常实用。

总结: Java 9 通过模块系统对Java平台进行了结构性革新。虽然社区对模块化褒贬不一,但不可否认它提高了代码封装和定制部署能力。除此之外,Java 9 在语法上小幅改进(私有接口方法、钻石操作符支持匿名类等未提及细节),在API和工具上为开发者提供了许多便捷,如JShell、集合工厂等。它为之后的版本铺平了道路,标志着Java进入了快速发布的新时代。

Java 10 新特性

发行时间: 2018 年 3 月

语言特性

var list = new ArrayList<String>();
var sum = 0;
for (var item : list) {
    // 在循环中,item 的类型也会被推断
}

在上述代码中,编译器会推断出 list 的类型为ArrayList<String>sum 的类型为 int注意: var 不是动态类型或弱类型,Java 类型系统仍是静态的,只是让编译器替我们填写类型。因此,var 只能用于有初始化的局部变量、for循环索引等,不可用于成员变量、方法参数,也不可将 null 赋给 var(无法推断类型)。另外,使用 var 可能降低代码可读性,应该在类型明显冗长的情况下使用,如复杂泛型类型。对于简单变量,直接写出类型可能更清晰。

应用场景var 可减少样板代码,特别是当类型本身冗长(如泛型嵌套)时。例如:

Map<String, List<Integer>> data = new HashMap<>();
// 使用 var 推断类型
var dataMap = new HashMap<String, List<Integer>>();

这在一定程度上让Java具有了脚本语言的简洁性,但依然保留了静态类型检查的安全性。

核心库增强

List<String> src = new ArrayList<>(List.of("a", "b"));
List<String> copy = List.copyOf(src);
src.add("c");
System.out.println(copy); // 输出 [a, b],copy 不受原列表修改影响
copy.add("d"); // UnsupportedOperationException,不可修改

应用场景: 当需要确保集合不被修改时,可以方便地获取其不可变版本,尤其在方法参数和返回值中传递集合时,使用 copyOf 能防止意外修改源集合。另外,Collectors 增加了 Collectors.toUnmodifiableList() 等方法,直接收集 Stream 元素为不可变集合。

性能和 JVM 改进

工具和平台

总结: 虽然 Java 10 属于非 LTS 的短期版本,但引入的 var 关键字极大地简化了日常编码。集合、Optional的小改进也增强了标准库的便利性。在性能方面,G1 并行Full GC、AppCDS 都是偏底层却意义重大的优化,让Java在大内存和大规模部署场景下表现更好。Java 10 还预示了未来的发展方向,如Graal编译器的引入为后续多语言支持铺路。作为承上启下的版本,Java 10 为后来Java 11的重大变化做好了准备。

Java 11 新特性

发行时间: 2018 年 9 月 25 日 (LTS长期支持版)

语言特性

Comparator<String> cmp = (var a, var b) -> Integer.compare(a.length(), b.length());

这对Lambda本身功能没有变化,但允许我们添加参数注解时更方便(因为只能用显式类型才能加注解)。总体来说,这一特性用途有限,仅在某些需要注解lambda参数的场景下提供了语法便利。

核心库增强

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

新客户端比旧的 HttpURLConnection 使用更简洁,功能更丰富(如内置 WebSocket 支持)。应用场景: 调用 REST 服务、执行HTTP请求等在企业开发中很常见,新API提高了开发效率,并发支持和HTTP/2多路复用也带来性能优势。

性能与内存

工具与其他

总结: 作为长期支持版本,Java 11 集大成,巩固了Java 9和10的变化并提供了一些关键的新功能。HttpClient的正式加入填补了长期以来标准库缺乏现代HTTP客户端的空白。一系列细小的API改进(字符串、文件、集合等)提升了日常开发体验。ZGC等革新的GC技术虽然仍是实验性质,但展现了Java在大内存低延迟领域的潜力。Java 11 开源了之前商用的JFR,统一了Oracle JDK和OpenJDK的差异,这对Java生态意义重大。可以说,Java 11 为随后版本的演进打下了稳定基础,也成为许多企业下一步升级的目标版本。

Java 12 新特性

发行时间: 2019 年 3 月

语言特性(预览)

// 新的switch表达式语法(Java 12+,需 --enable-preview)
int numDays = switch(day) {
    case MON, FRI, SUN -> 6;
    case TUE -> 7;
    case THU, SAT -> 8;
    case WED -> 9;
    // 对于多行的逻辑,可使用 yield 返回值
    default -> {
        System.out.println("Unknown day: " + day);
        yield 0;
    }
};

这种语法消除了忘写break导致贯穿的风险,每个分支的结果要么用->后的表达式,要么使用yield语句返回。Switch 表达式使得 switch 可用于内嵌在赋值或返回语句中,更加表达式化。 应用场景: 需要根据枚举或常量计算结果的场合会更简洁,例如将老式的 switch-case 结构转换为一行返回值的表达式。Java 12 此特性需通过--enable-preview启用,经过12、13的反馈,最终在Java 14成为正式特性。

if (obj instanceof String str) {
    // 进入此块则自动完成类型转换,可直接使用 str
    System.out.println(str.toUpperCase());
}

这样,无需再写 (String) obj 的强转和单独声明变量。这使代码更紧凑,避免了转换错误。应用场景: 广泛存在于对参数进行不同类型处理的时候。例如一个 Object 可以是多种类型,根据不同类型有不同逻辑,用传统 instanceof 需要繁琐的强转,有了模式匹配就简洁安全得多。Instanceof 模式匹配在Java 16转正。

核心库增强

NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, Style.SHORT);
System.out.println(fmt.format(1000));    // 输出 "1K"
System.out.println(fmt.format(1_000_000)); // 输出 "1M"

对需要友好展示统计数据的场合很实用,比如在界面上显示“2.3万”这样的格式(中文 Locale 会输出类似“2万3千”)。

JVM 与垃圾回收

工具和其他

总结: Java 12 属于过渡版本,但预示了Java语言几个重要演进方向:Switch表达式和模式匹配都是为了让Java语法更简洁、更强大(这些特性在后续版本陆续定型)。核心库的小改进(字符串indent/transform等)贴近开发者需求,让日常编码更高效。Shenandoah GC 等则体现了 Java 在 GC 领域的持续创新,和 ZGC 一起为低延迟场景提供了解决方案。虽然变化不如大版本明显,但Java 12为后来的Java 13、14继续迭代打下了基础。

Java 13 新特性

发行时间: 2019 年 9 月

语言特性(预览)

String json = "{\n" +
              "  \"name\": \"Alice\",\n" +
              "  \"age\": 25\n" +
              "}\n";

有了文本块,我们可以写成:

String json = """
              {
                  "name": "Alice",
                  "age": 25
              }
              """;

以上文本块会自动包含换行符和缩进空格,使字符串内容看起来与源码格式一致。Java 13 的文本块还引入了两个新的转义序列:\ 作为行结尾时表示忽略改行(连接行)、\s 表示空格。文本块显著提高了编写 JSON、HTML、正则、多行日志等字符串的可读性 (优点),避免繁琐的换行符拼接和转义。需要注意文本块会保留缩进,可以用 later Java 14+的 stripIndent()方法去除多余缩进。Text Blocks 在Java15转正成为正式特性。

String result = switch (day) {
    case MON, TUE, WED, THU, FRI:
        yield "Workday";
    case SAT, SUN:
        yield "Weekend";
    default:
        yield "Invalid";
};

在Java 12中,使用的是 -> 箭头和直接返回值或者break value形式;Java 13统一改用yield关键字提高一致性。这个版本的Switch表达式依旧是预览,直到Java 14才正式定型。

核心库与其他

工具和移除

总结: Java 13 延续了预览新特性的打磨,文本块让多行字符串处理在Java中首次变得愉悦;Switch表达式朝着最终定稿又迈进一步。虽然没有正式定稿的新语法,但这些预览特性在社区中引起了极大兴趣。底层方面,Socket API 的重构和ZGC的完善提高了性能和资源利用率。Java 13 的新特性数量相对不多,但“麻雀虽小五脏俱全”,为随后Java 14的大量新功能铺垫了环境。

Java 14 新特性

发行时间: 2020 年 3 月

JVM 改进

// 假设 a、b 可能为空
a.b.c.i = 99;

之前的异常信息只是 “NullPointerException at … 第5行”,现在则会提示类似 “Cannot read field ‘c’ because ‘a.b’ is null”。这个更详尽的消息让开发者立刻知道空指针的原因,大幅减少了排查时间 (优点)。该功能通过 JVM 参数 -XX:+ShowCodeDetailsInExceptionMessages 打开(在Java 14中默认关闭,在Java 15中成为默认开启)。

语言特性

String label = switch (day) {
    case "M", "W", "F" -> "MWF";
    case "T", "TH", "S" -> "TTS";
    default -> {
        if(day.isEmpty()) yield "Please insert a valid day.";
        else yield "Looks like a Sunday.";
    }
};
System.out.println(label);

在这个例子里,多个 case 可以用逗号合并(如 “M”, “W”, “F”),default 分支里演示了多语句块如何使用 yield 返回值。Switch 表达式相比传统 switch 简洁且更安全,避免了遗忘 break 的错误。它还能直接用作表达式赋值或返回,大大增强了 Java 表达能力。

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

编译器将为 Point 自动生成构造器、x()/y()访问器方法、equals()hashCode()toString()。所有字段默认是 private final,Record类自身被隐式声明为 final(不可子类化)。Record非常适合表示纯数据,如坐标点、范围、DTO等。它极大减少了冗余代码,提高了可读性,相当于Java内置了 Lombok 的 @Data 功能。应用场景: 用于值对象,特别是在API间传输数据的时候,使代码更简洁清晰。Record 在Java 16成为正式特性。

核心库和工具

安全和其他

总结: Java 14 是一个内容相当丰富的版本。Switch表达式终于定型、记录类模式匹配开始崭露头角。这些语言层面的增强让Java变得更简洁和富有表达力,逐步摆脱“样板代码多”的诟病。尽管Record和模式匹配仍是预览,但开发者已经可以尝鲜感受到它们的魅力。核心库方面,本版本改动不大,但像文本块等继续改进。JVM则在整理历史包袱(移除CMS)和推广新GC(ZGC跨平台)上迈进。总的来说,Java 14标志着Java语言在保持稳定性的同时,开始大胆引入新语法,为现代应用需求提供更优雅的解决方案。

Java 15 新特性

发行时间: 2020 年 9 月

核心语言特性

public sealed class Person permits Employee, Manager {
    // ...
}
public final class Employee extends Person { ... }
public non-sealed class Manager extends Person { ... }

在上例中,Person 这个密封类只允许有 EmployeeManager 两个子类。而这两个子类必须要么声明为 final 完全封闭继承(如Employee),要么继续声明为 sealed(列出下一层许可继承),或者声明为 non-sealed 表示开放继承(如Manager,不再受限制)。密封类的好处是可以严格限定继承层次,确保代码覆盖时知道所有可能的子类,有助于模式匹配等特性的穷尽性检查。应用场景: 枚举类型的扩展,当枚举不适用时,可以用密封类+模式匹配代替;或者框架定义接口只允许特定实现等。密封类提高了系统建模的精确性。它在Java 17正式成为语言特性。

核心库与安全

KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(data);
byte[] signature = sig.sign();

这将产生一个 Ed25519 签名。EdDSA 的加入让Java标准库紧跟加密领域前沿,为需要高性能签名(如区块链、JWT)的应用提供了新选择。

JVM 和性能

预览/非正式特性

总结: Java 15 虽然是短期版本,但特性相当丰富。文本块在这一版终于尘埃落定,使多行字符串处理不再繁琐。密封类的引入让Java的类型系统更加强大,配合记录类、pattern matching,Java正朝着模式匹配和代数数据类型的方向迈进。库方面小幅增强了集合、字符序列、密码学算法。ZGC成熟和Nashorn移除则反映了JVM内部技术迭代。对于开发者来说,Java 15 提供了更多工具来编写简洁、安全、高性能的代码,是一款值得关注的版本。

Java 16 新特性

发行时间: 2021 年 3 月

语言特性

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

编译器自动生成构造器、getter(以字段名为方法名)、equals/hashCodetoString 等。相比Java中的普通类,Record极大减少了样板代码,并且语义明确地表示这是一个值类型。值得注意的是,Java 16 对Record作了一些调整:允许内部类定义Record,并支持在接口中定义静态Record嵌套类等。应用场景: Record非常适合作为数据传输对象(DTO)、只读配置类等。例如在 Spring MVC 中,一个请求的响应可以直接用Record来建模,由于其自带toString/equals,打印日志和比较都方便许多。大量原本需写构造器和getter的类现在一行就搞定,开发效率和代码可读性大幅提升。

Object obj = ...;
if (obj instanceof Integer data) {
    System.out.println(data + 1); // 直接使用 data,无需强转
}

省去了典型的模板代码,提高了代码安全性(不易出错)和可读性。

核心库增强

FloatVector v1 = FloatVector.fromArray(FloatVector.SPECIES_256, arr1, 0);
FloatVector v2 = FloatVector.fromArray(FloatVector.SPECIES_256, arr2, 0);
FloatVector vSum = v1.add(v2);
vSum.intoArray(result, 0);

这利用CPU的SIMD指令实现了并行加法。虽然Vector API 对一般应用不是直接可见的变化,但对数值计算、高性能应用来说是个重要工具。它在Java 20已孵化到第5次。

工具与 JVM

总结: Java 16 是一个重要的过渡版本。它完成了若干预览特性的正化(Records, 模式匹配)和JDK内部重构(强封装),使Java语言和平台更加现代安全。记录类的加入改变了Java定义数据结构的习惯,大幅减少样板代码,受到开发者欢迎。与此同时,Java 16 在高性能计算领域布局:Vector API和外部内存API的孵化,为Java迈向系统编程领域铺平道路。对于一般开发者,Java 16的直接提升在于更简洁的代码和更一致的API,而对高级用户,则看到了Java在性能和底层操作上的野心。总的来说,Java 16继续平稳演进,为即将到来的下一个LTS版本(Java 17)做好了准备。

Java 17 新特性

发行时间: 2021 年 9 月 14 日 (LTS长期支持版)

语言特性

sealed interface Shape permits Circle, Rectangle, Square { }
final class Circle implements Shape { }
final class Rectangle implements Shape { }
final class Square implements Shape { }

以上接口只有这三种实现,未来也不可能出现第4种,实现了逻辑上的完备性。

static String formatShape(Shape s) {
    return switch(s) {
        case Circle c    -> "Circle radius=" + c.radius();
        case Rectangle r -> "Rectangle w=" + r.width();
        case Square sq   -> "Square side=" + sq.side();
        default          -> "Unknown shape";
    };
}

在这个预览特性中,switch 的 case 可以是带变量的类型(如 Circle c),匹配成功则自动将 s 转型为对应类型绑定到 c。这是模式匹配在 switch 上的首次尝试。对于密封类,编译器还能检查 default 分支是否必要(如上例如穷尽所有子类,可省略default)。Switch模式匹配让多态处理更加简洁强大,不过该特性在Java 17仅预览,最终于Java 19正式发布。

核心库

RandomGenerator rand = RandomGenerator.of("L128X256MixRandom");
int n = rand.nextInt();

这样获取的随机数在统计质量和速度上都会优于传统的 java.util.Random。此外统一的接口让代码更灵活,可以在不改变代码逻辑情况下切换不同生成器算法。

性能与内部

适配与生态

作为LTS版本,Java 17 得到了各大框架和库的快速支持。Spring Boot 3 就要求最低Java 17运行。相较Java 11,Java 17的代码可读性更好(Record, Pattern Matching等)并且性能有所提升(如更好的GC选择和更优的随机数生成器)。迁移到Java 17需要注意以下:

总结: Java 17 是继Java 11之后的又一个长期支持版本,也是“现代Java”功能集的大成者。密封类、Record、Pattern Matching这些Java近年推出的语法糖在此全部稳固下来。它极大地提高了Java的开发效率和表达力,让Java代码更加简洁、类型更加安全可控。在JDK底层,Java 17 清理了许多历史遗留(如安全管理器、老的RMI子系统等),并优化了Randome API等基础库。对于计划长期使用的项目,Java 17 无疑是一个理想的选择。Java 17发布后,Java社区进入半年发布的新常态,但有了LTS保障,企业也能每隔几年来一次平滑升级以享受新特性红利。

Java 18 新特性

发行时间: 2022 年 3 月

平台与性能

新特性和API

安全与密码

总结: Java 18 新特性相对较少,而且很多是预览或孵化(Vector、外部内存、switch模式)。最大的“显性”变化对普通开发者来说莫过于默认编码UTF-8和内置简易Web服务器。UTF-8默认消除了长期以来跨平台编码不一致的问题,让Java更贴合互联网时代的数据交换标准。jwebserver则体现出Java对开发者体验的重视,即使是一个很小的工具,也能发挥作用。在底层性能上,Java 18 继续推进Panama和Vector等,使Java在系统编程和高性能计算上更具竞争力。作为非LTS版本,Java 18 提供了一个让社区试水新功能的平台,其反馈将作用于后续的Java 19和21中。

Java 19 新特性

发行时间: 2022 年 9 月

并发与虚拟线程

Thread.startVirtualThread(() -> {
    // 虚拟线程执行的代码
    System.out.println("Hello from a virtual thread");
});

或通过 Executors.newVirtualThreadPerTaskExecutor() 创建虚拟线程的执行器。对于IO阻塞操作,虚拟线程在等待时会让出底层OS线程,不会“固定”占用它。这意味着高并发IO场景下,使用虚拟线程可以大幅提升吞吐,而编程模型仍然是简单的同步代码,不需要使用复杂的Async框架。应用场景: 特别适合服务器端处理大量并发连接的场景(如Web服务器、聊天服务器),过去用线程池+异步,现在可以一请求一线程且线程数非常多却无明显性能损失。虚拟线程在Java 19为预览,需要 --enable-preview 开启,并在Java 21正式发布(JEP 444)。

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> user = scope.fork(() -> fetchUser());
    Future<String> order = scope.fork(() -> fetchOrder());
    scope.join();           // 等待所有任务完成
    scope.throwIfFailed();  // 若有任务异常则抛出
    // 获取结果
    String userInfo = user.resultNow();
    String orderInfo = order.resultNow();
}

这样的模式使得并行任务像局部代码块一样管理,有助于避免遗留线程无法控制的问题。这一API仍在孵化阶段(需显式加入 jdk.incubator.concurrent),最终目标是简化多线程代码的组织,提高可维护性。

语言和模式匹配

Object obj = new Point(3, 5);
if (obj instanceof Point(int x, int y)) {
    System.out.println("x=" + x + ", y=" + y);
}

这里instanceof Point(int x, int y) 就是记录模式,它不仅判断 obj 是否为 Point,还进一步将内部的 x,y 解构出来赋给新的局部变量。Record Patterns 还可以嵌套使用,用于复杂结构的匹配。应用场景: 配合密封类和switch,可以用非常简洁的代码处理递归数据结构或组合数据。例如匹配一个包含两个点的 Line 记录:case Line(Point(int x1, int y1), Point(int x2, int y2)) -> ...。记录模式在Java 19为预览,Java 20进行了第二次预览,Java 21正式发布。

外部接口和内存

CLinker linker = CLinker.systemCLinker();
MethodHandle strlen = linker.downcallHandle(
        linker.lookup("strlen").get(), 
        FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
MemorySegment cString = CLinker.toCString("hello");
long len = (long) strlen.invokeExact(cString.address());

以上代码通过 CLinker 调用标准C函数 strlen 计算字符串长度。可以看到没有一行JNI代码,全部在Java中完成。这个特性对于需要和C库交互的应用来说极为便利 (优点),性能也与JNI相当。经过Java 19、20两轮预览,最终在Java 21正式发布 (JEP 442)。

其他改进

总结: Java 19是一个亮点颇多的版本。虚拟线程的初次亮相标志着Java并发模型迎来巨大变革;结构化并发记录模式 等则完善了并发和模式匹配的语法,使得Java代码能够写得更简洁清晰。外部函数/内存API的预览,则让Java开始真正涉足系统级编程。尽管这些重要特性多数仍在预览/孵化,但Java 19作为LTS之前的一个功能集合,已经让社区看到了Java未来的样子:高并发、高性能、强大的模式匹配和数据表达能力。对于愿意尝鲜的开发者,Java 19提供了极好的机会在实验环境试用这些新功能,并为迁移到Java 21LTS做技术准备。

Java 20 新特性

发行时间: 2023 年 3 月

项目 Loom 持续改进

public static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
// 在某作用域绑定 USER_ID
ScopedValue.where(USER_ID, "alice").run(() -> {
    // 在作用域内的任意代码都可读取 USER_ID
    processOrder();
});
void processOrder() {
    String uid = USER_ID.get();  // 获取当前作用域绑定的值 "alice"
    ...
}

相比 ThreadLocal,ScopedValue 的生命周期和线程结构更加明确,不会发生内存泄漏,并且对于大量虚拟线程场景,访问开销也更小。它非常适合保存一些上下文信息(如当前用户ID、请求ID等),供调用链路下游使用而无需层层传参。ScopedValue 在Java 20孵化,Java 21再次预览(JEP 487 第四次预览),预计Java 22+正式推出。

模式匹配和类型系统

if (obj instanceof Point(var x, var y) p && x == y) { ... }

其中的 (var x, var y) 就是记录模式,使用了 var 来省略类型。记录模式的二次预览让它和其它模式(类型模式、常量模式)配合得更好。基本语法趋于稳定,为Java 21转正铺路。

switch (shape) {
    case Circle c               -> handleCircle(c);
    case Rectangle(var w, var h)-> handleRect(w, h);
    case null                   -> handleNull();
    default                     -> handleOther();
}

这里既有类型模式(Circle)、又有记录模式(Rectangle解构),还有显式处理null的分支。Java 20 的预览几乎与最终版一致,之后Java 21正式推出时仅有极小调整。

外部接口

其他

总结: Java 20 在功能上和Java 19一脉相承,并无全新重量级特性亮相,但却将之前的创新推进到了最后阶段。虚拟线程更完善、结构化并发作用域值为简化并发提供了全新思路;记录模式switch模式几乎打磨成熟,为模式匹配全面落地做好准备。这些都预示了即将到来的Java 21将会是个非常强大的版本。因此对于期待LTS的开发者来说,Java 20 显得“波澜不惊”,但这正是暴风雨前的平静——所有新特性的铺垫都已就绪,等待在Java 21中释放。

Java 21 新特性

发行时间: 2023 年 9 月 19 日 (LTS长期支持版)

Java 21 作为第5个LTS版本,融合了过去几版的预览特性,带来了15项新特性。这是一版里程碑式的发布,使Java在语法、并发、性能等方面迈上新台阶。

语言特性

String user = "Bob";
int score = 42;
String msg = STR.`Hello, ${user}! Your score is ${score}.`;
System.out.println(msg); // 输出: Hello, Bob! Your score is 42.

使用时需在字符串前加 STR. 前缀来开启模板字符串字面量(这是预览期的语法要求)。模板中可以直接嵌入局部变量、字段、方法调用等。编译器会将模板转换为高效拼接代码,并可配合模板处理API进行高级用法(如SQL安全拼接等)。应用场景: 拼接字符串是最常见的需求,字符串模板让这件事变得安全又简单,再也不需大量引号加加号,也无需 String.format 的占位符,对新手友好、对老手高效。虽然Java 21为预览,需要 --enable-preview,但预计不久后转正,使Java终于拥抱与现代语言一致的字符串插值能力。

记录模式允许匹配记录类并直接解构其组分,例如:

if (shape instanceof Rectangle(int w, int h)) {
    System.out.println("长方形面积:" + w*h);
}

Switch模式匹配允许 switch 直接按类型/结构分支,例如结合密封类:

switch (shape) {
    case Circle(double r)        -> System.out.println("圆面积:" + Math.PI*r*r);
    case Rectangle(int w, int h) -> System.out.println("矩形面积:" + w*h);
    case null                    -> System.out.println("空形状");
    default                      -> System.out.println("未知形状");
}

其中 Circle(double r) 是类型+变量模式,Rectangle(int w, int h) 是记录模式。由于 Shape 假设是密封接口,列举了所有子类,所以可以省略 default 或只用于 null 情况。应用场景: 模式匹配让 Java 处理复杂数据结构时如鱼得水。例如遍历一个 JSON 抽象语法树,不同节点类型(对象、数组、值)用 switch 匹配类型,既直观又安全。过去这些需要大量的 instanceof + 强转+if/else,而现在简洁明了。Java 21 的模式匹配使Java具备了代数数据类型模式匹配的威力,大大提高了代码可读性和可靠性。

if (point instanceof Point(int x, _)) {  // 只关心x,不在意y值
    System.out.println("x坐标:" + x);
}

这里第二个分量用了 _,表示匹配任何y而不绑定变量。这避免了像以前那样写一个无用的名字又不使用产生警告。

int _ = computeHeavy();  // 调用方法但不关心返回值,只是为了触发方法副作用

定义了一个名为_的int变量,但后续无法引用它。这样做的意义在于清晰表达“我故意忽略这个返回值”。这种写法主要用于兼容需要变量语法但我们不需要实际变量的场景(类似 _ = in.read() 读而不处理)。另外,未命名变量只能定义一次,不能重复定义或和其他变量重名。

应用场景: 未命名模式在复杂模式匹配时尤其实用,如果一个模式中有部分内容不关心,可以用 _ 占位,使代码更简洁。未命名变量用得不多,但在调用返回值不需要的方法时可以表明意图。例如在测例中调用某初始化函数,只为触发内部逻辑,不关心结果,就可以 Object _ = init();。需要注意,下划线作为关键字在这之前已不能当普通变量名使用,因此这项特性没有向后不兼容问题。

void main() {
    System.out.println("Hello World!");
}

这样写一个 .java 文件也能编译运行。编译器会自动包裹成 class X { void main() {...} } 形式并生成入口。

应用场景: 主要目的是降低初学者学习Java的门槛,以及让脚本式的小程序更简洁。过去新手写HelloWorld,一上来就要理解类、静态方法、字符串数组,对入门不友好。有了这个预览特性,可以更像脚本语言一样直接写代码执行。当然,这只是源代码层面的语法糖,实际编译还是会生成类。这个特性也显示了Java追求更“轻量”的一面。不过在大型项目中,还是会明确定义类和main,未命名类更多用于教学、快速脚本等场景。它在Java 21为预览,要启用预览才能使用。

并发和虚拟线程

Thread vt = Thread.startVirtualThread(() -> {
    // ... 虚拟线程执行代码 ...
});
vt.join();

或通过 Executors.newVirtualThreadPerTaskExecutor() 来大量提交任务。虚拟线程的调度、同步都与普通线程一致,支持ThreadLocal、锁等,只是代价极低,可以创建百万级。应用场景: 几乎所有并发服务器负载都可以考虑迁移到虚拟线程模型。例如基于Spring、Tomcat的Web应用,可配置使用虚拟线程的执行器,每请求一个虚拟线程处理,简化复杂的异步逻辑。数据库驱动如果配合良好(阻塞IO释放OS线程),也能提升吞吐。总之,虚拟线程使编程模型和高并发性能实现了统一:我们可以用同步代码写出异步高并发效果。需要注意监控工具升级以识别大量线程,但JDK本身的jstack等已经支持很好。

SequencedCollection<Integer> coll = new ArrayDeque<>();
coll.addFirst(1);
coll.addLast(2);
System.out.println(coll.getFirst()); // 1
System.out.println(coll.getLast());  // 2
SequencedCollection<Integer> reversed = coll.reversed();

在Java 21中,ArrayListLinkedList 实现了 SequencedCollectionLinkedHashSet 实现了SequencedSetLinkedHashMap实现了 SequencedMap。这样我们可以方便地对这些集合的头尾进行操作,而不必区分List或Deque接口。例如以前要获取List最后一个元素需要 list.get(list.size()-1),现在 list.getLast() 即可。反转视图 reversed() 则返回一个与原集合顺序相反的同类型集合视图,操作视图相当于操作原集合两端。应用场景: 任何需要队列/双端队列操作的地方更方便了,而且统一接口后,可以写通用算法处理SequencedCollection,不用分别考虑List或Deque,实现接口分离更合理(Deque过于笼统,而Sequenced只关注顺序)。这个特性在集合框架演进史上是个重要调整,使得List, Set, Map在迭代顺序方面有了共同的父接口,更加一致。

JVM 和性能

新安全特性

总结: Java 21 是迄今功能最强大的Java版本之一。虚拟线程结构化并发在并发编程领域给予Java巨大能量,Sequenced集合完善了集合框架,模式匹配全面落地使Java具备了代数数据类型的便利。诸多预览(字符串模板、隐藏类、KDF等)也预示着未来的发展方向。作为LTS版本,Java 21 具备足够的稳定性,又融入了现代语言的诸多精华,难怪被称为“继Java 8之后最重要的版本”。对于开发者来说,如果从Java 17升级上来,将明显感受到代码可以写得更简洁(Record, 模式匹配), 性能可以更上一层楼(虚拟线程, 新GC), 开发体验也更好(UTF-8默认, 文本块, 字符串模板等)。Java 21 为未来几年Java的发展奠定了基础,下一个LTS预计是Java 25,期间Java 22/23/24也会继续在这些方向演进。

上一篇 下一篇

猜你喜欢

热点阅读