当前位置: 首页 > news >正文

Java 24 新特性解析与代码示例

Java 24 新特性解析与代码示例

文章目录

  • Java 24 新特性解析与代码示例
    • 1. 引言
    • 2. 新特性详解
      • 2.1. 原始类型在模式、instanceof和switch中的使用(JEP 488,第二预览)
      • 2.2. 灵活的构造器体(JEP 492,第三预览)
      • 2.3. 模块导入声明(JEP 494,第二预览)
      • 2.4. Stream聚合器(JEP 485,已最终确定)
      • 2.5. 简单源文件和实例主方法(JEP 495,第四预览)
      • 2.6. 类文件API(JEP 484,已最终确定)
      • 2.7. 作用域值(JEP 487,第四预览)
      • 2.8. Vector API(JEP 489,第九次孵化)
      • 2.9. 结构化并发(JEP 499,第四预览)
      • 2.10. 密钥派生函数API(JEP 478,预览)
      • 2.11. 量子抗性模块格子基密钥封装机制(JEP 496)
      • 2.12. 量子抗性模块格子基数字签名算法(JEP 497)
      • 2.13. 警告使用sun.misc.Unsafe中的内存访问方法(JEP 498)
      • 2.14. 实验性Shenandoah GC(JEP 404)
      • 2.15. 紧凑对象头(JEP 450,实验性)
      • 2.16. G1的晚期屏障扩展(JEP 475)
      • 2.17. 准备限制JNI的使用(JEP 472)
      • 2.18. 移除Windows 32位x86端口(JEP 479)
      • 2.19. 永久禁用安全管理器(JEP 486)
      • 2.20. ZGC:移除非分代模式(JEP 490)
      • 2.21. 同步虚拟线程而不固定(JEP 491)
      • 2.22. 提前类加载和链接(JEP 483)
      • 2.23. 不使用JMOD链接运行时镜像(JEP 493)
      • 2.24. 弃用32位x86端口(JEP 501)
    • 3. 结语

1. 引言

Java 24(JDK 24),于2025年3月18日正式发布,是Java语言和平台的最新版本。作为一个短期支持(非LTS)版本,它将在六个月内获得Oracle的Premier Support。Java 24引入了24个JDK增强提案(JEPs),涵盖了核心库、语言规范、安全性、性能优化和开发工具等多个领域。这些新特性旨在提升开发者的生产力,增强Java语言的表现力,并为未来的技术发展(如量子计算)奠定基础。

本文将深入剖析Java 24的每一项新特性,提供详细的解释和完整的代码示例。对于优化的特性,我们将通过对比代码展示其改进之处。本文的目标是为读者提供“硬核”且实用的内容,确保读完后能够理解并应用这些新特性。以下内容将按照特性类型和重要性组织,确保逻辑清晰、易于理解。

在这里插入图片描述

2. 新特性详解

2.1. 原始类型在模式、instanceof和switch中的使用(JEP 488,第二预览)

这一特性扩展了模式匹配功能,允许在instanceofswitch语句中使用原始类型(如intdouble等)。这使得代码更简洁、类型安全,减少了不必要的装箱和拆箱操作。

对比代码:

  • Java 24之前的写法:

    Object value = 10;
    if (value instanceof Integer) {int i = (Integer) value;if (i > 5) {System.out.println("Value is an integer greater than 5");}
    }
    
  • Java 24的新写法:

    Object value = 10;
    if (value instanceof int i && i > 5) {System.out.println("Value is an integer greater than 5");
    }
    

说明:
Java 24允许直接在instanceof中使用原始类型int,并将值绑定到变量i,省去了显式的类型转换。这提高了代码的可读性和效率,尤其在处理混合类型数据时。

使用注意:
此特性为预览功能,需在编译和运行时使用--enable-preview标志:

javac --enable-preview --release 24 MyClass.java
java --enable-preview MyClass

2.2. 灵活的构造器体(JEP 492,第三预览)

此特性允许在构造器中,在调用super()this()之前执行初始化或验证逻辑。这简化了构造器的编写,减少了对辅助方法的依赖。

