序
本文主要讲述一下Java26的新特性
openjdk version "26" 2026-03-17
OpenJDK Runtime Environment (build 26+35-2893)
OpenJDK 64-Bit Server VM (build 26+35-2893, mixed mode, sharing)
Java 26于2026年3月17日正式发布,延续每六个月一次的发布节奏,本次共包含10个JEP。Java 26并非长期支持(LTS)版本,若需LTS支持,请继续使用Java 21或Java 25。
本特性旨在为未来版本限制通过深度反射修改final字段做准备,从JDK 26起,凡是通过Field::set、MethodHandles.Lookup::unreflectSetter等深度反射API修改final字段的行为,都会在运行时发出警告。
动机:final字段代表不可变状态,是JVM进行常量折叠等性能优化的基础。但长期以来Java平台允许通过反射绕过这一约束,导致JVM无法充分信任任何final字段的值,影响了安全性和性能。此特性是Java迈向"默认完整性"(integrity by default)的重要一步。
行为变化:
final字段都会触发运行时警告--add-opens来规避该警告--enable-final-field-mutation为特定模块启用final字段修改--illegal-final-field-mutation控制违规行为的处理方式:warn(默认,发出警告)、allow(静默允许)、debug(打印堆栈)或deny(抛出异常)示例:
Field f =Config.class.getDeclaredField("env");// env 是 final 字段
f.setAccessible(true);
f.set(cfg,"dev");// Java 26 中会在运行时打印警告
如需临时抑制警告:
java --illegal-final-field-mutation=allow -jar myapp.jar
注意:序列化库在反序列化过程中修改
final字段不受本特性影响,建议使用sun.reflect.ReflectionFactoryAPI处理此类场景。
在JDK9的JEP 289: Deprecate the Applet API中首次废弃Applet API 在JDK17的JEP 398: Deprecate the Applet API for Removal中标记为待移除 在JDK26中则彻底移除了Applet相关API
移除的内容包括:
java.applet包(Applet、AppletContext、AppletStub、AudioClip)java.beans.AppletInitializerjavax.swing.JAppletjava.beans.Beans、javax.swing.RepaintManager等中引用上述类的方法和字段移除原因:现代浏览器已不再支持Java Applet;JDK 11已移除appletviewer工具;JDK 24已永久禁用Security Manager(Applet沙箱运行所依赖的机制)。Applet API已完全无法使用,保留毫无意义。
迁移建议:界面容器可改用AWT的其他组件;音频播放可改用JDK 25引入的
javax.sound.SoundClip。
在JDK24的JEP 483: Ahead-of-Time Class Loading & Linking中率先引入AOT缓存,支持将已读取、解析、加载和链接的类存入缓存 在JDK25的JEP 514: Ahead-of-Time Command-Line Ergonomics中简化了AOT缓存的创建命令,将两步合并为一步 在JDK26中,AOT缓存的支持进一步扩展到所有垃圾收集器(包括ZGC),解决了此前用户在使用低延迟GC与快速启动之间必须二选一的困境。
核心问题:JDK 24/25中的AOT缓存以GC特定格式(如G1兼容格式)存储Java对象(Class对象、字符串等),可直接内存映射到堆,实现极速加载。但ZGC使用独特的引用编码格式,无法直接映射,因此此前ZGC用户无法享受AOT缓存带来的启动优化。
解决方案:JEP 516引入了GC无关的对象缓存格式,将对象间引用存储为逻辑索引而非直接内存地址。加载时由后台线程顺序"物化"对象(分配堆内存、初始化字段、将逻辑索引转换为真实引用),应用程序首次使用类时等待物化完成。
训练环境 | 推荐缓存格式 |
|---|---|
使用了ZGC 或 -XX:-CompressedOops 或堆>32GB | GC无关格式(流式加载) |
使用-XX:+UseCompressedOops(默认,堆<32GB) | GC特定格式(内存映射) |
使用示例:
# 训练阶段(生成GC无关缓存)
java-XX:AOTCacheOutput=app.aot -XX:+AOTStreamableObjects-jar yourapp.jar
# 生产阶段使用缓存(可与训练时使用不同的GC)
java-XX:+UseZGC-XX:AOTCache=app.aot -jar yourapp.jar
也可以在训练阶段使用G1,生产阶段切换到ZGC,AOT缓存照样生效——这正是GC无关格式的最大价值。
JDK11的JEP 321: HTTP Client正式引入了HTTP Client API,支持HTTP/1.1和HTTP/2。 JDK26的JEP 517在此基础上新增了对HTTP/3协议(基于QUIC/UDP)的支持,使应用只需极少代码改动即可与HTTP/3服务器交互。
HTTP/3的优势:
默认协议仍为HTTP/2,开发者需主动选择启用HTTP/3:
// 方式一:在 HttpClient 层面启用 HTTP/3
HttpClient client =HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
// 方式二:仅在单个 HttpRequest 上指定 HTTP/3
HttpRequest request =HttpRequest.newBuilder(URI.create("https://example.com/"))
.version(HttpClient.Version.HTTP_3)
.GET()
.build();
HttpResponse<String> response = client.send(request,HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.version());// HTTP_3
协议协商策略(当服务器不支持HTTP/3时):
HttpRequest设置HTTP_3:先尝试HTTP/3,超时后回退到HTTP/2或HTTP/1.1HttpClient设置HTTP_3:并行建立HTTP/3和HTTP/2连接,使用最先成功的H3_DISCOVERY选项:先用HTTP/2,收到服务器ALT-SVC响应头后切换到HTTP/3注意:在HTTP/3的最初实现中,除了默认的安全套接字提供者SunJSSE之外,其他安全套接字提供者将无法被使用。要支持第三方安全套接字提供者,就需要在相关接口中添加相应的功能,而那些提供者的维护人员则需负责实现这些功能。我们可以在后续工作中解决这个问题。
本特性通过减少应用线程与GC优化线程之间的内存同步开销,显著提升G1垃圾收集器的吞吐量,是推动G1成为Java平台唯一默认垃圾收集器(参见JEP 523: Make G1 the Default Garbage Collector in All Environments)的重要基础工作。
技术原理:G1的写屏障(Write Barrier)此前需要维护一张卡表(Card Table)来追踪跨代引用,应用线程在修改对象引用时需要频繁同步这张卡表。JEP 522通过引入第二张卡表,将应用线程写屏障中的同步操作从约50条指令压缩至约12条,大幅降低了竞争。
性能提升:
本特性完全透明,无需任何代码或配置改动,升级到JDK 26后自动生效。
JDK25的JEP 470: PEM Encodings of Cryptographic Objects (Preview)作为第一次preview,引入了PEMEncoder和PEMDecoder API
JDK26作为第二次preview,在此基础上做了以下增强:支持对KeyPair和PKCS8EncodedKeySpec进行加密/解密;对API细节进行了若干调整和完善。
使用示例:
// 由于是预览版本,需要 --enable-preview
// 1. 生成 EC 密钥对并编码为 PEM
KeyPairGenerator kpg =KeyPairGenerator.getInstance("EC");
KeyPair keyPair = kpg.generateKeyPair();
PEMEncoder encoder =PEMEncoder.of();
String publicKeyPEM = encoder.encodeToString(keyPair.getPublic());
System.out.println("Public Key PEM:\n"+ publicKeyPEM);
// 2. 加密私钥后编码为 PEM
char[] password ="secret".toCharArray();
String encryptedPEM =PEMEncoder.of()
.withEncryption(password)
.encodeToString(keyPair.getPrivate());
System.out.println("Encrypted Private Key PEM:\n"+ encryptedPEM);
// 3. 从 PEM 解码公钥
PublicKey pubKey =PEMDecoder.of().decode(publicKeyPEM,PublicKey.class);
System.out.println("Decoded Public Key Algorithm: "+ pubKey.getAlgorithm());
// 4. 解密并解码私钥
PrivateKey privKey =PEMDecoder.of()
.withDecryption(password)
.decode(encryptedPEM,PrivateKey.class);
System.out.println("Decoded Private Key Algorithm: "+ privKey.getAlgorithm());
// 5. 编码/解码证书
CertificateFactory cf =CertificateFactory.getInstance("X.509");
X509Certificate cert =(X509Certificate) cf.generateCertificate(inputStream);
String certPEM = encoder.encodeToString(cert);
X509Certificate decodedCert =PEMDecoder.of().decode(certPEM,X509Certificate.class);
编译及运行:
javac --release26 --enable-preview PEMExample.java
java --enable-preview PEMExample
JDK19的JEP 428: Structured Concurrency (Incubator)作为第一次incubator JDK20的JEP 437: Structured Concurrency (Second Incubator)作为第二次incubator JDK21的JEP 453: Structured Concurrency (Preview)作为首次preview JDK22的JEP 462: Structured Concurrency (Second Preview)作为第二次preview JDK23的JEP 480: Structured Concurrency (Third Preview)作为第三次preview JDK24的JEP 499: Structured Concurrency (Fourth Preview)作为第四次preview JDK25的JEP 505: Structured Concurrency (Fifth Preview)作为第五次preview JDK26作为第六次preview,新增了
Joiner.onTimeout()用于支持整个任务组的超时控制。
结构化并发将多个并发任务视为一个统一的工作单元,若其中一个子任务失败,其余子任务会自动取消,避免线程泄漏和资源浪费。
使用示例:
Responsehandle()throwsInterruptedException,ExecutionException{
try(var scope =StructuredTaskScope.open(Joiner.allSuccessfulOrThrow())){
Subtask<String> user = scope.fork(()->findUser());
Subtask<Integer> order = scope.fork(()->fetchOrder());
scope.join();// 等待所有子任务完成,若任意一个失败则取消其他任务
// 所有子任务均已成功,合并结果
returnnewResponse(user.get(), order.get());
}
}
带超时的用法(JDK 26新增):
// 整组任务超过 2 秒则取消
try(var scope =StructuredTaskScope.open(
Joiner.onTimeout(Duration.ofSeconds(2),()->defaultResponse()))){
var user = scope.fork(()->findUser());
var order = scope.fork(()->fetchOrder());
scope.join();
returnnewResponse(user.get(), order.get());
}
JDK25的JEP 502: Stable Values (Preview)作为第一次preview,名称为Stable Values(稳定值) JDK26作为第二次preview,经过重大重新设计后更名为Lazy Constants(惰性常量),主要变化:
StableValue改名为LazyConstant,位于java.lang包List.ofLazy和Map.ofLazyorElseSet()等方法,改为更简洁的get()null(返回null将抛出NullPointerException)LazyConstant实现了延迟初始化的不可变数据持有者:首次通过get()访问时执行初始化,之后JVM将其视为真正的常量并可进行常量折叠优化,同时保证线程安全。
使用示例:
// 1. 单个惰性常量(替代延迟初始化的 null 字段)
classOrderController{
// 将 LazyConstant 自身声明为 final,以启用 JVM 常量折叠优化
privatefinalLazyConstant<Logger> logger =
LazyConstant.of(()->Logger.create(OrderController.class));
voidsubmitOrder(User user,List<Product> products){
logger.get().info("order started");
// ...
logger.get().info("order submitted");
}
}
// 2. 应用级组件延迟初始化
classApplication{
staticfinalLazyConstant<OrderController>ORDERS=LazyConstant.of(OrderController::new);
staticfinalLazyConstant<ProductRepository>PRODUCTS=LazyConstant.of(ProductRepository::new);
staticfinalLazyConstant<UserService>USERS=LazyConstant.of(UserService::new);
publicstaticOrderControllerorders(){returnORDERS.get();}
}
// 3. 惰性列表(按需初始化各元素)
staticfinalList<Worker>WORKERS=List.ofLazy(10, index ->newWorker("worker-"+ index));
// 4. 惰性映射(按键延迟初始化)
staticfinalMap<String,OrderController>ORDER_MAP=
Map.ofLazy(Set.of("Customers","Internal","Testing"), key ->newOrderController());
编译及运行(预览功能):
javac --release26 --enable-preview LazyExample.java
java --enable-preview LazyExample
向量API可在支持AVX、NEON等SIMD指令的CPU架构上,将向量化循环可靠地编译为最优本地指令:
staticfinalVectorSpecies<Float>SPECIES=FloatVector.SPECIES_PREFERRED;
voidvectorizedCompute(float[] a,float[] b,float[] c){
int i =0;
int upperBound =SPECIES.loopBound(a.length);
// 向量化主循环
for(; i < upperBound; i +=SPECIES.length()){
FloatVector va =FloatVector.fromArray(SPECIES, a, i);
FloatVector vb =FloatVector.fromArray(SPECIES, b, i);
FloatVector vc = va.mul(va).add(vb.mul(vb)).neg();
vc.intoArray(c, i);
}
// 标量尾部处理
for(; i < a.length; i++){
c[i]=-(a[i]* a[i]+ b[i]* b[i]);
}
}
编译及运行(需添加孵化模块):
javac --add-modules jdk.incubator.vector VectorExample.java
java --add-modules jdk.incubator.vector VectorExample
JDK23的JEP 455: Primitive Types in Patterns, instanceof, and switch (Preview)作为第一次preview
JDK24的JEP 488: Primitive Types in Patterns, instanceof, and switch (Second Preview)作为第二次preview
JDK25的JEP 507: Primitive Types in Patterns, instanceof, and switch (Third Preview)作为第三次preview
JDK26作为第四次preview,主要改进:增强了模式支配性检查(Dominance Check),例如long类型模式可以支配int类型模式,帮助编译器在switch中检测更多逻辑错误。
原始类型模式匹配示例:
// switch 中支持 int、long、float、double 等原始类型
staticStringcategory(Object price){
returnswitch(price){
caseint p when p <100->"BUDGET";
caseint p when p <500->"MID";
caseint p ->"PREMIUM";
caselong l when l >10000->"PREMIUM";
caselong l ->"BUSINESS";
default->"UNKNOWN";
};
}
// 使用 when 条件的原始类型 switch
switch(x.getStatus()){
case0->"okay";
case1->"warning";
case2->"error";
caseint i ->"unknown status: "+ i;// 捕获其余所有int值
}
// 布尔类型的 switch(JDK 26改进)
String result =switch(guitar.isInTune()){
casetrue->"Ready to play";
casefalse->"Out of tune";
};
// instanceof 支持原始类型的精确性检查
int i =42;
System.out.println(i instanceofbyte);// true(42 能无损存入 byte)
int big =1000;
System.out.println(big instanceofbyte);// false(1000 超出 byte 范围)
// 带绑定变量的原始类型 instanceof
if(roomSize instanceofbyte b){
System.out.println("Fits in byte: "+ b);
}
编译及运行:
javac --release26 --enable-preview PrimitivePatterns.java
java --enable-preview PrimitivePatterns
上面列出的是大方面的特性,除此之外还有一些API的更新及废弃,主要见JDK 26 Release Notes,这里举几个例子。
min和max默认方法 (JDK-8356995)
java.util.Comparator接口新增了min(T a, T b)和max(T a, T b)两个默认方法,可直接通过比较器对象调用,简化比较操作(此改进由JetBrains贡献)
java.lang.Process类现在实现了AutoCloseable接口,支持try-with-resources语法,close()方法等价于调用destroy(),方便管理进程生命周期
ofEpochMillis方法,支持生成UUIDv7 (JDK-8334015)
java.util.UUID新增ofEpochMillis(long epochMilli, long seq)静态方法,用于创建基于时间戳排序的UUIDv7,适合分布式系统中需要有序UUID的场景
MIN和MAX常量 (JDK-8366829)
java.time.Duration新增了MIN和MAX常量,分别表示最小和最大持续时间,方便边界判断
plusSaturating方法 (JDK-8368856)
java.time.Instant新增plusSaturating(Duration duration)方法,加法溢出时返回Instant.MIN或Instant.MAX而非抛出异常,避免处理时间边界时的溢出问题
getTotalGcCpuTime方法 (JDK-8368527)
java.lang.management.MemoryMXBean新增getTotalGcCpuTime()方法,返回垃圾回收累积的CPU时间(纳秒),方便监控GC的CPU开销
ofFileChannel方法 (JDK-8329829)HTTP Client API新增
BodyPublishers.ofFileChannel(FileChannel fc, long position, long length)方法,支持上传文件的指定区域,无需先将文件内容读入内存
jdk.crypto.disabledAlgorithms安全属性 (JDK-8244336)新增
jdk.crypto.disabledAlgorithms安全属性,可在JCE/JCA层统一禁用特定算法(区别于现有的jdk.tls.disabledAlgorithms仅作用于TLS层)
现在可以使用后量子数字签名算法ML-DSA(Module-Lattice-Based Digital Signature Algorithm)通过
jarsigner工具或JarSignerAPI对JAR文件进行签名,增强了供应链安全性
jarsigner -keystore ks -sigalg ML-DSA-65 -signedjar signed.jar test.jar mldsa
新增对RFC 9180定义的混合公钥加密(HPKE)方案的支持,通过现有
CipherAPI配合新的HPKEParameterSpec类使用,结合了KEM(密钥封装)、KDF(密钥派生)和AEAD(认证加密)三类算法
Javadoc生成的HTML文档现在支持深色主题,会根据用户系统的主题偏好自动切换(通过CSS媒体查询
prefers-color-scheme实现)
UseGCOverheadLimit (JDK-8212084)G1 GC现在支持
-XX:+UseGCOverheadLimit(默认启用):当连续5次GC的开销超过GCTimeLimit(默认98%)且堆空闲低于GCHeapFreeLimit(默认2%)时,抛出OutOfMemoryError,防止GC颠簸(与Parallel GC行为保持一致)
G1现在会急切回收符合条件的、包含引用的巨型对象(占用超过半个Region的对象),此前仅能回收不含引用的巨型对象,改善了短命巨型对象的内存回收及时性
Thread.stop()方法 (JDK-8368226)不安全的
Thread.stop()方法已被移除,该方法在JDK 1.2时代即被废弃,应改用标志变量或Thread.interrupt()来协调线程停止
jrunscript工具 (JDK-8367157)实验性的
jrunscript命令行工具被移除,该工具依赖已废弃的Nashorn引擎
jdk.jsobject模块 (JDK-8359760)
jdk.jsobject模块已迁移至JavaFX,从JDK标准发行版中移除
移除了对InfiniBand SDP的支持,现代高性能网络场景应使用更标准的网络栈
javax.management.modelmbean.DescriptorSupport中的XML交换功能 (JDK-8351413)移除了MBean Descriptor的XML序列化/反序列化功能,降低了安全风险
InitialRAMPercentage默认值变更为0 (JDK-8371986)
-XX:InitialRAMPercentage的默认值从1.5625(即1/64)改为0,JVM将使用自适应机制动态设置初始堆大小,建议直接用-Xms和-Xmx显式指定
以下JVM标志被标记为废弃以备移除:
-Xmaxjitcodesize-XX:+ParallelRefProcEnabled-XX:MaxRAM(建议改用-Xmx)-XX:+AlwaysActAsServerClassMachine-XX:+NeverActAsServerClassMachine-XX:+AggressiveHeapjava.net.SocketPermission废弃以备移除 (JDK-8356557)
java.net.SocketPermission类被标记为废弃,是整体清理Security Manager相关Permission类的延续
setPerformancePreferences方法废弃以备移除 (JDK-8366577)
java.net.Socket、SocketImpl和ServerSocket中的setPerformancePreferences方法被废弃,该方法历来是空操作(no-op),从未实际生效
ObjectMethods.bootstrap方法在权限不足时行为不一致 (JDK-8366424)在某些权限受限的
MethodHandles.Lookup场景下,ObjectMethods.bootstrap方法可能抛出意外异常,行为与预期不符,计划在JDK 27中修复
G1现在更频繁地基于CPU使用率重新评估堆内存需求,实现更动态的堆大小调整。默认GC CPU使用目标从8%降至4%(JDK-8247843),反映G1效率的提升
Serial GC将Eden区移至年轻代的末尾(Survivor区之后),使Eden区在需要时能向年轻代末端扩展以满足分配请求,有助于避免因无法扩展而导致的过早
OutOfMemoryError
修复了JDK 25引入的问题:在THP模式设置为
madvise的系统上,G1现在可以正确使用透明大页(该修复也已向后移植到JDK 25.0.2)
PKCS12格式的KeyStore现在支持使用更现代的PBMAC1算法进行完整性保护,替代旧的MAC方案
ByteOrder转为枚举类型 (JDK-8362637)
java.nio.ByteOrder从普通类转变为枚举,这是规范层面的改进,使其可在switch表达式中直接使用
BigDecimal.sqrt的标度处理修正 (JDK-8370057)修正了
BigDecimal.sqrt()在某些边界情况下的标度(scale)计算问题,使结果更符合规范
C2编译器现在可以正确编译参数数量超过30个的Java方法,此前这类方法会回退到C1编译器或解释执行,影响性能
Windows平台上的监听套接字(ServerSocket)现在支持设置大于200的连接积压队列(backlog),解决了高并发场景下的限制
Java 26共包含10个JEP,整体概括如下:5个正式特性(JEP 500、504、516、517、522),4个预览特性(JEP 524、525、526、530),1个孵化器特性(JEP 529)。本次发布没有新的稳定语言特性,语言层面的增强仍以预览形式推进,重心在于JVM性能提升、网络协议现代化和API完善。正如社区所言:“Java 26是无聊的,但这正是它的优点。”
此次发布了10个JEP,整体概括如下:
JEP | Title | Status | Project | Feature Type | Changes since Java 25 |
|---|---|---|---|---|---|
500 | Prepare to Make Final Mean Final | Core Libs | Library | New feature | |
504 | Remove the Applet API | Client Libs | Deprecation | Removal | |
516 | Ahead-of-Time Object Caching with Any GC | Leyden | Performance | New feature | |
517 | HTTP/3 for the HTTP Client API | Core Libs | New API | New feature | |
522 | G1 GC: Improve Throughput by Reducing Synchronization | HotSpot / GC | Performance | New feature | |
524 | PEM Encodings of Cryptographic Objects | Second Preview | Security Libs | Security | Minor changes |
525 | Structured Concurrency | Sixth Preview | Loom | Concurrency | Minor changes |
526 | Lazy Constants | Second Preview | Core Libs | New API | Major redesign |
529 | Vector API | Eleventh Incubator | Panama | New API | None |
530 | Primitive Types in Patterns, instanceof, and switch | Fourth Preview | Amber | Language | Minor changes |