最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?JDK21?新特性全面解析

 更新時(shí)間:2026年05月23日 14:07:18   作者:哎呦喂606  
JDK221作為JTS版本,核心正式特性提升開(kāi)發(fā)效率、程序性能和代碼安全性,虛擬線線、模式匹配fswitch、記錄模式、密封類(lèi)、分代ZGC、序列化集合六大特性,適合高并發(fā)、大數(shù)據(jù)、企業(yè)級(jí)應(yīng)用場(chǎng)景,本文給大家介紹Java?JDK21?新特性,感興趣的朋友跟隨小編一起看看吧

JDK21 是 Oracle 發(fā)布的繼 JDK8、JDK11、JDK17 之后的第四個(gè)長(zhǎng)期支持版(LTS),于 2023 年 9 月 19 日正式發(fā)布,共包含 15 個(gè)新特性,涵蓋語(yǔ)言增強(qiáng)、性能優(yōu)化、API 擴(kuò)展等多個(gè)維度,極大提升了開(kāi)發(fā)效率、程序性能和代碼安全性。

提示:本文所有代碼均基于 JDK21 環(huán)境編寫(xiě),運(yùn)行前請(qǐng)確保已安裝 JDK21 并配置好環(huán)境變量;涉及預(yù)覽特性的代碼,需在編譯和運(yùn)行時(shí)添加 --enable-preview 參數(shù)(具體用法見(jiàn)對(duì)應(yīng)特性說(shuō)明)。

一、核心正式特性

1. 虛擬線程(Virtual Threads,JEP 444)—— 輕量級(jí)高并發(fā)利器

1.1 特性說(shuō)明

虛擬線程是 JDK21 最受關(guān)注的核心特性,它是由 JVM 管理的輕量級(jí)線程,而非操作系統(tǒng)內(nèi)核線程,徹底解決了傳統(tǒng)平臺(tái)線程(Platform Thread)資源占用高、上下文切換開(kāi)銷(xiāo)大的問(wèn)題。虛擬線程的內(nèi)存占用極低(每個(gè)僅需幾 KB),可輕松創(chuàng)建數(shù)百萬(wàn)個(gè)并發(fā)任務(wù),尤其適合 I/O 密集型場(chǎng)景(如接口調(diào)用、數(shù)據(jù)庫(kù)操作、文件讀寫(xiě)等),能極大提升程序吞吐量,且無(wú)需修改現(xiàn)有線程相關(guān)代碼,上手成本極低。

核心優(yōu)勢(shì):

  • 輕量級(jí):內(nèi)存占用遠(yuǎn)低于平臺(tái)線程,單個(gè) JVM 可創(chuàng)建數(shù)百萬(wàn)個(gè)虛擬線程。
  • 低開(kāi)銷(xiāo):上下文切換由 JVM 管理,無(wú)需操作系統(tǒng)內(nèi)核參與,開(kāi)銷(xiāo)極小。
  • 易遷移:現(xiàn)有基于 Runnable、Callable 的代碼可直接遷移,無(wú)需修改業(yè)務(wù)邏輯。
  • 結(jié)構(gòu)化并發(fā):配合 Executors.newVirtualThreadPerTaskExecutor(),簡(jiǎn)化并發(fā)任務(wù)管理。
  • 調(diào)度優(yōu)化:虛擬線程阻塞時(shí)會(huì)自動(dòng)切換到其他正在運(yùn)行的線程,充分利用 CPU 時(shí)間,避免資源閑置。
  • 無(wú)規(guī)模限制:不受操作系統(tǒng)對(duì)線程數(shù)量的限制,理論上支持無(wú)限量創(chuàng)建(受限于內(nèi)存)。

