前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java 21 新特性的实践,确实很丝滑!

Java 21 新特性的实践,确实很丝滑!

作者头像
用户1220090
发布于 2025-05-19 02:57:37
发布于 2025-05-19 02:57:37
14800
代码可运行
举报
文章被收录于专栏:芋道源码芋道源码
运行总次数:0
代码可运行

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn

来源:medium.com/


JDK 21 于 2023 年 9 月 19 日发布,是继之前的 LTS 版本 JDK 17 之后最新的长期支持 (LTS) 版本。在本文中,我们将探讨 JDK 21 新引入的功能。

1 虚拟线程

从 Java 代码的角度来看,虚拟线程感觉就像普通线程,但它们没有 1:1 映射到操作系统/平台线程。它是从虚拟线程到载体线程进而到操作系统线程的M:N映射。

有一个所谓的载体线程池,虚拟线程临时映射(“安装”)到该线程池上。一旦虚拟线程遇到阻塞操作,虚拟线程就会从载体线程中移除(“卸载”),并且载体线程可以执行另一个虚拟线程(新的或之前被阻塞的虚拟线程)。

载体线程池是ForkJoinPool

虚拟线程的一些优点:

  • 提高应用程序吞吐量
  • 提高应用程序可用性
  • 减少内存消耗

创建虚拟线程

要创建虚拟线程,我们可以使用 Thread.ofVirtual() 工厂方法并传递可运行对象。

  1. Thread.ofVirtual().start(Runnable);
  2. Thread.ofVirtual().unstarted(Runnable);

如果你想让虚拟线程立即启动,你可以使用start() 方法,它会立即执行传递给它的Runnable start()

如果不希望虚拟线程立即启动,可以使用该unstarted()方法。

创建使用虚拟线程的ExecutorService

我们只需要替换newFixedThreadPoolnewVirtualThreadPerTaskExecutor

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VirtualThreadExample {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

        executor.submit(() -> {
            System.out.println(Thread.currentThread().getName())
        });

        executor.shutdown();
    }
}

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

2 顺序集合

顺序集合为我们提供了defined encounter order(是一种所见即所得的顺序,含义是从队列中取出元素的顺序既是你存放该元素时候的顺序),用于访问第一个和最后一个元素并以相反的顺序迭代。

这意味着我们可以在集合的两端添加、检索或删除元素。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface SequencedCollection<E> extends Collection<E> {
    default void addFirst(E e) { ... }
    default void addLast(E e) { ... }
    default E getFirst() { ... }
    default E getLast() { ... }
    default E removeFirst() { ... }
    default E removeLast() { ... }
    SequencedCollection<E> reversed();
}

正如我们所看到的,除了reverse()之外的所有方法都是默认方法并提供默认实现。

这意味着现有的集合类(例如 ArrayList 和 LinkedList)都可以实现此接口,而无需更改其代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ArrayList<Integer> list = new ArrayList<>();
        list.add(1); // [1]
        list.addFirst(0); // [0, 1]
        list.addLast(2); // [0, 1, 2]
        list.getFirst(); // 0
        list.getLast(); // 2
        list.reversed(); // [2, 1, 0]

SequencedSet

SequencedSet 接口对于具有有序元素的 Set 非常有用,特别是当您必须执行某些操作(例如检索或删除第一个或最后一个索引处的元素)时。它还提供了一种反转元素的方法。

您还需要知道两个 SequencedSet 对象的比较与其他类型的 Set 相同,不依赖于元素顺序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
    SequencedSet<E> reversed();
}

LinkedHashSet 是 Set 的一个实现,它实现了 SequencedSet 接口。

因此,您可以使用 LinkedHashSet 来创建 SequencedSet。

Set 的其他实现(例如 HashSet 和 TreeSet)未实现该接口。

