Skip to main content
🚀 Java 24 正式发布!作为 JDK 25(下一个 LTS 版本)的重要铺垫,这次更新带来了哪些令人兴奋的新特性?让我们一起探索 Java 开发的新世界!

🎯 为什么 JDK 24 如此重要?

JDK 24 是一个具有里程碑意义的版本,因为这次更新中包含的 JEP(JDK 增强提案)候选功能很有可能成为JDK 25的正式特性。而 JDK 25 将在2025 年 9 月发布,成为继 JDK 21 之后的下一个长期支持(LTS)版本

1. 🎉 简化的 Hello World:告别繁琐语法

终于来了! Java 现在支持简单的void main()方法来打印”Hello, World”!

传统写法 vs 新写法

JDK 21 及之前的传统写法:
public class MyFirstClass {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}
痛点:对于 Java 初学者来说,这种冗长的代码难以理解,特别是对那些不熟悉 Java 语法的人。 JDK 24 的新写法:
void main() {
    println("Hello, World");
}
⚠️ 注意:这不是全新的 Java 语法——有经验的 Java 开发者仍然可以使用所有现有的语法和详细写法。 👉 实现原理:JVM 通过隐式声明类和实例方法来实现这一功能。同时,JVM 现在会在运行时自动导入某些必需的包,减少了使用println()等实用方法时的手动命名空间声明需求。

实际应用示例

下面的程序从控制台读取用户姓名并打印问候消息:
void main() {
    var name = readln();
    var message = "Hello, World and " + name;
    println(message);
}
如果转换为传统的 Java 代码,等价于:
import java.io.*;
import java.util.Scanner;

public class A {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String name = sc.nextLine();
        String message = "Hello, World and " + name;
        System.out.println(message);
        sc.close();
    }
}
🔗 相关 JEPJEP 495 - 简单源文件和实例主方法

2. 🔍 原始类型模式匹配:instanceof 和 Switch 的新玩法

解决的问题

JDK 21 的限制:类型匹配要求被比较的变量必须是引用类型,而不能是原始类型。 👉 JDK 24 的改进:从 JDK 24 开始,instanceofswitch-case中的类型匹配将允许对象与原始类型匹配,而不仅仅是引用类型。

instanceof 示例

public class Test {
    public static String identifyType(Object value) {
        if (value instanceof int) {
            return String.format("int: %d", value);
        } else if (value instanceof String) {
            return String.format("String: %s", value);
        } else if (value instanceof Double) {
            return String.format("Double: %f", value);
        } else {
            return "Unknown type";
        }
    }
}

Switch-Case 示例

String identifyType(Object value) {
    return switch (value) {
        case int i -> String.format("int: %d", i);      // 原始类型!
        case Double d -> String.format("Double: %f", d);
        case String s -> String.format("String: %s", s);
        default -> "Unknown type";
    };
}
🔗 相关 JEPJEP 488 - 模式中的原始类型

3. 🏗️ 灵活的构造函数体:突破 super() 限制

👉 新特性:开发者现在可以在构造函数体中使用super()初始化父类之前包含逻辑代码。 JDK 21 的限制:这是不可能的——开发者必须在父类中使用辅助方法来在初始化后执行逻辑。

构造函数的新结构

构造函数体现在分为两部分:序言(Prologue)尾声(Epilogue)
public class A extends B {
    public A() {
        // 序言 - 可以在 super() 之前执行逻辑
        super();
        // 尾声 - super() 之后的逻辑
    }
}
⚠️ 重要限制:在序言阶段,无法访问正在初始化的当前实例。

实际应用示例

public class A extends B {
    private static int multiplier = 10;
    private final int childValue;

    public A(int baseValue) {
        // 序言 - 计算逻辑
        int computedValue = baseValue * multiplier;

        super(baseValue); // 初始化父类 B

        // 尾声 - 设置子类属性
        this.childValue = computedValue;
    }
}
🔗 相关 JEPJEP 492 - 灵活的构造函数体

4. 🔒 作用域值:ThreadLocal 的完美替代品

解决 ThreadLocal 的三大痛点