1.2 代碼樣例(4種創(chuàng)建方式,推薦第三種)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
 * 虛擬線程 4種創(chuàng)建方式演示
 */
  public static void main(String[] args) throws InterruptedException {
        // 方式1:通過(guò) Thread.startVirtualThread() 直接創(chuàng)建(最簡(jiǎn)單)
        Thread.startVirtualThread(() -> {
            System.out.println("方式1:虛擬線程運(yùn)行中,線程類(lèi)型:" + Thread.currentThread().getClass().getSimpleName());
            try {
                // 模擬I/O操作(睡眠100ms)
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("方式1:虛擬線程執(zhí)行完畢");
        });
        // 方式2:通過(guò) Thread.Builder 構(gòu)建(可設(shè)置線程名稱(chēng),移除不支持的優(yōu)先級(jí))
        Thread virtualThread = Thread.ofVirtual()
                .name("virtual-thread-2") // 設(shè)置線程名稱(chēng)(虛擬線程支持設(shè)置名稱(chēng))
                .unstarted(() -> {
                    System.out.println("方式2:虛擬線程運(yùn)行中,線程名稱(chēng):" + Thread.currentThread().getName());
                    try {
                        TimeUnit.MILLISECONDS.sleep(150);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("方式2:虛擬線程執(zhí)行完畢");
                });
        virtualThread.start(); // 啟動(dòng)虛擬線程
        // 方式3:通過(guò)線程池創(chuàng)建(推薦,適合批量管理并發(fā)任務(wù))
        // newVirtualThreadPerTaskExecutor():每個(gè)任務(wù)對(duì)應(yīng)一個(gè)虛擬線程
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 5; i++) {
                int taskId = i;
                executor.submit(() -> {
                    System.out.println("方式3:虛擬線程任務(wù)" + taskId + "運(yùn)行中,線程ID:" + Thread.currentThread().threadId());
                    try {
                        TimeUnit.MILLISECONDS.sleep(200);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("方式3:虛擬線程任務(wù)" + taskId + "執(zhí)行完畢");
                });
            }
        } // try-with-resources 自動(dòng)關(guān)閉線程池,無(wú)需手動(dòng)shutdown
        // 方式4:通過(guò) ThreadFactory 創(chuàng)建(靈活定制線程屬性)
        // 替換 namePrefix 為 name(prefix, start),移除無(wú)效的 daemon 方法
        ThreadFactory factory = Thread.ofVirtual()
                .name("custom-virtual-thread-", 0) // 名稱(chēng)前綴 + 起始數(shù)字(0開(kāi)始)
                .factory();
        Thread customThread = factory.newThread(() -> {
            System.out.println("方式4:自定義虛擬線程運(yùn)行中,線程名稱(chēng):" + Thread.currentThread().getName());
            try {
                TimeUnit.MILLISECONDS.sleep(250);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("方式4:自定義虛擬線程執(zhí)行完畢");
        });
        customThread.start();
        // 等待所有虛擬線程執(zhí)行完畢(主線程阻塞,避免程序提前退出)
        TimeUnit.SECONDS.sleep(1);
        System.out.println("所有虛擬線程執(zhí)行完畢,主線程退出");
    }

1.3 運(yùn)行結(jié)果(關(guān)鍵信息)

方式1:虛擬線程運(yùn)行中,線程類(lèi)型:VirtualThread
方式2:虛擬線程運(yùn)行中,線程名稱(chēng):virtual-thread-2
方式3:虛擬線程任務(wù)3運(yùn)行中,線程ID:40
方式3:虛擬線程任務(wù)2運(yùn)行中,線程ID:39
方式3:虛擬線程任務(wù)4運(yùn)行中,線程ID:41
方式3:虛擬線程任務(wù)0運(yùn)行中,線程ID:37
方式3:虛擬線程任務(wù)1運(yùn)行中,線程ID:38
方式1:虛擬線程執(zhí)行完畢
方式2:虛擬線程執(zhí)行完畢
方式3:虛擬線程任務(wù)0執(zhí)行完畢
方式3:虛擬線程任務(wù)2執(zhí)行完畢
方式3:虛擬線程任務(wù)4執(zhí)行完畢
方式3:虛擬線程任務(wù)3執(zhí)行完畢
方式3:虛擬線程任務(wù)1執(zhí)行完畢
方式4:自定義虛擬線程運(yùn)行中,線程名稱(chēng):custom-virtual-thread-0
方式4:自定義虛擬線程執(zhí)行完畢
所有虛擬線程執(zhí)行完畢,主線程退出

1.4 注意事項(xiàng)

  • 虛擬線程適合 I/O 密集型場(chǎng)景,不適合 CPU 密集型場(chǎng)景(CPU 密集型任務(wù)建議使用平臺(tái)線程,避免 JVM 調(diào)度開(kāi)銷(xiāo))。
  • 虛擬線程不能調(diào)用 Thread.setDaemon()(守護(hù)線程)、Thread.suspend()、Thread.resume() 方法,調(diào)用會(huì)拋出 UnsupportedOperationException
  • 通過(guò) Executors.newVirtualThreadPerTaskExecutor() 創(chuàng)建的線程池,無(wú)需手動(dòng)調(diào)用 shutdown(),try-with-resources 會(huì)自動(dòng)關(guān)閉。
  • 虛擬線程的優(yōu)先級(jí)設(shè)置僅對(duì) JVM 調(diào)度有效,不影響操作系統(tǒng)內(nèi)核線程的優(yōu)先級(jí)。
  • 虛擬線程與平臺(tái)線程完全兼容,可通過(guò) Thread.isVirtual() 方法判斷線程類(lèi)型。

2. 模式匹配 for switch(Pattern Matching for switch,JEP 441)—— 簡(jiǎn)化分支判斷

2.1 特性說(shuō)明

JDK21 正式定稿了 switch 模式匹配功能,該特性經(jīng)過(guò) 4 輪預(yù)覽迭代(JDK17-JDK20),最終成為正式特性。它擴(kuò)展了 switch 的能力,允許 case 標(biāo)簽使用模式(而非僅常量),支持類(lèi)型匹配、null 匹配、記錄模式、守衛(wèi)模式等,徹底解決了傳統(tǒng) switch 只能匹配常量、類(lèi)型判斷繁瑣、空值處理麻煩的問(wèn)題,讓分支邏輯更簡(jiǎn)潔、更安全、更易維護(hù)。

核心增強(qiáng)點(diǎn):

  • 支持類(lèi)型模式:case 可直接匹配指定類(lèi)型,無(wú)需手動(dòng)強(qiáng)轉(zhuǎn)。
  • 支持 null 匹配:專(zhuān)門(mén)的 case null,無(wú)需在 switch 外部判斷空值。
  • 支持 guarded case(條件守衛(wèi)):通過(guò) when 子句添加額外判斷條件(也支持 && 語(yǔ)法)。
  • 支持窮盡性檢查:結(jié)合密封類(lèi)使用時(shí),編譯器會(huì)檢查是否覆蓋所有可能的情況,避免遺漏。
  • 改進(jìn)類(lèi)型推斷:編譯器能更好地推斷 switch 表達(dá)式的返回類(lèi)型,減少顯式聲明。

2.2 代碼樣例(5種常見(jiàn)場(chǎng)景)

import java.util.Objects;
/**
 * switch 模式匹配 5種常見(jiàn)場(chǎng)景演示
 * 要求:Java 21+ 版本運(yùn)行(所有特性均為正式特性)
 */
public class SwitchPatternMatchingDemo {
    public static void main(String[] args) {
        // 場(chǎng)景1:類(lèi)型模式匹配(替代 instanceof + 強(qiáng)轉(zhuǎn))
        Object obj1 = "JDK21";
        String typeResult = switch (obj1) {
            case String s -> "字符串類(lèi)型,長(zhǎng)度:" + s.length();
            case Integer i -> "整數(shù)類(lèi)型,值:" + i;
            case Double d -> "浮點(diǎn)數(shù)類(lèi)型,值:" + d;
            default -> "未知類(lèi)型";
        };
        System.out.println("場(chǎng)景1結(jié)果:" + typeResult);
        // 場(chǎng)景2:null 匹配(無(wú)需外部判斷空值)
        String str = null;
        System.out.print("場(chǎng)景2結(jié)果:");
        switch (str) {
            case null -> System.out.println("字符串為null");
            case "JDK" -> System.out.println("字符串是JDK");
            default -> System.out.println("字符串:" + str);
        }
        // 場(chǎng)景3:條件守衛(wèi)(統(tǒng)一使用 when 子句,修復(fù) && 語(yǔ)法錯(cuò)誤)
        Object obj2 = 42;
        System.out.print("場(chǎng)景3結(jié)果:");
        switch (obj2) {
            case Integer i when i < 18 -> System.out.println("未成年,年齡:" + i);
            case Integer i when i >= 18 && i <= 60 -> System.out.println("成年,年齡:" + i);
            case Integer i -> System.out.println("老年,年齡:" + i);
            case String s when s.length() > 5 -> System.out.println("字符串長(zhǎng)度大于5:" + s);
            default -> System.out.println("未知數(shù)據(jù)");
        }
        // 場(chǎng)景4:結(jié)合密封類(lèi)(窮盡性檢查,編譯器自動(dòng)校驗(yàn))
        Shape shape = new Circle(5.0);
        double area = switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Triangle t -> (t.base() * t.height()) / 2.0;
            // 無(wú)需default,編譯器會(huì)檢查密封類(lèi)的所有子類(lèi)是否都被覆蓋
        };
        System.out.println("場(chǎng)景4結(jié)果:圖形面積:" + String.format("%.2f", area));
        // 場(chǎng)景5:記錄模式匹配(直接解構(gòu)記錄屬性)
        Person person = new Person("Alice", 30);
        String personInfo = switch (person) {
            case Person(String name, int age) when age < 18 -> "未成年人:" + name;
            case Person(String name, int age) -> "成年人:" + name + "(" + age + "歲)";
        };
        System.out.println("場(chǎng)景5結(jié)果:" + personInfo);
    }
    // 密封類(lèi):限制子類(lèi)只能是指定的幾個(gè)類(lèi)(配合switch實(shí)現(xiàn)窮盡性檢查)
    public sealed interface Shape permits Circle, Rectangle, Triangle {}
    // 密封類(lèi)的子類(lèi),必須使用final、sealed或non-sealed修飾(record默認(rèn)final,符合要求)
    public record Circle(double radius) implements Shape {}
    public record Rectangle(double width, double height) implements Shape {}
    public record Triangle(double base, double height) implements Shape {}
    // 記錄類(lèi):用于演示記錄模式匹配
    public record Person(String name, int age) {}
}

2.3 運(yùn)行結(jié)果

場(chǎng)景1結(jié)果:字符串類(lèi)型,長(zhǎng)度:5
場(chǎng)景2結(jié)果:字符串為null
場(chǎng)景3結(jié)果:成年,年齡:42
場(chǎng)景4結(jié)果:圖形面積:78.54
場(chǎng)景5結(jié)果:成年人:Alice(30歲)

2.4 注意事項(xiàng)

  • case 標(biāo)簽的順序會(huì)影響匹配結(jié)果,更具體的模式(如帶條件守衛(wèi)的 case)應(yīng)放在前面,避免被更寬泛的模式覆蓋。
  • 當(dāng) switch 的表達(dá)式為 null 時(shí),會(huì)優(yōu)先匹配 case null,無(wú)需在 switch 前添加 Objects.nonNull() 判斷。
  • 結(jié)合密封類(lèi)使用時(shí),若遺漏密封類(lèi)的某個(gè)子類(lèi),編譯器會(huì)直接報(bào)錯(cuò)(窮盡性檢查),避免運(yùn)行時(shí)異常。
  • 模式變量(如 case String s 中的 s)僅在當(dāng)前 case 分支中有效,不可跨分支使用。
  • 支持兩種條件守衛(wèi)語(yǔ)法:when 子句(更優(yōu)雅)和 && 邏輯判斷(更靈活),可根據(jù)場(chǎng)景選擇。

3. 記錄模式(Record Patterns,JEP 440)—— 簡(jiǎn)化數(shù)據(jù)解構(gòu)

3.1 特性說(shuō)明

記錄模式是 JDK21 正式特性,它擴(kuò)展了模式匹配的能力,允許直接解構(gòu)記錄(Record)的組件,無(wú)需手動(dòng)調(diào)用 record 的 getter 方法,同時(shí)支持嵌套解構(gòu)、部分解構(gòu),可輕松提取復(fù)雜對(duì)象的深層屬性,讓代碼更簡(jiǎn)潔、更具可讀性。該特性從 JDK19 開(kāi)始預(yù)覽,經(jīng)過(guò)兩輪優(yōu)化后正式定稿,常與 switch 模式匹配配合使用,是處理不可變數(shù)據(jù)的最佳實(shí)踐。

核心價(jià)值:

  • 簡(jiǎn)化記錄解構(gòu):直接提取 record 的組件,無(wú)需調(diào)用 xxx() 方法。
  • 支持嵌套解構(gòu):可多層嵌套,提取復(fù)雜對(duì)象的深層屬性。
  • 支持部分解構(gòu):無(wú)需關(guān)注所有組件,可忽略不需要的屬性。
  • 與 switch/instanceof 無(wú)縫集成:實(shí)現(xiàn)復(fù)雜數(shù)據(jù)的快速判斷和解構(gòu)。
  • 編譯時(shí)安全:通過(guò)編譯時(shí)檢查確保屬性訪問(wèn)的正確性,避免類(lèi)型錯(cuò)誤。

3.2 代碼樣例(基礎(chǔ)解構(gòu) + 嵌套解構(gòu) + 部分解構(gòu))

import java.time.LocalDate;
/**
 * 記錄(Record)模式匹配 - 解構(gòu)演示(修復(fù)未命名模式兼容問(wèn)題)
 * 要求:Java 21+
 */
public class RecordPatternMatchingDemo {
    // 基礎(chǔ)記錄(無(wú)嵌套)
    public record Point(int x, int y) {}
    // 嵌套記錄(包含Point記錄)
    public record ColoredPoint(Point point, String color) {}
    // 多層嵌套記錄(包含ColoredPoint記錄)
    public record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
    // 復(fù)雜業(yè)務(wù)記錄(包含嵌套記錄和普通類(lèi)型)
    public record Order(String orderId, LocalDate createTime, ColoredPoint deliveryAddress) {}
    public static void main(String[] args) {
        // 場(chǎng)景1:基礎(chǔ)記錄解構(gòu)(直接提取Point的x和y)
        Point point = new Point(10, 20);
        if (point instanceof Point(int x, int y)) {
            System.out.println("場(chǎng)景1:基礎(chǔ)解構(gòu) - x=" + x + ", y=" + y);
        }
        // 場(chǎng)景2:嵌套記錄解構(gòu)(提取ColoredPoint中的Point組件)
        ColoredPoint coloredPoint = new ColoredPoint(new Point(5, 8), "red");
        if (coloredPoint instanceof ColoredPoint(Point(int x, int y), String color)) {
            System.out.println("場(chǎng)景2:嵌套解構(gòu) - x=" + x + ", y=" + y + ", color=" + color);
        }
        // 場(chǎng)景3:多層嵌套解構(gòu)(提取Rectangle中的深層Point組件)
        Rectangle rectangle = new Rectangle(
                new ColoredPoint(new Point(1, 2), "blue"),
                new ColoredPoint(new Point(3, 4), "blue")
        );
        if (rectangle instanceof Rectangle(
                ColoredPoint(Point(int x1, int y1), String color1),
                ColoredPoint(Point(int x2, int y2), String color2)
        )) {
            System.out.println("場(chǎng)景3:多層嵌套解構(gòu) - " +
                    "左上角(" + x1 + "," + y1 + "," + color1 + "), " +
                    "右下角(" + x2 + "," + y2 + "," + color2 + ")");
        }
        // 場(chǎng)景4:部分解構(gòu)(替換_為顯式命名的忽略變量,解決兼容問(wèn)題)
        Order order = new Order("ORD-2024-001", LocalDate.now(), new ColoredPoint(new Point(100, 200), "green"));
        if (order instanceof Order(String orderId, LocalDate ignoreCreateTime, ColoredPoint(Point(int x, int y), String ignoreColor))) {
            // 忽略ignoreCreateTime和ignoreColor,僅使用需要的變量
            System.out.println("場(chǎng)景4:部分解構(gòu) - 訂單ID:" + orderId + ", 配送坐標(biāo):(" + x + "," + y + ")");
        }
        // 場(chǎng)景5:結(jié)合switch模式匹配(同樣替換_為ignore變量)
        Object obj = new ColoredPoint(new Point(3, 5), "green");
        String deconstructionResult = switch (obj) {
            case Point(int x, int y) -> "Point: (" + x + "," + y + ")";
            case ColoredPoint(Point(int x, int y), String color) -> "ColoredPoint: (" + x + "," + y + "), " + color;
            case Rectangle(ColoredPoint p1, ColoredPoint p2) -> "Rectangle: " + p1 + ", " + p2;
            case Order(String orderId, LocalDate ignoreTime, ColoredPoint ignoreAddr) -> "Order: " + orderId;
            default -> "未知記錄類(lèi)型";
        };
        System.out.println("場(chǎng)景5:switch結(jié)合記錄模式 - " + deconstructionResult);
    }
}

3.3 運(yùn)行結(jié)果

場(chǎng)景1:基礎(chǔ)解構(gòu) - x=10, y=20
場(chǎng)景2:嵌套解構(gòu) - x=5, y=8, color=red
場(chǎng)景3:多層嵌套解構(gòu) - 左上角(1,2,blue), 右下角(3,4,blue)
場(chǎng)景4:部分解構(gòu) - 訂單ID:ORD-2024-001, 配送坐標(biāo):(100,200)
場(chǎng)景5:switch結(jié)合記錄模式 - ColoredPoint: (3,5), green

3.4 注意事項(xiàng)

  • 記錄模式僅適用于 record 類(lèi),普通類(lèi)無(wú)法使用(record 類(lèi)是不可變的,組件具有固定的 getter 方法)。
  • 嵌套解構(gòu)時(shí),需保證每層的模式匹配正確,若某一層不匹配,則整個(gè)模式匹配失敗。
  • 可使用 var 簡(jiǎn)化變量聲明,例如 Point(var x, var y),編譯器會(huì)自動(dòng)推斷變量類(lèi)型。
  • 部分解構(gòu)時(shí),可使用下劃線 _ 表示不需要的組件,提高代碼可讀性。
  • 記錄模式可與 instanceof、switch 配合使用,尤其適合處理 DTO、VO 等不可變數(shù)據(jù)對(duì)象。

4. 密封類(lèi)(Sealed Classes,JEP 409)—— 控制類(lèi)繼承

4.1 特性說(shuō)明

密封類(lèi)在 JDK21 中正式定型,它允許開(kāi)發(fā)者限制類(lèi)或接口的繼承/實(shí)現(xiàn)范圍,明確指定哪些類(lèi)可以繼承它,避免不必要的繼承和擴(kuò)展,提升代碼的可維護(hù)性和安全性。密封類(lèi)從 JDK15 開(kāi)始預(yù)覽,經(jīng)過(guò)多輪優(yōu)化后正式成為標(biāo)準(zhǔn)特性,常與 switch 模式匹配配合使用,實(shí)現(xiàn)窮盡性檢查,確保分支邏輯覆蓋所有可能的子類(lèi)。

核心規(guī)則:

  • 密封類(lèi)使用 sealed 修飾,通過(guò) permits 關(guān)鍵字指定允許繼承的子類(lèi)。
  • 密封類(lèi)的子類(lèi)必須使用以下三種修飾符之一:
    • final:不可再被繼承。
    • sealed:可繼續(xù)限制子類(lèi)(需再次使用 permits 指定)。
    • non-sealed:開(kāi)放繼承,允許任意類(lèi)繼承。
  • 密封接口的實(shí)現(xiàn)類(lèi),也需遵循上述修飾符規(guī)則。
  • 密封類(lèi)及其子類(lèi)必須在同一個(gè)包下(若使用模塊,需在同一個(gè)模塊下)。

4.2 代碼樣例(密封類(lèi) + 密封接口 + 多層密封)

package org.example.model;
/**
 * 密封類(lèi) + 密封接口 + 多層密封 演示
 * 要求:Java 21+
 */
public class SealedClassDemo {
    public static void main(String[] args) {
        // 測(cè)試密封類(lèi)(靜態(tài)內(nèi)部類(lèi)可直接實(shí)例化)
        Animal cat = new Cat();
        Animal dog = new Dog();
        Animal puppy = new Puppy();
        Animal bird = new Bird();
        System.out.println("貓的叫聲:" + cat.makeSound());
        System.out.println("狗的叫聲:" + dog.makeSound());
        System.out.println("小狗的叫聲:" + puppy.makeSound());
        System.out.println("鳥(niǎo)的叫聲:" + bird.makeSound());
        // 測(cè)試密封接口
        Fruit apple = new Apple();
        Fruit banana = new Banana();
        Fruit orange = new Orange();
        System.out.println("蘋(píng)果的顏色:" + apple.getColor());
        System.out.println("香蕉的顏色:" + banana.getColor());
        System.out.println("橙子的顏色:" + orange.getColor());
        // 測(cè)試窮盡性檢查(switch + 密封類(lèi))
        printAnimalType(cat);
        printAnimalType(dog);
        printAnimalType(puppy);
        printAnimalType(bird);
    }
    // 給sealed類(lèi)添加abstract修飾符(包含抽象方法必須聲明為抽象類(lèi))
    // 密封+抽象類(lèi):僅允許 Cat、Dog、Bird 繼承
    public static abstract sealed class Animal permits Cat, Dog, Bird {
        public abstract String makeSound();
    }
    // final 子類(lèi):不可再繼承
    public static final class Cat extends Animal {
        @Override
        public String makeSound() {
            return "喵喵喵";
        }
    }
    // sealed 子類(lèi):可繼續(xù)限制子類(lèi)(僅允許 Puppy 繼承)
    public static sealed class Dog extends Animal permits Puppy {
        @Override
        public String makeSound() {
            return "汪汪汪";
        }
    }
    // Dog 的子類(lèi),final 修飾
    public static final class Puppy extends Dog {
        @Override
        public String makeSound() {
            return "嗚嗚嗚(小狗叫)";
        }
    }
    // non-sealed 子類(lèi):開(kāi)放繼承,允許任意類(lèi)繼承
    public static non-sealed class Bird extends Animal {
        @Override
        public String makeSound() {
            return "嘰嘰喳喳";
        }
    }
    // 密封接口:僅允許 Apple、Banana、Orange 實(shí)現(xiàn)
    public static sealed interface Fruit permits Apple, Banana, Orange {
        String getColor();
    }
    public static final class Apple implements Fruit {
        @Override
        public String getColor() {
            return "紅色";
        }
    }
    public static final class Banana implements Fruit {
        @Override
        public String getColor() {
            return "黃色";
        }
    }
    public static non-sealed class Orange implements Fruit {
        @Override
        public String getColor() {
            return "橙色";
        }
    }
    // 結(jié)合switch實(shí)現(xiàn)窮盡性檢查
    // 調(diào)整匹配順序(先匹配Puppy,再匹配Dog),讓編譯器識(shí)別窮盡性
    private static void printAnimalType(Animal animal) {
        String type = switch (animal) {
            case Cat ignore -> "貓科動(dòng)物";
            case Puppy ignore -> "犬科動(dòng)物(小狗)"; // 先匹配Puppy(Dog的子類(lèi))
            case Dog ignore -> "犬科動(dòng)物(成年犬)";  // 再匹配普通Dog
            case Bird ignore -> "鳥(niǎo)類(lèi)動(dòng)物";
        };
        System.out.println("動(dòng)物類(lèi)型:" + type);
    }
}

4.3 運(yùn)行結(jié)果

貓的叫聲:喵喵喵
狗的叫聲:汪汪汪
小狗的叫聲:?jiǎn)鑶鑶瑁ㄐ」方校?br />鳥(niǎo)的叫聲:嘰嘰喳喳
蘋(píng)果的顏色:紅色
香蕉的顏色:黃色
橙子的顏色:橙色
動(dòng)物類(lèi)型:貓科動(dòng)物
動(dòng)物類(lèi)型:犬科動(dòng)物(成年犬)
動(dòng)物類(lèi)型:犬科動(dòng)物(小狗)
動(dòng)物類(lèi)型:鳥(niǎo)類(lèi)動(dòng)物