让我们探索一些示例来演示如何访问第一个和最后一个元素,以及如何使用反向函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SequencedSet<String> values = new LinkedHashSet<>();
        values.add("one");
        values.add("two");
        System.out.println(values); // [one, two]

        values.addFirst("zero");
        System.out.println(values); // [zero, one, two]
        values.addFirst("one");
        System.out.println(values); // [one, zero, two]

        values.addLast("three");
        System.out.println(values); // [one, zero, two, three]

        values.removeFirst();
        System.out.println(values); // [zero, two, three]

        SequencedSet<String> reversedSet = values.reversed();
        System.out.println(reversedSet); // [three, two, zero]

        boolean isEqual = values.equals(reversedSet);
        System.out.println(isEqual); // true
        System.out.println(values.hashCode()); // 612888
        System.out.println(reversedSet.hashCode()); // 612888
        System.out.println(values.hashCode() == reversedSet.hashCode()); // true

SequencedMap

如果要使用 SequencedMap 中定义的新方法,则需要使用 Map 实现,例如 LinkedHashMap 或实现 SortedMap 的 Map。

HashMap 不利用 Sequenced Collections,因为它没有定义 defined encounter order(是一种所见即所得的顺序,含义是从队列中取出元素的顺序既是你存放该元素时候的顺序)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface SequencedMap<K,V> extends Map<K,V> {
    SequencedMap<K,V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Entry<K,V>> sequencedEntrySet();
    V putFirst(K, V);
    V putLast(K, V);
    Entry<K, V> firstEntry();
    Entry<K, V> lastEntry();
    Entry<K, V> pollFirstEntry();
    Entry<K, V> pollLastEntry();
}

在下面的示例中,正如您所看到的,我们可以通过firstEntry()lastEntry()方法访问第一个和最后一个元素。

pollFirstEntry()方法将删除并返回第一个键值元素,如果映射为空,则返回 null。

此外,调用reverse()只会比较元素,而不依赖于它们的顺序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SequencedMap<String, Integer> myMap = new LinkedHashMap<>();
        myMap.put("one", 1);
        myMap.put("two", 2);
        System.out.println(myMap); // {one=1, two=2}

        Entry<String, Integer> firstEntry = myMap.firstEntry();
        System.out.println(firstEntry); // one=1

        Entry<String, Integer> lastEntry = myMap.lastEntry();
        System.out.println(lastEntry); // two=2

        myMap.putFirst("zero", 0);
        System.out.println(myMap); // {zero=0, one=1, two=2}
        myMap.putFirst("one", -1);
        System.out.println(myMap); // {one=-1, zero=0, two=2}

        Entry<String, Integer> polledFirstEntry = myMap.pollFirstEntry();
        System.out.println(polledFirstEntry); // one=-1
        System.out.println(myMap); // {zero=0, two=2}

        SequencedMap<String, Integer> reversedMap = myMap.reversed();
        System.out.println(reversedMap); // {two=2, zero=0}

        boolean isEqual = myMap.equals(reversedMap);
        System.out.println(isEqual); // true
        System.out.println(myMap.hashCode()); // 692224
        System.out.println(reversedMap.hashCode()); // 692224
        System.out.println(myMap.hashCode() == reversedMap.hashCode()); // true

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

3 字符串模板

这是预览功能,默认禁用,我们需要使用

--enable-preview启用字符串模板。

首先,在深入探讨字符串模板之前,我将探讨一些用于组合字符串的技术。

+(加号)运算符: 最大的缺点是每次使用 + 运算符时都会创建一个新字符串。

StringBuffer 和 StringBuilder: StringBuffer 是线程安全的,而 StringBuilder 是在 Java 5 中添加的,性能更高,但不是线程安全的替代方案。

它们的主要缺点是冗长,尤其是对于更简单的字符串:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var greeting = new StringBuilder()
        .append("Hello, welcome ")
        .append(name)
        .toString();

String::format 和 String::formatter: 它们允许可重用模板,但它们要求我们指定格式并以正确的顺序提供变量。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var format = "Good morning %s, It's a beautiful day!";
        var text = String.format(format, name);