对比代码:

  • Java 24之前的写法:

    public class Employee extends Person {public Employee(String name) {super(validateName(name));}private static String validateName(String name) {if (name == null || name.isBlank()) {throw new IllegalArgumentException("Name cannot be empty");}return name;}
    }
    
  • Java 24的新写法:

    public class Employee extends Person {public Employee(String name) {if (name == null || name.isBlank()) {throw new IllegalArgumentException("Name cannot be empty");}super(name);}
    }
    

说明:
Java 24允许在构造器体中直接包含验证逻辑,代码更直观,减少了静态辅助方法的需要。此特性为预览功能,需启用--enable-preview

2.3. 模块导入声明(JEP 494,第二预览)

此特性引入import module语句,允许导入模块中所有导出的包中的公共类和接口,简化模块化开发。

对比代码:

  • Java 24之前的写法:

    import java.util.List;
    import java.util.ArrayList;List<String> list = new ArrayList<>();
    
  • Java 24的新写法:

    import module java.base;List<String> list = new ArrayList<>();
    

说明:
import module java.base;一次性导入java.base模块的所有公共类和接口,减少了冗长的导入语句。此特性为预览功能,需启用--enable-preview

2.4. Stream聚合器(JEP 485,已最终确定)

此特性为Stream API引入了自定义中间操作,允许开发者通过gather方法实现复杂的流转换。

示例代码:

import java.util.List;
import java.util.stream.Stream;public class StreamGathererExample {public static void main(String[] args) {List<String> strings = List.of("hello", "world", "java");String result = strings.stream().gather(() -> new StringBuilder(),(sb, s) -> sb.append(s.toUpperCase()),StringBuilder::toString).findFirst().orElse("");System.out.println(result); // 输出:HELLOWORLDJAVA}
}

说明:
gather方法允许开发者定义初始化、累积和完成逻辑,增强了Stream API的灵活性。此特性已最终确定,可直接在生产环境中使用。

2.5. 简单源文件和实例主方法(JEP 495,第四预览)

此特性简化了Java程序的编写,允许使用非静态的main方法,适合初学者和快速原型开发。

示例代码:

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

说明:
此特性消除了public static void main(String[] args)的复杂性,使Java更易于学习。此为预览功能,需启用--enable-preview

2.6. 类文件API(JEP 484,已最终确定)

此特性提供标准API用于解析、生成和转换类文件,减少对第三方库(如ASM)的依赖。

示例代码:

import java.nio.file.Files;
import java.nio.file.Path;
import jdk.classfile.ClassFile;public class ClassFileReader {public static void main(String[] args) throws Exception {ClassFile classFile = ClassFile.read(Files.readAllBytes(Path.of("Example.class")));System.out.println("Class Name: " + classFile.thisClass());}
}

说明:
ClassFile API与JDK版本同步更新,确保兼容性。此特性已最终确定,适合框架和工具开发者使用。

2.7. 作用域值(JEP 487,第四预览)

此特性提供了一种共享不可变数据的机制,替代ThreadLocal,性能更高且更易于理解。

示例代码:

import jdk.incubator.concurrent.ScopedValue;public class ScopedValueExample {static final ScopedValue<String> USER_ID = ScopedValue.newInstance();public static void main(String[] args) {ScopedValue.where(USER_ID, "user123", () -> {System.out.println("User ID: " + USER_ID.get());});}
}

说明:
作用域值适合与虚拟线程和结构化并发结合使用。此为预览功能,需启用--enable-preview

2.8. Vector API(JEP 489,第九次孵化)

此特性提供向量计算API,优化CPU向量指令的利用,适合高性能计算。

示例代码:

import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorSpecies;public class VectorExample {static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;public static void main(String[] args) {float[] a = {1.0f, 2.0f, 3.0f, 4.0f};float[] b = {5.0f, 6.0f, 7.0f, 8.0f};float[] c = new float[4];for (int i = 0; i < a.length; i += SPECIES.length()) {FloatVector va = FloatVector.fromArray(SPECIES, a, i);FloatVector vb = FloatVector.fromArray(SPECIES, b, i);FloatVector vc = va.add(vb);vc.intoArray(c, i);}for (float f : c) {System.out.println(f);}}
}

说明:
Vector API适合数值密集型应用。此为孵化功能,需启用--add-modules jdk.incubator.vector

2.9. 结构化并发(JEP 499,第四预览)

此特性提供了一种并发编程模型,将相关任务视为一个整体,简化错误处理和任务管理。

示例代码:

import java.util.concurrent.StructuredTaskScope;public class StructuredConcurrencyExample {public static void main(String[] args) throws Exception {try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {var task1 = scope.fork(() -> {System.out.println("Task 1 started");Thread.sleep(1000);System.out.println("Task 1 completed");return "Result 1";});var task2 = scope.fork(() -> {System.out.println("Task 2 started");Thread.sleep(500);System.out.println("Task 2 completed");return "Result 2";});scope.join();scope.throwIfFailed();System.out.println("Results: " + task1.get() + ", " + task2.get());}}
}

说明:
结构化并发提高了并发代码的可维护性。此为预览功能,需启用--enable-preview

2.10. 密钥派生函数API(JEP 478,预览)

此特性引入javax.crypto.KDFAPI,用于从秘密密钥派生其他密钥,支持如Argon2等算法。

示例代码:

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;public class KDFExample {public static void main(String[] args) throws Exception {char[] password = "mysecretpassword".toCharArray();byte[] salt = "somesalt".getBytes();int iterations = 10000;int keyLength = 256;SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);byte[] derivedKey = factory.generateSecret(spec).getEncoded();System.out.println("Derived Key: " + bytesToHex(derivedKey));}private static String bytesToHex(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(String.format("%02x", b));}return sb.toString();}
}