4.4 注意事項(xiàng)

  • 密封類(lèi)的 permits 關(guān)鍵字后,必須列出所有允許繼承的子類(lèi),不能遺漏。
  • 密封類(lèi)不能是 final 的(final 類(lèi)不可繼承,與密封類(lèi)的設(shè)計(jì)初衷沖突)。
  • 非密封子類(lèi)(non-sealed)可以被任意類(lèi)繼承,打破密封類(lèi)的限制,適合需要靈活擴(kuò)展的場(chǎng)景。
  • 密封類(lèi)與 switch 模式匹配配合時(shí),編譯器會(huì)自動(dòng)檢查是否覆蓋所有子類(lèi),避免遺漏分支。
  • 密封接口的實(shí)現(xiàn)類(lèi)同樣需要遵循修飾符規(guī)則,確保接口的實(shí)現(xiàn)范圍可控。

5. 分代 ZGC(Generational ZGC,JEP 439)—— 提升 GC 性能

5.1 特性說(shuō)明

ZGC 是 Java 推出的低延遲垃圾收集器,JDK21 正式將分代 ZGC 從實(shí)驗(yàn)特性轉(zhuǎn)為正式特性(JEP 439 Final 狀態(tài)),其核心是將堆內(nèi)存分為年輕代和老年代,針對(duì)不同代的對(duì)象采用差異化收集策略:

  • 年輕代對(duì)象生命周期短,采用頻繁、快速的收集策略;
  • 老年代對(duì)象生命周期長(zhǎng),采用低頻率、高效率的收集策略。