ScopedValue API 是对ThreadLocal API 的改进,用于在不同线程(包括虚拟线程)和任务之间共享不可变数据。 👉 ScopedValue 解决的三个关键挑战
  1. 可变性问题:ThreadLocal 中,线程既可以访问又可以修改共享变量,导致意外副作用。ScopedValue 只允许共享不可变数据。
  2. 无界生命周期:ThreadLocal 变量必须手动移除。ScopedValue 在原始拥有线程完成执行后自动清理。
  3. 昂贵的继承:ThreadLocal 变量必须为子线程复制和重新创建。ScopedValue 允许父子线程之间高效共享内存。

使用示例

static final ScopedValue<String> USERNAME = ScopedValue.newInstance();

void main(String[] args) {
    ScopedValue.where(USERNAME, "Amrit").run(() -> {
        System.out.println("主任务中的值:" + USERNAME.get());
        performSubTask();
    });
}

static void performSubTask() {
    System.out.println("子任务中的值:" + USERNAME.get());
}
🔗 相关 JEPJEP 487 - 作用域值

5. 📦 模块导入声明:告别重复导入

批量导入的新方式

👉 新特性:用户现在可以直接导入一组相关包,而不是单独导入它们。 这减少了冗长性,并消除了重复包导入造成的低效率。

对比示例

传统方式
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.stream.*;
新方式
import module java.xml;
👉 其他可用模块java.basejava.desktopjava.sql等,每个模块都为特定用例分组了所需的包。 🔗 相关 JEPJEP 494 - 模块导入声明

6. 🏗️ 结构化并发:并发编程的新范式

解决并发编程的结构化问题

JDK 24 引入的StructuredTask API 将并发编程中的子任务组合在一起,在运行时将它们视为一个单元,而不是独立的任务。 ExecutorService 的问题:使用传统的 ExecutorService API,子任务与启动它们的父任务只有逻辑关系,在运行时独立运行,没有直接连接。 👉 StructuredTask 的优势:开发者现在可以控制整个任务,该任务可以由在不同任务中运行的子任务组成为一个整体单元。如果一个任务失败,失败会传播到其他依赖任务,整个任务也会中止。

代码对比示例

import java.util.concurrent.*;

Integer fun1() throws Exception {
    Integer val = 1;
    for (int i = 0; i < 10; i++) {
        println(val);
        val++;
        Thread.sleep(1000);
        if (val == 4) {
            System.out.println("Fun 1 在值为 4 时停止");
            throw new Exception();
        }
    }
    return val;
}

Integer fun2() throws InterruptedException {
    Integer val = 1;
    for (int i = 0; i < 10; i++) {
        println(val);
        val++;
        Thread.sleep(1000);
    }
    return val;
}

// 使用 StructuredTask API
void handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        // 使用作用域分组
        Supplier<Integer> x = scope.fork(() -> fun1());
        Supplier<Integer> y = scope.fork(() -> fun2());
        scope.join().throwIfFailed();
    } catch (Exception e) {
        println("Fun 1 和 Fun 2 都已停止,因为 Fun 1 失败了");
    }
}

// 传统 ExecutorService 方式
void handleWithoutST() throws ExecutionException, InterruptedException {
    ExecutorService exsc = Executors.newFixedThreadPool(3);
    Future<Integer> x = exsc.submit(() -> fun1());
    Future<Integer> y = exsc.submit(() -> fun2());
    exsc.shutdown();
}
🔗 相关 JEPJEP 499 - 结构化任务

7. ⚡ AOT 类加载和链接:启动速度提升 40%

性能革命性提升

提前(AOT)类加载和链接旨在改善 Java 应用程序的启动时间。 👉 实现原理:通过在一次运行期间缓存类的加载和链接形式,并在后续运行中重用它们来实现这一点。 这一增强还为启动和预热时间的未来优化奠定了基础。 👉 性能提升:提前编译使 Java 程序性能提升了40% 🔗 相关 JEPJEP 483 - AOT 类加载和链接

8. 🚫 告别 32 位:移除 Windows 32 位支持

平台支持的重要变化

JDK 24 正式移除了对 Windows 32 位系统的支持,这是 Java 平台演进的一个重要里程碑。 重要提醒:如果你还在使用 Windows 32 位系统,必须迁移到 64 位系统才能使用 JDK 24 及以后的版本。 🔗 相关 JEPJEP 479 - 移除 Windows 32 位 x86 端口