说明:
此API增强了加密安全性,适合密码学应用。此为预览功能,需启用--enable-preview

2.11. 量子抗性模块格子基密钥封装机制(JEP 496)

此特性实现了模块格子基密钥封装机制(ML-KEM),为量子计算时代提供抗量子攻击的加密算法。

示例代码:

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.NamedParameterSpec;public class MLKEMExample {public static void main(String[] args) throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ML-KEM");keyPairGenerator.initialize(new NamedParameterSpec("ML-KEM-512"));KeyPair keyPair = keyPairGenerator.generateKeyPair();System.out.println("Public Key: " + keyPair.getPublic());System.out.println("Private Key: " + keyPair.getPrivate());}
}

说明:
ML-KEM支持ML-KEM-512、ML-KEM-768和ML-KEM-1024参数集,适用于未来安全通信。

2.12. 量子抗性模块格子基数字签名算法(JEP 497)

此特性实现了模块格子基数字签名算法(ML-DSA),提供量子抗性的数字签名。

示例代码:

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;public class MLDSAExample {public static void main(String[] args) throws Exception {KeyPair keyPair = KeyPairGenerator.getInstance("ML-DSA").generateKeyPair();Signature sig = Signature.getInstance("ML-DSA");sig.initSign(keyPair.getPrivate());byte[] message = "Hello, World!".getBytes();sig.update(message);byte[] signature = sig.sign();System.out.println("Signature: " + bytesToHex(signature));}private static String bytesToHex(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(String.format("%02x", b));}return sb.toString();}
}

说明:
ML-DSA确保签名在量子计算时代的安全性,适合高安全性应用。

2.13. 警告使用sun.misc.Unsafe中的内存访问方法(JEP 498)

此特性在使用sun.misc.Unsafe的内存访问方法时发出警告,鼓励迁移到更安全的API。

示例代码:

import sun.misc.Unsafe;public class UnsafeExample {public static void main(String[] args) {Unsafe unsafe = getUnsafe();long address = unsafe.allocateMemory(1024);System.out.println("Allocated memory at: " + address);}private static Unsafe getUnsafe() {try {return sun.misc.Unsafe.getUnsafe();} catch (SecurityException e) {return null;}}
}

说明:
运行此代码将触发警告,建议开发者使用VarHandleForeign Function & Memory API替代。

2.14. 实验性Shenandoah GC(JEP 404)

此特性引入了Shenandoah GC的分代模式,优化内存使用和性能。

示例代码:

// 运行时启用分代Shenandoah GC
java -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational MyApp