這一優(yōu)化極大提升了垃圾收集的吞吐量和響應(yīng)速度,尤其適合大堆內(nèi)存場(chǎng)景(如 10GB 以上堆內(nèi)存)。

核心優(yōu)勢(shì):

  • 低延遲:GC 停頓時(shí)間控制在毫秒級(jí)甚至亞毫秒級(jí),幾乎不影響程序正常運(yùn)行;
  • 高吞吐量:分代收集減少了不必要的垃圾掃描范圍,提升垃圾收集效率;
  • 大堆支持:64 位系統(tǒng)下最大支持 16TB 堆內(nèi)存(設(shè)計(jì)上限),可輕松應(yīng)對(duì)大內(nèi)存應(yīng)用場(chǎng)景;
  • 并發(fā)處理:年輕代和老年代的核心垃圾收集流程均為并發(fā)執(zhí)行,應(yīng)用線程無(wú)需長(zhǎng)時(shí)間阻塞;
  • 動(dòng)態(tài)內(nèi)存布局:可根據(jù)應(yīng)用實(shí)際內(nèi)存需求自動(dòng)調(diào)整年輕代 / 老年代的大??;
  • 無(wú)縫遷移:無(wú)需修改業(yè)務(wù)代碼,僅通過(guò) JVM 參數(shù)即可啟用,無(wú)侵入性。

官方規(guī)劃:OpenJDK 計(jì)劃在 JDK25 及后續(xù)版本中將分代 ZGC 設(shè)為默認(rèn)垃圾收集器,后續(xù)會(huì)逐步移除非分代 ZGC 實(shí)現(xiàn),ZGenerational 選項(xiàng)也將不再需要手動(dòng)配置。

5.2 啟用方式(JVM 參數(shù))

# 啟用分代 ZGC(JDK21 正式特性,無(wú)需解鎖實(shí)驗(yàn)性選項(xiàng))
-XX:+UseZGC -XX:+ZGenerational
# 可選參數(shù)(優(yōu)化大堆場(chǎng)景)
-XX:ZHeapSize=10G        # 設(shè)置堆內(nèi)存總大小為10G
-XX:MaxGCPauseMillis=10  # 建議最大GC停頓時(shí)間(ZGC 會(huì)盡力滿足,非強(qiáng)制)
-XX:ZYoungGenSize=2G     # 設(shè)置年輕代初始大小為2G(默認(rèn)自動(dòng)動(dòng)態(tài)調(diào)整,無(wú)需手動(dòng)配置)

5.3 實(shí)際應(yīng)用示例

# 運(yùn)行Spring Boot應(yīng)用并啟用分代ZGC(JDK21+)
java -XX:+UseZGC -XX:+ZGenerational -XX:ZHeapSize=20G -jar your-app.jar
# 進(jìn)階配置(指定年輕代大小+停頓時(shí)間目標(biāo))
java -XX:+UseZGC -XX:+ZGenerational -XX:ZHeapSize=32G -XX:ZYoungGenSize=4G -XX:MaxGCPauseMillis=5 -jar your-app.jar

5.4 注意事項(xiàng)

  • 版本要求:分代 ZGC 僅在 JDK21 及以上版本支持,JDK21 之前的 ZGC 無(wú)分代收集能力;
  • 排他性:?jiǎn)⒂梅执?ZGC 后,不可同時(shí)啟用 G1、Shenandoah 等其他垃圾收集器,避免 JVM 參數(shù)沖突;
  • 場(chǎng)景適配:適合大堆內(nèi)存場(chǎng)景(堆內(nèi)存 ≥ 4GB),小堆內(nèi)存場(chǎng)景(<4GB)使用 G1 收集器性價(jià)比更高;
  • 參數(shù)原則:分代 ZGC 會(huì)自動(dòng)動(dòng)態(tài)調(diào)整年輕代 / 老年代大小,除非有明確的性能調(diào)優(yōu)需求,否則無(wú)需手動(dòng)配置 ZYoungGenSize
  • 技術(shù)依賴(lài):依賴(lài)染色指針(Colored Pointers)和讀屏障(Load Barriers)技術(shù),64 位主流操作系統(tǒng) / 虛擬機(jī)環(huán)境默認(rèn)已啟用,無(wú)需額外配置;
  • 堆上限:ZGC 設(shè)計(jì)最大堆大小為 16TB(JDK21),實(shí)際可用上限受操作系統(tǒng)、硬件物理內(nèi)存限制,無(wú)法突破 16TB。

總結(jié)

  1. 核心參數(shù)修正:
    • 移除多余的 -XX:+UnlockExperimentalVMOptions(JDK21 分代 ZGC 是正式特性,無(wú)需解鎖);
    • 錯(cuò)誤參數(shù) ZYoungGenerationSize → 正確參數(shù) ZYoungGenSizeGenGeneration 的官方縮寫(xiě),JVM 僅識(shí)別縮寫(xiě)形式);
    • ZGC 最大堆上限為 16TB(非 “16TB 以上”)。
  2. 啟用原則:優(yōu)先使用默認(rèn)參數(shù),僅在大堆場(chǎng)景(≥4GB)手動(dòng)調(diào)整堆總大?。?code>ZHeapSize),年輕代大?。?code>ZYoungGenSize)無(wú)需主動(dòng)配置。
  3. 關(guān)鍵驗(yàn)證:若在 JVM 啟動(dòng)時(shí)傳入 ZYoungGenerationSize,會(huì)直接提示 Unrecognized VM option 'ZYoungGenerationSize',而 ZYoungGenSize 是 JDK21 分代 ZGC 的官方標(biāo)準(zhǔn)參數(shù)。

6. 序列化集合(Sequenced Collections,JEP 431)—— 有序集合標(biāo)準(zhǔn)化

6.1 特性說(shuō)明

  • JDK21 正式引入 Sequenced Collections(序列化集合,又稱(chēng)有序集合),這是一種具有確定出現(xiàn)順序(encounter order)的集合,無(wú)論遍歷多少次,元素的出現(xiàn)順序始終固定。它提供了統(tǒng)一的接口和方法來(lái)處理集合的首尾元素、反向視圖等,解決了以往不同集合(List、Set、Map)處理順序操作的 API 不一致問(wèn)題。
  • 核心優(yōu)勢(shì):
    • 核心接口關(guān)系:
    • 統(tǒng)一接口:提供 SequencedCollection、SequencedSet、SequencedMap 三大核心接口,標(biāo)準(zhǔn)化順序操作。
    • 有序性保證:元素插入順序與遍歷順序一致,支持首尾元素快速訪問(wèn)。
  • 線程安全:部分實(shí)現(xiàn)(如 ConcurrentLinkedDeque)通過(guò) Sequenced Locks 機(jī)制實(shí)現(xiàn)線程安全,支持多線程并發(fā)讀寫(xiě)。
  • 可擴(kuò)展性:設(shè)計(jì)考慮了大量數(shù)據(jù)的并發(fā)訪問(wèn),適合高并發(fā)場(chǎng)景。
  • 反向視圖:通過(guò) reversed() 方法快速獲取反向順序的集合視圖,無(wú)需手動(dòng)反轉(zhuǎn)。
    • SequencedCollection:繼承 Collection,基礎(chǔ)有序集合接口。
    • SequencedSet:繼承 SequencedCollectionSet,有序不重復(fù)集合接口。
    • SequencedMap:繼承 Map,有序鍵值對(duì)映射接口。

6.2 代碼樣例(三大接口完整演示)

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.SequencedSet;
/**
 * 有序集合(Sequenced Collections)三大接口演示
 * 注意:使用LinkedList(而非ArrayList),因?yàn)槠渲С謅ddFirst()/addLast()
 */