// Java 15+
        var text = format.formatter(name);

尽管我们节省了字符串分配的数量,但现在 JVM 必须解析/验证模板字符串。

java.text.MessageFormat: 与String格式相同,但更详细

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 var format = new MessageFormat("Good morning {0}, It's a beautiful day!");
        var greeting = format.format(name);

现在我们有字符串模板来拯救

它简单、简洁,处理字符串的新方法称为模板表达式。它们可以执行插值,还为我们提供了组合字符串的灵活性,并将结构化文本转换为任何对象,而不仅仅是字符串。

模板表达式由三个组成部分组成:

  • 模板处理器:Java 提供了两种用于执行字符串插值的模板处理器:STR 和 FMT
  • 包含包装表达式的模板,如 {name}
  • 点 (.) 字符

以下是一些关于如何将字符串模板与模板处理器一起使用的示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.mina.stringtemplates;

import static java.util.FormatProcessor.FMT;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class StringTemplateExamples {

    public static String greeting(String firstName, String lastName) {
        return STR."Hello! Good morning \{ firstName } \{ lastName }" ;
    }

    public static String multiplyWithArithmeticExpressions(int a, int b) {
        return STR."\{ a } times \{ b } = \{ a * b }" ;
    }

    public static String multiplyWithJavaExpression(int a, int b) {
        return STR."\{ a } times \{ b } = \{ Math.multiplyExact(a, b) }" ;
    }

    //  multiplication with floating point numbers rounded to two decimal places using the FMT template processor
    public static String multiplyFloatingNumbers(double a, double b) {
        return FMT."%.2f\{ a } times %.2f\{ b } = %.2f\{ a * b }" ;
    }

    public static String getErrorResponse(int httpStatus, String errorMessage) {
        return STR."""
                {
                  "httpStatus": \{ httpStatus },
                  "errorMessage": "\{ errorMessage }"
                }""" ;
    }

    public static String getCurrentDate() {
        return STR."Today's date: \{
        LocalDate.now().format(
                DateTimeFormatter.ofPattern("yyyy-MM-dd")
        ) }" ;
}
}

4 记录模式

记录模式匹配是一种在单个步骤中匹配记录类型并访问其组件的方法。

我们用它来测试一个值是否是记录类类型的实例,如果是,则对其组件值执行模式匹配。

下面的示例测试是否是具有记录模式transaction的记录实例TransactionTransaction(String type, double amount)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.mina.recordpattern;

public class RecordPatternExample {

    // I'm using "_" for readability here, this won't compile
    public static String getTransactionType(Transaction transaction) {
        return switch (transaction) {
            case null -> throw new IllegalArgumentException("Transaction can not be null.");
            case Transaction(String type, double amount) when type.equals("Deposit") && amount > 0 -> "Deposit";
            case Transaction(String type, _) when type.equals("Withdrawal") -> "Withdrawal";
                default -> "Unknown transaction type";
        };
    }

    record Transaction(String type, double amount) {

    }
}

如果事务为空,会发生什么?你是对的——抛出了一个空指针异常。这也是Java 21中的情况,但是现在我们可以通过写case null->来显式地使用null case,这样可以避免NullPointerException。

保护模式:也可以保护特定情况。例如,我们使用when关键字来检查相等性。

5 switch 模式匹配

switch模式匹配在 Java 17 中作为预览功能引入,并在 Java 21 中永久保留。

语句switch将控制转移到多个语句或表达式之一,具体取决于其选择器表达式的值(可以是任何类型),并且case标签可以具有模式。

它检查其选择器表达式是否与模式匹配,与测试其选择器表达式是否完全等于常量相比,这更具可读性和灵活性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.mina.switchpatternmatching;

import com.mina.switchpatternmatching.SwitchPatternMatchingExample.Transaction.Deposit;
import com.mina.switchpatternmatching.SwitchPatternMatchingExample.Transaction.Withdrawal;

public class SwitchPatternMatchingExample {