说明:
分代Shenandoah GC支持并发收集,适合低延迟应用。此为实验性功能,需谨慎使用。

2.15. 紧凑对象头(JEP 450,实验性)

此特性在64位架构上将对象头大小从128位减至64位,降低内存和CPU开销。

示例代码:

// 运行时启用紧凑对象头
java -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders MyApp

说明:
此特性可显著减少内存占用,适合内存密集型应用。此为实验性功能。

2.16. G1的晚期屏障扩展(JEP 475)

该特性主要是将 Late Barrier Expansion 引进到 G1 中。Barrier expansion 是指在垃圾回收过程中插入或生成额外代码(称为“屏障”)以管理内存并确保垃圾回收的正确性。这些屏障通常被插入到字节码中的特定位置,例如在内存访问之前或之后,以执行以下任务:

  • 记住写操作:跟踪对象的更改,这有助于垃圾回收器识别需要扫描的堆的部分。例如,写屏障(write barrier)会在每次存储操作之前执行,记录哪些对象被修改,从而帮助垃圾回收器维护对象的可达性信息。
  • 保持一致性:确保程序对内存的视图与垃圾回收器的视图保持一致,特别是在并发垃圾回收阶段。例如,读屏障(read barrier)会在读取操作之前检查指针是否指向堆内存,并记录这些操作,以防止垃圾回收器误判对象的可达性。
  • 处理引用:管理对象之间的引用,特别是在压缩或迁移阶段。例如,在垃圾回收的增量收集中,屏障可以阻止指向未分配空间(from-space)的指针进入寄存器,从而避免垃圾回收器无法追踪到这些对象。

Early barrier expansion 的含义是这些屏障在编译过程的早期插入或生成,而如果在过程的后期进行(正如 JEP 所提议的),则可以实现更优化的放置,并可能减少这些屏障相关的开销,这可以带来性能的提升和更高效的垃圾回收。

研究表明,在 HotSpot 的 C2 JIT 编译器中使用早期屏障展开会增加大约 10%–20% 的编译开销。通过采用后期屏障展开,JEP 475 降低了这种编译开销,并使 G1 的实现更易于维护,从而提升了 Java 应用的性能。

2.17. 准备限制JNI的使用(JEP 472)

JNI(Java 本地接口)自 Java 1.1 引入,是 Java 与本地 C/C++ 代码交互的标准方式。然而,本地代码容易引发 JVM 崩溃和未定义行为等风险。因此,JEP 472 在 Java 24 中引入了警告机制。当通过 JNI 或 FFM(外部函数与内存 API)访问本地代码时,JVM 默认会发出警告,以提醒开发者并为未来默认禁止本地互操作做准备。默认情况下,这些警告是开启的,如果需要继续使用本地代码,可以通过启动参数关闭警告,例如:

java --enable-native-access=ALL-UNNAMED ...
java --enable-native-access=M1,M2,...

该特性主要是默认会对通过 JNI 或者 FFM 去访问本地代码发出警告,为后续默认限制访问做准备。可以通过启用 native access 来关闭警告,命令如下:

java --enable-native-access=ALL-UNNAMED ...
java --enable-native-access=M1,M2,...

2.18. 移除Windows 32位x86端口(JEP 479)

此特性移除了Windows 32位x86端口,鼓励迁移到64位架构。

说明:
开发者需确保应用兼容64位环境。

在 JDK 21 的 JEP 449 中已经废弃了对 Windows 32 位 x86 平台的移植,本次 JDK 24 删除了相关源代码,并移除了对相关构建支持。

2.19. 永久禁用安全管理器(JEP 486)

此特性永久禁用了安全管理器,建议使用现代安全机制。

说明:
安全管理器已不适合现代应用,开发者应采用其他安全框架。