public class SequencedCollectionsDemo {
    public static void main(String[] args) {
        // 場(chǎng)景1:SequencedCollection 演示(以 LinkedList 為例,支持addFirst/addLast)
        System.out.println("=== 場(chǎng)景1:SequencedCollection 演示 ===");
        SequencedCollection<Integer> collection = new LinkedList<>();
        collection.add(1);
        collection.addFirst(0); // 添加到頭部(LinkedList支持,ArrayList不支持)
        collection.addLast(2); // 添加到尾部
        System.out.println("集合元素:" + collection);
        System.out.println("第一個(gè)元素:" + collection.getFirst());
        System.out.println("最后一個(gè)元素:" + collection.getLast());
        SequencedCollection<Integer> reversedCollection = collection.reversed();
        System.out.println("反向集合:" + reversedCollection);
        collection.removeFirst(); // 刪除頭部元素
        collection.removeLast(); // 刪除尾部元素
        System.out.println("刪除首尾后:" + collection);
        // 場(chǎng)景2:SequencedSet 演示(以 LinkedHashSet 為例)
        System.out.println("\n=== 場(chǎng)景2:SequencedSet 演示 ===");
        SequencedSet<Integer> set = new LinkedHashSet<>();
        set.add(3);
        set.add(1);
        set.add(2);
        set.addFirst(0); // 添加到頭部
        set.addLast(4); // 添加到尾部
        System.out.println("Set元素(有序不重復(fù)):" + set);
        System.out.println("第一個(gè)元素:" + set.getFirst());
        System.out.println("最后一個(gè)元素:" + set.getLast());
        SequencedSet<Integer> reversedSet = set.reversed();
        System.out.println("反向Set:" + reversedSet);
        // 場(chǎng)景3:SequencedMap 演示(以 LinkedHashMap 為例)
        System.out.println("\n=== 場(chǎng)景3:SequencedMap 演示 ===");
        SequencedMap<Integer, String> map = new LinkedHashMap<>();
        map.put(1, "Apple");
        map.put(2, "Banana");
        map.put(3, "Orange");
        map.putFirst(0, "Grape"); // 添加到頭部
        map.putLast(4, "Mango"); // 添加到尾部
        System.out.println("Map元素:" + map);
        System.out.println("第一個(gè)鍵值對(duì):" + map.firstEntry());
        System.out.println("最后一個(gè)鍵值對(duì):" + map.lastEntry());
        // 獲取有序的key、value、entry集合
        System.out.println("有序key集合:" + map.sequencedKeySet());
        System.out.println("有序value集合:" + map.sequencedValues());
        System.out.println("有序entry集合:" + map.sequencedEntrySet());
        // 移除首尾鍵值對(duì)
        map.pollFirstEntry(); // 移除并返回第一個(gè)鍵值對(duì)
        map.pollLastEntry(); // 移除并返回最后一個(gè)鍵值對(duì)
        System.out.println("移除首尾后:" + map);
        // 反向Map
        SequencedMap<Integer, String> reversedMap = map.reversed();
        System.out.println("反向Map:" + reversedMap);
    }
}

6.3 運(yùn)行結(jié)果

=== 場(chǎng)景1:SequencedCollection 演示 ===
集合元素:[0, 1, 2]
第一個(gè)元素:0
最后一個(gè)元素:2
反向集合:[2, 1, 0]
刪除首尾后:[1]

=== 場(chǎng)景2:SequencedSet 演示 ===
Set元素(有序不重復(fù)):[0, 3, 1, 2, 4]
第一個(gè)元素:0
最后一個(gè)元素:4
反向Set:[4, 2, 1, 3, 0]

=== 場(chǎng)景3:SequencedMap 演示 ===
Map元素:{0=Grape, 1=Apple, 2=Banana, 3=Orange, 4=Mango}
第一個(gè)鍵值對(duì):0=Grape
最后一個(gè)鍵值對(duì):4=Mango
有序key集合:[0, 1, 2, 3, 4]
有序value集合:[Grape, Apple, Banana, Orange, Mango]
有序entry集合:[0=Grape, 1=Apple, 2=Banana, 3=Orange, 4=Mango]
移除首尾后:{1=Apple, 2=Banana, 3=Orange}
反向Map:{3=Orange, 2=Banana, 1=Apple}

6.4 注意事項(xiàng)

實(shí)現(xiàn)類(lèi)說(shuō)明:

  • List 接口繼承 SequencedCollectionLinkedList 重寫(xiě) addFirst()/addLast() 可正常使用,ArrayList 的同名方法拋 UnsupportedOperationException);
  • Deque 接口繼承 SequencedCollection(如 ConcurrentLinkedDeque、ArrayDeque);
  • LinkedHashSet、TreeSet(實(shí)現(xiàn) NavigableSet)實(shí)現(xiàn) SequencedSet;
  • LinkedHashMap、TreeMap(實(shí)現(xiàn) NavigableMap)實(shí)現(xiàn) SequencedMap

線程安全:SequencedCollections 接口本身不保證線程安全,需使用線程安全的實(shí)現(xiàn)類(lèi)(如 ConcurrentLinkedDeque)或手動(dòng)加鎖;ConcurrentLinkedDeque 的線程安全基于 CAS 機(jī)制實(shí)現(xiàn),無(wú) “Sequenced Locks” 概念。

反向視圖:reversed() 方法返回的是原集合的反向視圖(而非新集合),修改原集合會(huì)同步影響反向視圖,反之亦然。

空集合處理:調(diào)用空集合的 getFirst()、getLast() 方法會(huì)拋出 NoSuchElementException,需提前通過(guò) isEmpty() 判斷集合是否為空。

與傳統(tǒng)方法對(duì)比:

  • addFirst()/addLast() 是對(duì) Deque 同名方法的標(biāo)準(zhǔn)化擴(kuò)展(覆蓋到所有 List),而非 “替代”;
  • sequencedKeySet()Map.keySet()有序增強(qiáng)版(返回 SequencedSet),二者共存,并非 “替代”。

二、預(yù)覽特性(需啟用 --enable-preview 參數(shù))

預(yù)覽特性是 JDK 中已實(shí)現(xiàn)但尚未正式定型的特性,可能在未來(lái)版本中優(yōu)化或調(diào)整,使用時(shí)需在編譯和運(yùn)行時(shí)添加 --enable-preview 參數(shù)(具體用法見(jiàn)每個(gè)特性的說(shuō)明)。JDK21 中有多個(gè)實(shí)用預(yù)覽特性,以下重點(diǎn)講解最常用的 5 個(gè)。

1. 字符串模板(String Templates,JEP 430)—— 簡(jiǎn)化字符串拼接

1.1 特性說(shuō)明

  • 字符串模板是 JDK21 的預(yù)覽特性,類(lèi)似 Python 的 f-string、JavaScript 的模板字符串,允許在字符串中直接嵌入表達(dá)式,無(wú)需使用 + 拼接或 String.format(),支持三種內(nèi)置模板處理器,同時(shí)支持自定義處理器,解決了傳統(tǒng)字符串拼接繁瑣、易出錯(cuò)、可讀性差的問(wèn)題。
  • 核心優(yōu)勢(shì):
  • 內(nèi)置模板處理器:
    • 簡(jiǎn)潔高效:直接嵌入表達(dá)式,無(wú)需拼接,代碼更簡(jiǎn)潔。
    • 類(lèi)型安全:表達(dá)式類(lèi)型自動(dòng)適配,無(wú)需手動(dòng)轉(zhuǎn)換。
    • 支持格式化:通過(guò) FMT 處理器支持格式化說(shuō)明符(如 %.2f、%-10s)。
    • 支持自定義:可實(shí)現(xiàn) StringTemplate.Processor 接口創(chuàng)建自定義處理器。
    • 多行支持:支持多行字符串模板,保留換行和縮進(jìn)格式。
    • 表達(dá)式靈活:支持變量、方法調(diào)用、復(fù)雜計(jì)算、注釋等多種表達(dá)式形式。
    • STR:基礎(chǔ)處理器,自動(dòng)執(zhí)行字符串插值,將表達(dá)式替換為字符串值。
    • FMT:格式化處理器,支持格式化說(shuō)明符(如 %.2f、%-10s)。
    • RAW:原始處理器,返回 StringTemplate 對(duì)象,不自動(dòng)處理插值,用于自定義處理。

1.2 代碼樣例(完整用法演示)

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
 * 字符串模板(預(yù)覽特性)完整用法演示
 * 編譯命令:javac --enable-preview --release 21 StringTemplateDemo.java
 * 運(yùn)行命令:java --enable-preview StringTemplateDemo
 */