    public static String getTransactionType(Transaction transaction) {
        return switch (transaction) {
            case null:
                throw new IllegalArgumentException("Transaction can't be null.");
            case Deposit deposit when deposit.getAmount() > 0: //  Guarded pattern with when clause
                yield "Deposit";
            case Withdrawal withdrawal:
                yield "Withdrawal";
            default:
                yield "Unknown transaction type";
        };
    }

    sealed class Transaction permits Deposit, Withdrawal {

        private double amount;

        public Transaction(double amount) {
            this.amount = amount;
        }

        public double getAmount() {
            return amount;
        }


        final class Withdrawal extends Transaction {

            public Withdrawal(double amount) {
                super(amount);
            }
        }

        final class Deposit extends Transaction {

            public Deposit(double amount) {
                super(amount);
            }
        }
    }
}

希望这篇文章可以帮助您更好地了解 Java 21 的新功能!

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 芋道源码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JDK21 新特性分析,但我用Java8
在此之前,我们创建线程后需要销毁线程来释放内存,会造成大量的成本消耗,完美的解决方案就是我们用线程池,通过线程池来管理线程,并且共享线程相当于说我用的时候去租用一个线程。
小熊学Java
2023/11/03
1.4K0
JDK21 新特性分析,但我用Java8
JDK21昨天发布了!面试官:来,谈下jdk21的新特性!
JDK21 计划23年9月19日正式发布,尽管一直以来都是“版随意出,换 8 算我输”,但这么多年这么多版本的折腾,若是之前的 LTS 版本JDK17你还觉得不错,那 JDK21还是有必要关注一下,因为会有一批重要更新发布到生产环境中,特别是被众人期待已久的虚拟线程,纵然说这东西我感觉没有必要的用不到,需要的早已转go了,但作为近几年JDK一个“重要”的更新,在实际开发应用中还是有相当的价值。如果说之前的 JDK17你还觉得没必要折腾,那 JDK21确实有必要关注一下了。因为 JDK21 引入了一种新型的并发编程模式。当前 Java 中的多线程并发编程绝对是另我们都非常头疼的一部分,感觉就是学起来难啃,用起来难用。但是转头看看使用其他语言的朋友们,根本就没有这个烦恼嘛,比如 GoLang,感觉人家用起来就很丝滑因此这篇文章主要摘录了这次更新中个人觉得相对有价值的几点做个基本的介绍,想要体验新功能的同学可以阅读一下。
架构狂人
2023/09/21
3K0
JDK21昨天发布了!面试官:来,谈下jdk21的新特性!
Java21的新特性
在java21之前,字符串拼接或者字符串与表达式组合主要是用StringBuilder、String::format、java.text.MessageFormat,不过可读性都不是太好,java21引入了StringTemplate(java.lang.StringTemplate)来解决这个问题。
code4it
2023/09/20
1K0
Java21的新特性
Java21的新特性
在java21之前,字符串拼接或者字符串与表达式组合主要是用StringBuilder、String::format、java.text.MessageFormat,不过可读性都不是太好,java21引入了StringTemplate(java.lang.StringTemplate)来解决这个问题。
code4it
2023/09/22
7830
Java21的新特性
Java之TreeMap详解
1. 是一个有序的key-value集合,它是通过红黑树实现的。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
用户5224393
2019/08/20
3.5K0
Java8新特性----Stream
map–接收Lambda,将元素转换为其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
大忽悠爱学习
2021/11/15
5280
Java 8新特性
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
用户9615083
2022/12/25
1.4K0
Java 8新特性
Java8-新特性
哈喽!大家好,我是小简。今天开始学习《Java8-新特性》,此系列是我做的一个 “Java 从 0 到 1 ” 实验,给自己一年左右时间,按照我自己总结的 Java-学习路线,从 0 开始学 Java 知识,并不定期更新所学笔记,期待一年后的蜕变吧!<有同样想法的小伙伴,可以联系我一起交流学习哦!>
小简
2023/01/04
4180
Java8-新特性
Java5新特性及使用
Java语言引入泛型的好处是安全简单。可以将运行时错误提前到编译时错误。在Java5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的任意化,任意化带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
Abalone
2022/07/14
1.4K0
JavaSE:第十六章:java8新特性
##java8内容 1.Lambda表达式 ★ 2.函数式接口 ★ 3.方法引用 ★ 4.构造器引用|数组引用 ★ 5.StreamAPI ★ 6.接口中可以定义默认方法和静态方法 ★ 7.Optional类的引入:为了减少空指针异常【了解】 8.新日期API【了解】 9.重复注解【了解】 10.Nashone引擎的使用:在jvm上运行js【后面课程】
Java廖志伟
2022/08/01
6250
【Java 21 新特性】顺序集合(Sequenced Collections)
引入新的接口表示具有定义的遇到顺序的集合。每个这样的集合都有一个明确定义的第一个元素、第二个元素,依此类推,直到最后一个元素。提供统一的API来访问它的第一个和最后一个元素,并以相反的顺序处理它的元素。
JavaEdge
2024/01/03
2550
【Java 21 新特性】顺序集合(Sequenced Collections)
Java8 新特性
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以取代大部分的匿名内部类,可以写出更简洁、更灵活的代码。尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。作为一种更紧凑的代码风格,使 Java 的语言表达能力得到提升。JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。 【1】从匿名类到 Lambda 的转换:虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
Java架构师必看
2021/05/14
9290
Java8 新特性
Java学习之基础
常用五大包:java.long(longuage)该包下的类,在使用时是不需要导包的;java.util 工具包;java,io 文件读写;java.net 网络编程包;java.sql 操作数据库
用户8447427
2022/08/18
4270
Java学习之基础
Java23的新特性
jdk22的JEP 457: Class-File API (Preview)提供了一个用于解析、生成和转换 Java 类文件的标准 API jdk23则作为第二次preview,比如要生成如下类
code4it
2024/09/29
2110
java8 新特性
Java 8 Tutorial 接口的默认方法(Default Methods for Interfaces) Lambda表达式(Lambda expressions) 函数式接口(Functional Interfaces) 方法和构造函数引用(Method and Constructor References) Lamda 表达式作用域(Lambda Scopes) 访问局部变量 访问字段和静态变量 访问默认接口方法 内置函数式接口(Built-in Functional Inter
袁新栋-jeff.yuan
2020/08/26
5510
Java集合框架(五)—— Map、HashMap、Hashtable、Properties、SortedMap、TreeMap、WeakHashMap、IdentityHashMap、EnumMap
Map Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另一组值用于保存Map里的value,key和value都可以是任何引用类型的数据。Map的key不容许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。   key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的、确定的value。从Map中取出数据时,只要给出指定的key,就可以取出对应的value。   如果把Map里的所有key放在一
10JQKA
2018/05/09
1.6K0
JDK 21探秘:引领Java开发新潮流的强大功能
JDK 21是Java开发工具包的最新版本,它引入了许多令人振奋的新特性,旨在提高开发人员的生产力和代码质量。在本文中,我们将介绍一些JDK 21的新特性,并提供使用示例,以帮助您更好地理解和应用这些功能。
伍六七AI编程
2023/12/07
5450
JDK 21探秘:引领Java开发新潮流的强大功能
生产上常用 JDK 版本 1.7 与 1.8 新特性
JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。使用二进制字面量的好处是,可以是代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B
BUG弄潮儿
2021/12/01
1.2K0
Java之异常处理
异常是程序中的一些错误,但并不是所有的错误都是异常,也就是在的程序中,不存在所谓的“异常”,只存在错误并且错误有时候是可以避免的。
用户5224393
2019/08/20
9800
Java之异常处理
JAVA8十大新特性详解
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下:
用户3467126
2019/11/01
9570
相关推荐
JDK21 新特性分析,但我用Java8
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验