安全管理器(Security Manager)作为 Java 平台自出生以来的一项安全机制,其设计思想是“最小权限原则”,默认情况下不信任任何代码,需要显式授予权限才能访问系统资源。但随着时间推移,安全管理器的使用极其有限,且权限检查机制过于复杂,导致维护成本高昂。因此,Java 17(JEP 411)已将其标记为弃用为待移除,Java 24 则彻底禁用了安全管理器。启动时无法启用安全管理器(-Djava.security.manager 参数将报错退出),在运行时也无法安装新的安全管理器;例如,如果代码中调用 System.setSecurityManager(new SecurityManager()),JVM 会抛出 UnsupportedOperationException。因此,安全管理器在 Java 24 中已无法启用,相关 API 将在未来版本中被移除。

2.20. ZGC:移除非分代模式(JEP 490)

此特性移除了ZGC的非分代模式,默认使用分代模式。

说明:
此变化简化了ZGC维护,开发者无需调整代码。

JDK 21 的 JEP 439 引入了分代模式;JDK 23 的 JEP 474 将分代模式设置为默认,并废弃非分代模式以便后续移除;JDK 24 则完全移除了 ZGC 的非分代模式,只保留分代模式。

2.21. 同步虚拟线程而不固定(JEP 491)

JDK 21 引入虚拟线程时存在一个“pinning”(固定)的问题:当虚拟线程在其载体(底层平台线程)上执行 synchronized 代码块时,该虚拟线程会被固定在其载体上,直到退出该 synchronized 代码块才会卸载。这意味着在调用本地代码时也会被固定,导致性能下降。JEP 491 通过让虚拟线程也能够获取、持有和释放监视器,而无需绑定到载体线程,消除了这一限制。从 Java 24 开始,只要虚拟线程不调用本地代码,synchronized 对虚拟线程与平台线程的性能影响将相同。以下示例展示了 synchronized 代码块对虚拟线程和普通线程的一致性影响:

class Counter {private int count = 0;synchronized void increment() {if (count < 100) {count++;}}synchronized void decrement() {count--;}
}
// 假设多个虚拟线程调用 increment()/decrement()
// 在 Java 23 及更早版本,虚拟线程在执行这些 synchronized 方法时会被固定。
// 在 Java 24 及以后,虚拟线程可以卸载而不影响同步行为。

此特性优化了虚拟线程在synchronized方法中的行为,支持动态挂起和恢复。

说明:
此特性提升了虚拟线程的并发性能,适合高并发应用。

2.22. 提前类加载和链接(JEP 483)

该特性通过使应用程序的类在启动时立即可用(即提前加载和链接),从而缩短启动时间。通过在应用首次运行期间监视其类加载和链接过程,并将所有类的加载和链接形式存储在缓存中,以供后续运行使用,实现此目的。这为将来改进启动和预热时间奠定了基础。该特性通过 Ahead-of-Time (AOT) 缓存来存储已经读取、解析、加载和链接的类,使用步骤如下:

  • 记录配置:首先运行应用以记录 AOT 配置:

    java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -cp app.jar com.example.App ...
    
  • 创建 AOT 缓存:使用该配置来创建 AOT 缓存:

    java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot -cp app.jar com.example.App ...
    
  • 使用 AOT 缓存启动:最后使用 AOT 缓存启动应用:

    java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
    

AOT 缓存将读取、解析、加载和链接(通常在程序执行期间即时完成)的任务提前到缓存创建的阶段。因此,在执行阶段,程序启动速度更快,因为其类可以从缓存中快速访问。其性能提升可以高达 42%。

2.23. 不使用JMOD链接运行时镜像(JEP 493)

该特性能够在不使用 JMOD 文件的情况下创建自定义运行时映像(runtime image)。在构建 JDK 时可以启用此功能;默认情况下未启用,以便某些 JDK 发行版可以跳过它。已安装的 JDK 由运行时映像和一组以 JMOD 格式打包的模块组成,每个模块都在运行时映像中。jlink 在创建自定义运行时映像时通常会使用 JMOD 文件。实际上,JDK 自身的运行时映像也是通过这种方式生成,这导致 JDK 运行时映像与模块的 JMOD 文件在资源上存在冗余。JMOD 文件占 JDK 总大小的约 25%。如果 jlink 能够改变为直接从运行时映像中提取资源,我们可以简单省略这些 JMOD 文件显著减小 JDK 的大小。

使用示例(假设未在模块路径上指定 JMOD):