public class StringTemplateDemo {
    // 測(cè)試方法:用于表達(dá)式調(diào)用
    private static String getName() {
        return "Java開(kāi)發(fā)者";
    }
    // 自定義模板處理器:轉(zhuǎn)換為大寫(xiě)格式(兼容null參數(shù))
    private static class UpperCaseProcessor implements StringTemplate.Processor<String, RuntimeException> {
        @Override
        public String process(StringTemplate st) {
            // 拼接模板文本和表達(dá)式值,轉(zhuǎn)換為大寫(xiě)
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < st.fragments().size(); i++) {
                sb.append(st.fragments().get(i));
                if (i < st.arguments().size()) {
                    Object arg = st.arguments().get(i);
                    // 兼容null參數(shù),避免空指針
                    sb.append(arg == null ? "null" : arg.toString().toUpperCase());
                }
            }
            return sb.toString();
        }
    }
    public static void main(String[] args) {
        // 基礎(chǔ)信息
        String name = "Alice";
        int age = 30;
        double salary = 15000.50;
        String userInput = "<script>alert('攻擊')</script>"; // 修正:設(shè)置有注入風(fēng)險(xiǎn)的輸入
        // 場(chǎng)景1:STR處理器(基礎(chǔ)插值)
        String info1 = STR."姓名:\{name},年齡:\{age},薪資:\{salary}";
        System.out.println("場(chǎng)景1:STR基礎(chǔ)插值 - " + info1);
        // 場(chǎng)景2:STR處理器(表達(dá)式調(diào)用:變量、方法、計(jì)算)
        String info2 = STR."靜態(tài)變量:\{name},方法調(diào)用:\{getName()},計(jì)算結(jié)果:\{10 + 20 == 30 ? "正確" : "錯(cuò)誤"}";
        System.out.println("場(chǎng)景2:STR表達(dá)式調(diào)用 - " + info2);
        // 場(chǎng)景3:FMT處理器(格式化)
        String info3 = FMT."姓名:%-10s\{name},年齡:%03d\{age},薪資:%.2f\{salary}";
        System.out.println("場(chǎng)景3:FMT格式化 - " + info3);
        // 場(chǎng)景4:RAW處理器(原始模板,自定義處理)
        StringTemplate rawTemplate = RAW."姓名:\{name},年齡:\{age}";
        System.out.println("場(chǎng)景4:RAW原始文本 - " + rawTemplate.text());
        System.out.println("場(chǎng)景4:RAW插值后(STR處理) - " + STR.process(rawTemplate));
        // 場(chǎng)景5:多行字符串模板
        String info4 = STR."""
                個(gè)人信息:
                姓名:\{name}
                年齡:\{age}歲
                薪資:\{salary}元/月
                當(dāng)前時(shí)間:\{
                    // 多行表達(dá)式支持注釋
                    DateTimeFormatter.ofPattern("HH:mm:ss")
                                    .format(LocalTime.now())
                }
                """;
        System.out.println("場(chǎng)景5:多行模板 - " + info4);
        // 場(chǎng)景6:自定義模板處理器(修正調(diào)用語(yǔ)法)
        UpperCaseProcessor upperProcessor = new UpperCaseProcessor();
        StringTemplate upperTemplate = STR."姓名:\{name},年齡:\{age}";
        String info5 = upperProcessor.process(upperTemplate);
        System.out.println("場(chǎng)景6:自定義處理器(大寫(xiě)) - " + info5);
        // 場(chǎng)景7:安全說(shuō)明:STR/FMT不自動(dòng)轉(zhuǎn)義,防注入需自定義處理器
        String unsafeInfo = STR."用戶輸入:\{userInput}";
        System.out.println("場(chǎng)景7:未轉(zhuǎn)義輸入 - " + unsafeInfo);
    }
}

1.3 運(yùn)行結(jié)果

場(chǎng)景1:STR基礎(chǔ)插值 - 姓名:Alice,年齡:30,薪資:15000.5
場(chǎng)景2:STR表達(dá)式調(diào)用 - 靜態(tài)變量:Alice,方法調(diào)用:Java開(kāi)發(fā)者,計(jì)算結(jié)果:正確
場(chǎng)景3:FMT格式化 - 姓名:Alice     ,年齡:030,薪資:15000.50
場(chǎng)景4:RAW原始文本 - 姓名:\{name},年齡:\{age}
場(chǎng)景4:RAW插值后(STR處理) - 姓名:Alice,年齡:30
場(chǎng)景5:多行模板 - 個(gè)人信息:
姓名:Alice
年齡:30歲
薪資:15000.5元/月
當(dāng)前時(shí)間:14:35:22

場(chǎng)景6:自定義處理器(大寫(xiě)) - 姓名:ALICE,年齡:30
場(chǎng)景7:未轉(zhuǎn)義輸入 - 用戶輸入:<script>alert('攻擊')</script>

1.4 注意事項(xiàng)

  • 啟用方式:必須在編譯和運(yùn)行時(shí)添加 --enable-preview --release 21 參數(shù)(JDK22/23 同理),否則會(huì)提示 “字符串模板是預(yù)覽特性,需啟用預(yù)覽模式”;
  • 安全說(shuō)明:STR 和 FMT 處理器不會(huì)自動(dòng)轉(zhuǎn)義特殊字符(如 <>/'/"),處理用戶輸入時(shí)需自定義處理器實(shí)現(xiàn) XSS / 注入防護(hù);
  • 多行模板:使用 “”" 包裹,保留換行和縮進(jìn)格式,表達(dá)式嵌入方式與單行一致,多行表達(dá)式內(nèi)可寫(xiě)注釋?zhuān)?/li>
  • 自定義處理器:
    • 實(shí)現(xiàn) StringTemplate.Processor 接口的 process 方法,入?yún)?StringTemplate(包含模板文本片段 fragments() 和表達(dá)式參數(shù) arguments());
    • 調(diào)用時(shí)需先通過(guò) STR/RAW 創(chuàng)建 StringTemplate 對(duì)象,再傳入處理器的 process() 方法;
    • 需處理參數(shù)為 null 的情況,避免空指針異常;
  • 表達(dá)式限制:模板中的表達(dá)式不能包含未聲明的變量,表達(dá)式執(zhí)行拋出的異常會(huì)直接傳播,需手動(dòng)捕獲處理;
  • 版本說(shuō)明:JDK21/22/23 中 String Templates 均為預(yù)覽特性,正式版尚未發(fā)布,生產(chǎn)環(huán)境慎用。

2. 結(jié)構(gòu)化并發(fā)(Structured Concurrency,JEP 453)—— 簡(jiǎn)化并發(fā)任務(wù)管理

2.1 特性說(shuō)明

結(jié)構(gòu)化并發(fā)是 JDK21 的預(yù)覽特性(JDK23 正式轉(zhuǎn)正),旨在簡(jiǎn)化多線程并發(fā)任務(wù)的管理,解決傳統(tǒng)并發(fā)編程中線程泄漏、任務(wù)間依賴(lài)混亂、異常處理繁瑣等問(wèn)題。它將一組相關(guān)的并發(fā)任務(wù)視為一個(gè)整體,統(tǒng)一管理生命周期(啟動(dòng)、執(zhí)行、取消、終止),確保所有子任務(wù)完成后主線程再繼續(xù)執(zhí)行,同時(shí)支持異常統(tǒng)一捕獲和任務(wù)取消擴(kuò)散,讓并發(fā)代碼更具可讀性、可維護(hù)性和安全性。
核心優(yōu)勢(shì):

  • 生命周期統(tǒng)一:子任務(wù)與父任務(wù)綁定,父任務(wù)取消 / 終止時(shí),所有子任務(wù)自動(dòng)取消,避免線程泄漏;
  • 異常集中處理:子任務(wù)拋出的異常會(huì)被收集到 scope 中,可統(tǒng)一獲取和處理,無(wú)需單獨(dú)捕獲;
  • 任務(wù)依賴(lài)清晰:通過(guò)結(jié)構(gòu)化層級(jí)明確任務(wù)間的父子關(guān)系,避免并發(fā)任務(wù)邏輯混亂;
  • 與虛擬線程兼容:可靈活搭配虛擬線程使用,輕松管理大量并發(fā)子任務(wù),提升高并發(fā)場(chǎng)景性能;
  • 取消擴(kuò)散:父任務(wù)取消時(shí),會(huì)自動(dòng)中斷所有未完成的子任務(wù),避免資源浪費(fèi);
  • 自動(dòng)資源管理:結(jié)合 try-with-resources 語(yǔ)法,確保 scope 自動(dòng)關(guān)閉,杜絕線程泄漏。

2.2 代碼樣例(基礎(chǔ)使用 + 異常處理 + 取消任務(wù))

import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
 * 結(jié)構(gòu)化并發(fā)(預(yù)覽特性)演示
 * 編譯命令:javac --enable-preview --release 21 StructuredConcurrencyDemo.java
 * 運(yùn)行命令:java --enable-preview StructuredConcurrencyDemo
 */
public class StructuredConcurrencyDemo {
    public static void main(String[] args) throws InterruptedException {
        // 場(chǎng)景1:基礎(chǔ)結(jié)構(gòu)化并發(fā)(父子任務(wù)綁定)
        System.out.println("=== 場(chǎng)景1:基礎(chǔ)結(jié)構(gòu)化并發(fā) ===");
        try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
            // 提交子任務(wù)1(虛擬線程需顯式指定線程工廠,默認(rèn)平臺(tái)線程)
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(100);
                return "子任務(wù)1執(zhí)行完成";
            });
            // 提交子任務(wù)2
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(200);
                return "子任務(wù)2執(zhí)行完成";
            });
            // 等待子任務(wù)完成(ShutdownOnSuccess:任一成功則關(guān)閉其他任務(wù))
            scope.join();
            // 獲取成功完成的子任務(wù)結(jié)果(第一個(gè)成功的)
            System.out.println("成功結(jié)果:" + scope.result());
        } // try-with-resources 自動(dòng)關(guān)閉scope
        // 場(chǎng)景2:異常處理(子任務(wù)拋出異常,父任務(wù)統(tǒng)一捕獲)
        System.out.println("\n=== 場(chǎng)景2:異常處理 ===");
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 子任務(wù)3:拋出異常
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(50);
                System.out.println("子任務(wù)3執(zhí)行中");
                throw new RuntimeException("子任務(wù)3執(zhí)行失敗");
            });
            // 子任務(wù)4:因子任務(wù)3失敗,會(huì)被提前終止,不會(huì)執(zhí)行到打印
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(150);
                System.out.println("子任務(wù)4執(zhí)行完成");
                return "子任務(wù)4成功";
            });
            try {
                scope.join(); // 等待,ShutdownOnFailure:任一失敗則關(guān)閉
            } catch (ExecutionException e) {
                // 捕獲封裝的異常,實(shí)際異常在scope.failures()中
                System.out.println("捕獲到執(zhí)行異常:" + e.getMessage());
            }
            // 遍歷所有子任務(wù)的異常(核心:從failures()獲?。?
            scope.failures().forEach(ex -> System.out.println("子任務(wù)異常:" + ex.getMessage()));
        }
        // 場(chǎng)景3:手動(dòng)取消任務(wù)
        System.out.println("\n=== 場(chǎng)景3:手動(dòng)取消任務(wù) ===");
        try (var scope = new StructuredTaskScope<String>()) {
            // 子任務(wù)5:耗時(shí)任務(wù)
            var future1 = scope.fork(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                    return "子任務(wù)5執(zhí)行完成";
                } catch (InterruptedException e) {
                    System.out.println("子任務(wù)5被取消");
                    return null;
                }
            });
            // 子任務(wù)6:快速完成
            var future2 = scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(100);
                return "子任務(wù)6執(zhí)行完成";
            });
            // 等待150ms,或所有子任務(wù)完成
            try {
                scope.joinUntil(System.currentTimeMillis() + 150);
                if (future2.isDone()) {
                    System.out.println("子任務(wù)6結(jié)果:" + future2.resultNow());
                    scope.shutdown(); // 觸發(fā)子任務(wù)中斷(子任務(wù)5)
                }
            } catch (TimeoutException e) {
                System.out.println("等待超時(shí)");
            }
        }
    }
}

2.3 運(yùn)行結(jié)果

=== 場(chǎng)景1:基礎(chǔ)結(jié)構(gòu)化并發(fā) ===
成功結(jié)果:子任務(wù)1執(zhí)行完成

=== 場(chǎng)景2:異常處理 ===
子任務(wù)3執(zhí)行中
捕獲到執(zhí)行異常:java.lang.RuntimeException: 子任務(wù)3執(zhí)行失敗
子任務(wù)異常:子任務(wù)3執(zhí)行失敗

=== 場(chǎng)景3:手動(dòng)取消任務(wù) ===
子任務(wù)6結(jié)果:子任務(wù)6執(zhí)行完成
子任務(wù)5被取消

2.4 注意事項(xiàng)

  • 啟用方式:需在編譯和運(yùn)行時(shí)添加 --enable-preview --release 21 參數(shù)(JDK22/23 同理,JDK23 該特性已正式發(fā)布,無(wú)需預(yù)覽參數(shù));
  • scope 生命周期:必須使用 try-with-resources 語(yǔ)法,確保自動(dòng)關(guān)閉(觸發(fā)未完成子任務(wù)中斷);手動(dòng)關(guān)閉需調(diào)用 shutdown(),但仍建議配合 try-with-resources;
  • 核心 scope 實(shí)現(xiàn):
  • 線程模型:fork() 方法默認(rèn)使用 ForkJoinPool.commonPool()(平臺(tái)線程),若要使用虛擬線程,需顯式指定線程工廠:scope.fork(() -> {...}, Thread.ofVirtual().factory());
  • 異常處理:
    • 取消機(jī)制:shutdown() 僅標(biāo)記 scope 為關(guān)閉狀態(tài),子任務(wù)需通過(guò)捕獲 InterruptedException 響應(yīng)取消(如場(chǎng)景 3 的子任務(wù) 5)。
    • ShutdownOnSuccess:任一子任務(wù)成功則立即關(guān)閉其他任務(wù),優(yōu)先返回首個(gè)成功結(jié)果;
    • ShutdownOnFailure:任一子任務(wù)失敗則立即關(guān)閉其他任務(wù),失敗信息存入 failures()
    • StructuredTaskScope:基礎(chǔ)實(shí)現(xiàn),需手動(dòng)控制關(guān)閉 / 取消,靈活性最高;
  • join() 可能拋出 InterruptedException(線程中斷)、ExecutionException(子任務(wù)異常封裝);
  • 所有子任務(wù)的原始異常需通過(guò) scope.failures() 獲取,而非直接捕獲 join() 的異常;

3. 向量 API(Vector API,JEP 448)—— 提升數(shù)值計(jì)算性能

3.1 特性說(shuō)明

向量 API 是 JDK21 的孵化特性(Incubator)(經(jīng) JDK16-JDK20 多輪孵化優(yōu)化),旨在提供一套高效的數(shù)值計(jì)算 API,充分利用 CPU 的向量指令(如 AVX2、AVX512、NEON),實(shí)現(xiàn)單指令多數(shù)據(jù)(SIMD)并行計(jì)算,大幅提升數(shù)組、矩陣等數(shù)值操作的性能(尤其適合科學(xué)計(jì)算、機(jī)器學(xué)習(xí)、信號(hào)處理等場(chǎng)景)。它提供類(lèi)型安全的向量操作,避免手動(dòng)編寫(xiě)匯編代碼,同時(shí)支持跨平臺(tái),無(wú)需關(guān)注底層 CPU 架構(gòu)差異。
核心優(yōu)勢(shì):

  • 高性能:直接映射 CPU 向量指令,SIMD 并行計(jì)算,比傳統(tǒng)循環(huán)快數(shù)倍(大規(guī)模計(jì)算場(chǎng)景);
  • 類(lèi)型安全:強(qiáng)類(lèi)型向量接口,編譯時(shí)檢查操作合法性,避免類(lèi)型錯(cuò)誤;
  • 跨平臺(tái):自動(dòng)適配 x86、ARM 等不同 CPU 架構(gòu),無(wú)需修改代碼;
  • 易用性:API 設(shè)計(jì)簡(jiǎn)潔,支持向量加減乘除、邏輯運(yùn)算、聚合等常用操作;
  • 自適應(yīng):支持 SPECIES_PREFERRED 自動(dòng)適配當(dāng)前 CPU 最優(yōu)向量長(zhǎng)度。

3.2 代碼樣例(向量計(jì)算 + 性能對(duì)比)

import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorSpecies;
import java.util.Arrays;
/**
 * 向量API(孵化特性)演示 + 性能對(duì)比
 * 編譯命令:javac --release 21 --add-modules jdk.incubator.vector VectorAPIDemo.java
 * 運(yùn)行命令:java --add-modules jdk.incubator.vector VectorAPIDemo
 */
public class VectorAPIDemo {
    // 定義向量物種:自動(dòng)適配當(dāng)前CPU最優(yōu)長(zhǎng)度(推薦),float類(lèi)型
    private static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
    // 也可手動(dòng)指定:FloatVector.SPECIES_128(128位,4個(gè)float)、FloatVector.SPECIES_256(256位,8個(gè)float)
    // 傳統(tǒng)循環(huán)計(jì)算:兩個(gè)float數(shù)組相加
    public static void arrayAddTraditional(float[] a, float[] b, float[] result) {
        for (int i = 0; i < a.length; i++) {
            result[i] = a[i] + b[i];
        }
    }
    // 向量API計(jì)算:兩個(gè)float數(shù)組相加(修正length()為length)
    public static void arrayAddVector(float[] a, float[] b, float[] result) {
        int i = 0;
        // 向量批量處理(每次處理SPECIES.length個(gè)元素)
        int upperBound = SPECIES.loopBound(a.length);
        for (; i < upperBound; i += SPECIES.length) { // 修正:length(屬性)而非length()
            // 從數(shù)組中加載向量
            FloatVector va = FloatVector.fromArray(SPECIES, a, i);
            FloatVector vb = FloatVector.fromArray(SPECIES, b, i);
            // 向量相加,結(jié)果存儲(chǔ)到數(shù)組
            va.add(vb).intoArray(result, i);
        }
        // 處理剩余不足一個(gè)向量長(zhǎng)度的元素
        for (; i < a.length; i++) {
            result[i] = a[i] + b[i];
        }
    }
    public static void main(String[] args) {
        // 初始化兩個(gè)大數(shù)組(1000萬(wàn)元素)
        int size = 10_000_000;
        float[] a = new float[size];
        float[] b = new float[size];
        float[] resultTraditional = new float[size];
        float[] resultVector = new float[size];
        // 填充數(shù)據(jù)(固定值,避免隨機(jī)數(shù)影響性能)
        Arrays.fill(a, 1.23f);
        Arrays.fill(b, 4.56f);
        // JIT預(yù)熱:先運(yùn)行一次,避免編譯開(kāi)銷(xiāo)影響性能測(cè)試
        arrayAddTraditional(a, b, resultTraditional);
        arrayAddVector(a, b, resultVector);
        // 測(cè)試傳統(tǒng)循環(huán)性能(多次運(yùn)行取平均)
        long start = System.currentTimeMillis();
        for (int j = 0; j < 5; j++) {
            arrayAddTraditional(a, b, resultTraditional);
        }
        long traditionalTime = (System.currentTimeMillis() - start) / 5;
        System.out.println("傳統(tǒng)循環(huán)平均耗時(shí):" + traditionalTime + "ms");
        // 測(cè)試向量API性能(多次運(yùn)行取平均)
        start = System.currentTimeMillis();
        for (int j = 0; j < 5; j++) {
            arrayAddVector(a, b, resultVector);
        }
        long vectorTime = (System.currentTimeMillis() - start) / 5;
        System.out.println("向量API平均耗時(shí):" + vectorTime + "ms");
        // 驗(yàn)證結(jié)果一致性
        boolean isEqual = Arrays.equals(resultTraditional, resultVector);
        System.out.println("結(jié)果是否一致:" + isEqual);
        // 打印向量物種信息(當(dāng)前CPU適配的長(zhǎng)度)
        System.out.println("當(dāng)前CPU最優(yōu)向量長(zhǎng)度(float元素?cái)?shù)):" + SPECIES.length);
    }
}