jlink --add-modules java.xml --output image
image/bin/java --list-modules
# 输出:
# java.base@24
# java.xml@24

说明:
此特性优化了运行时镜像的创建,适合容器化部署。

2.24. 弃用32位x86端口(JEP 501)

此特性弃用了32位x86端口,为未来移除做准备。

说明:
开发者应迁移到64位架构,以确保兼容性。

支持多个平台一直是 Java 生态系统关注的重点。然而,较旧平台无法无限期支持,这也是弃用 32 位 x86(Linux)端口的原因之一。维护 32 位 x86 所需的精力超过其价值。为了跟上新特性(如 Loom、FFM、向量 API 以及 G1 晚期屏障扩展),维护成本显著增加,因此该端口现已弃用。在 JDK 24 中,默认情况下尝试构建 32 位 x86 版本将失败。如果仍需构建,可以使用新的构建选项 --enable-deprecated-ports=yes 来抑制错误。这意味着 32 位 x86 用户可以继续使用 JDK 24,但在未来版本中将完全移除对此平台的支持,届时受影响的用户应已迁移到 64 位 JVM。

3. 结语

Java 24通过24个JEPs为开发者带来了性能、安全性和语言表达力的全面提升。从量子抗性加密到虚拟线程优化,再到简化的编程模型,Java 24展示了Java语言持续进化的活力。开发者可以通过本文提供的代码示例快速上手这些新特性,并在项目中探索其潜力。

然而,由于Java 24为非LTS版本,其部分特性仍处于预览或实验阶段,生产环境使用需谨慎。建议开发者密切关注官方文档和社区反馈,以确保代码的稳定性和兼容性。未来,Java 25(预计2025年9月发布)作为LTS版本,将进一步稳定这些特性。

参考资料:

资源名称链接
Java 24 New Features With ExamplesJavaTechOnline
What’s New in JDK 24Medium
Java 24 FeaturesHappy Coders
JDK 24: The new features in Java 24InfoWorld
Java 24 Features UnveiledBellSoft
JDK 24 Release NotesOracle

注意:
预览和孵化特性可能在未来版本中发生变化,生产环境使用需结合业务需求评估风险。希望本文能为您的Java开发之旅提供实用指导!

http://www.lryc.cn/news/606863.html

相关文章:

  • 理想I8对撞乘龙卡车,AI基于数学和物理的角度如何看?
  • macOS卸载.net core 8.0
  • 基于OpenCV的cv2.solvePnP方法实现头部姿态估计
  • STM32-ESP8266Wi-Fi模块使用USART实现通信/创建AP和STA模式配置教程(寄存器版)
  • 预测性维护之温振传感器选型与应用秘籍
  • ubuntu22.04系统入门 linux入门(二) 简单命令 多实践以及相关文件管理命令
  • Node.js的用途和安装方法
  • CS231n2017-Lecture9经典CNN架构笔记
  • 关于继承的一些知识(C++)
  • visual studio 2015 编写C++ 静态库和动态库、调用静态库和动态库
  • C++--多态
  • 20257月29日-8月2日训练日志
  • 软件测试测评公司关于HTTP安全头配置与测试?
  • 用 Ubuntu 22.04 (Jammy) 的 MongoDB 源
  • Java 学习笔记:常用类、String 与日期时间处理
  • 新手小白做一个简单的微服务
  • oracle的安全加密有哪些?
  • Linux基础 -- 内核快速向用户态共享内核变量方案之ctl_table
  • 编程与数学 03-002 计算机网络 17_云计算与网络
  • 无人设备遥控器之多设备协同技术篇
  • Java,八股,cv,算法——双非研0四修之路day22
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘altair’问题
  • 【Leetcode hot 100】1.两数之和
  • 切比雪夫不等式
  • qcustomplot 大量数据拖拽卡顿,开启opengl
  • SketchUp扩展工具分享:Ropefall v1.02插件轻松实现绳索模拟
  • 1、【C语言】【进阶】数组,指针与退化
  • 函数fdopendir的用法
  • [vue3 echarts] echarts 动态数据更新 setInterval
  • 深度学习(鱼书)day08--误差反向传播(后三节)