3.3 運(yùn)行結(jié)果

傳統(tǒng)循環(huán)平均耗時(shí):30ms
向量API平均耗時(shí):7ms
結(jié)果是否一致:true
當(dāng)前CPU最優(yōu)向量長(zhǎng)度(float元素?cái)?shù)):8

3.4 注意事項(xiàng)

啟用方式(核心修正):

  • 編譯:javac --release 21 --add-modules jdk.incubator.vector VectorAPIDemo.java;
  • 運(yùn)行:java --add-modules jdk.incubator.vector VectorAPIDemo;
  • 無(wú)需 --enable-preview(孵化特性≠預(yù)覽特性),需導(dǎo)入 jdk.incubator.vector 包。

向量物種選擇:

  • 優(yōu)先使用 SPECIES_PREFERRED(自動(dòng)適配當(dāng)前 CPU 最優(yōu)向量長(zhǎng)度);
  • 手動(dòng)選擇需匹配 CPU 架構(gòu)(x86 支持 SPECIES_128/256/512,ARM 支持 SPECIES_128/256)。

適用場(chǎng)景:

  • 適合大規(guī)模數(shù)值計(jì)算(數(shù)組 / 矩陣運(yùn)算,元素?cái)?shù) ≥ 10 萬(wàn));
  • 小規(guī)模計(jì)算(<1 萬(wàn)元素)因向量初始化開(kāi)銷(xiāo),性能可能不如傳統(tǒng)循環(huán)。

數(shù)據(jù)類(lèi)型支持:僅支持 float、double、int、long 等基本數(shù)值類(lèi)型,不支持引用類(lèi)型。

性能優(yōu)化:

  • 數(shù)組長(zhǎng)度盡量為向量長(zhǎng)度的整數(shù)倍,減少剩余元素循環(huán);
  • 性能測(cè)試需做 JIT 預(yù)熱(先運(yùn)行幾次),避免編譯開(kāi)銷(xiāo)導(dǎo)致數(shù)據(jù)失真;
  • 確保 CPU 開(kāi)啟向量指令集(如 x86 的 AVX2/AVX512),否則性能無(wú)提升。

三、總結(jié)

JDK21 作為 LTS 版本,核心正式特性聚焦于提升開(kāi)發(fā)效率、程序性能和代碼安全性,虛擬線程、模式匹配 for switch、記錄模式、密封類(lèi)、分代 ZGC、序列化集合六大正式特性,可直接應(yīng)用于生產(chǎn)環(huán)境,尤其適合高并發(fā)、大數(shù)據(jù)、企業(yè)級(jí)應(yīng)用場(chǎng)景;五大預(yù)覽特性則為后續(xù)開(kāi)發(fā)提供了更多可能性,可根據(jù)項(xiàng)目需求選擇性試用(需注意預(yù)覽特性可能在未來(lái)版本中調(diào)整)。

本文所有代碼均經(jīng)過(guò) JDK21 環(huán)境測(cè)試,可直接復(fù)制運(yùn)行,建議開(kāi)發(fā)者結(jié)合實(shí)際項(xiàng)目場(chǎng)景,合理運(yùn)用這些新特性,提升開(kāi)發(fā)效率和程序性能。后續(xù)可持續(xù)關(guān)注 JDK 版本更新,及時(shí)掌握新特性的優(yōu)化和迭代。

到此這篇關(guān)于Java JDK21 新特性全面解析的文章就介紹到這了,更多相關(guān)Java JDK21 新特性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java反轉(zhuǎn)鏈表的多種解決方法舉例詳解

    java反轉(zhuǎn)鏈表的多種解決方法舉例詳解

    這篇文章主要介紹了java反轉(zhuǎn)鏈表的多種解決方法,分別是使用棧、雙指針和遞歸,每種方法都有其實(shí)現(xiàn)原理和代碼示例,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • Spring?boot如何在IDEA中讓一個(gè)應(yīng)用在不同的端口多次啟動(dòng)詳解

    Spring?boot如何在IDEA中讓一個(gè)應(yīng)用在不同的端口多次啟動(dòng)詳解

    在開(kāi)發(fā)過(guò)程中,SpringBoot項(xiàng)目的多端口啟動(dòng)是一個(gè)實(shí)用的需求,特別是在進(jìn)行并發(fā)測(cè)試或者隔離環(huán)境時(shí),這篇文章主要介紹了Spring?boot如何在IDEA中讓一個(gè)應(yīng)用在不同的端口多次啟動(dòng)的相關(guān)資料,需要的朋友可以參考下
    2026-01-01
  • 淺析如何在Java應(yīng)用中優(yōu)雅的發(fā)送短信

    淺析如何在Java應(yīng)用中優(yōu)雅的發(fā)送短信

    很多業(yè)務(wù)場(chǎng)景里,我們都需要發(fā)送短信,比如登陸驗(yàn)證碼、告警、營(yíng)銷(xiāo)通知、節(jié)日祝福等等,這篇文章,我們聊聊 Java 應(yīng)用中如何優(yōu)雅的發(fā)送短信,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2023-11-11
  • SpringCloud-Alibaba-Sentinel服務(wù)降級(jí),熱點(diǎn)限流,服務(wù)熔斷

    SpringCloud-Alibaba-Sentinel服務(wù)降級(jí),熱點(diǎn)限流,服務(wù)熔斷

    這篇文章主要介紹了SpringCloud-Alibaba-Sentinel服務(wù)降級(jí),熱點(diǎn)限流,服務(wù)熔斷,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Spring?@Autowired注解超詳細(xì)示例

    Spring?@Autowired注解超詳細(xì)示例

    @Autowired注解可以用在類(lèi)屬性,構(gòu)造函數(shù),setter方法和函數(shù)參數(shù)上,該注解可以準(zhǔn)確地控制bean在何處如何自動(dòng)裝配的過(guò)程。在默認(rèn)情況下,該注解是類(lèi)型驅(qū)動(dòng)的注入
    2022-08-08
  • springboot+rabbitmq實(shí)現(xiàn)指定消費(fèi)者才能消費(fèi)的方法

    springboot+rabbitmq實(shí)現(xiàn)指定消費(fèi)者才能消費(fèi)的方法

    當(dāng)項(xiàng)目部署到測(cè)試環(huán)境后,QA測(cè)試過(guò)程中,總是“莫名其妙”的發(fā)現(xiàn)所保存的用戶付款單數(shù)據(jù)有問(wèn)題。這篇文章主要介紹了springboot+rabbitmq實(shí)現(xiàn)指定消費(fèi)者才能消費(fèi),需要的朋友可以參考下
    2021-11-11
  • java高級(jí)應(yīng)用:線程池的全面講解(干貨)

    java高級(jí)應(yīng)用:線程池的全面講解(干貨)

    這篇文章主要介紹了java高級(jí)應(yīng)用:線程池的全面講解(干貨),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • 淺談Java中Map和Set之間的關(guān)系(及Map.Entry)

    淺談Java中Map和Set之間的關(guān)系(及Map.Entry)

    這篇文章主要介紹了淺談Java中Map和Set之間的關(guān)系(及Map.Entry),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java中本地緩存的4種實(shí)現(xiàn)方式總結(jié)

    Java中本地緩存的4種實(shí)現(xiàn)方式總結(jié)

    這篇文章主要介紹了Java中本地緩存的4種實(shí)現(xiàn)方式,分別是基礎(chǔ)緩存實(shí)現(xiàn)、GuavaLoadingCache、SpringBoot整合Caffeine和JetCache,通過(guò)實(shí)例代碼,詳細(xì)講解了每種緩存技術(shù)的特點(diǎn)和使用方法,需要的朋友可以參考下
    2025-04-04
  • springboot一鍵application.yml配置文件實(shí)踐

    springboot一鍵application.yml配置文件實(shí)踐

    application-dev.yml文件是Spring Boot項(xiàng)目中用于定義開(kāi)發(fā)環(huán)境配置的一個(gè)YAML格式文件,它包含了數(shù)據(jù)庫(kù)連接、服務(wù)器端口、日志級(jí)別、緩存配置等開(kāi)發(fā)過(guò)程中常用的設(shè)置,通過(guò)這個(gè)文件,開(kāi)發(fā)者可以在不同的環(huán)境中(如開(kāi)發(fā)、測(cè)試、生產(chǎn))切換配置,而無(wú)需修改代碼
    2026-02-02

最新評(píng)論

新建县| 大兴区| 博客| 原阳县| 思南县| 娱乐| 鄂托克旗| 三原县| 曲靖市| 阿拉尔市| 余干县| 遂昌县| 本溪市| 黑龙江省| 新蔡县| 太康县| 德保县| 肥东县| 泽州县| 隆子县| 蚌埠市| 凤山市| 南华县| 德化县| 堆龙德庆县| 乌兰浩特市| 迁安市| 佳木斯市| 磐安县| 乌审旗| 景洪市| 获嘉县| 个旧市| 铜山县| 鹤壁市| 龙里县| 辽中县| 柘城县| 福泉市| 和林格尔县| 广汉市|