Java15是一个重要的功能发布版本,它为Java带来了许多JVM特定的变化和语言特定的变化。它遵循了从Java10开始引入的Java发布节奏,并在Java14发布后的六个月后于2020年9月发布。
Java15新特性如下
339: | Edwards-Curve Digital Signature Algorithm (EdDSA) 爱德华兹曲线数字签名算法 |
360: | Sealed Classes (Preview) 封闭类(预览) |
371: | Hidden Classes 隐藏类 |
372: | Remove the Nashorn JavaScript Engine 删除Nashorn JavaScript引擎 |
373: | Reimplement the Legacy DatagramSocket API 重新实现传统DatagramSocket API |
374: | Disable and Deprecate Biased Locking 禁用和弃用偏置锁定 |
375: | Pattern Matching for instanceof (Second Preview) instanceof匹配增强(第二次预览) |
377: | ZGC: A Scalable Low-Latency Garbage Collector Z垃圾收集器 |
378: | Text Blocks 文本块 |
379: | Shenandoah: A Low-Pause-Time Garbage Collector Shenandoah垃圾收集算法 |
381: | Remove the Solaris and SPARC Ports 删除Solaris and SPARC Ports |
383: | Foreign-Memory Access API (Second Incubator) 外部内存(第二次孵化) |
384: | Records (Second Preview) Records类(第二次预览) |
385: | Deprecate RMI Activation for Removal 弃用RMI |
JEP 339 Edwards-Curve Digital Signature Algorithm (EdDSA)
在公钥密码学中,爱德华兹曲线数字签名算法( EdDSA ) 是一种数字签名方案,它使用基于扭曲爱德华兹曲线的Schnorr 签名变体。它被设计为比现有的数字签名方案更快而不牺牲安全性。
在Java中,EdDSA(爱德华兹曲线数字签名算法)[ RFC 8032 ] 是在Java 15中通过JEP 339添加的另一种附加数字签名方案。它不会取代 JDK 中现有的椭圆曲线数字签名算法( ECDSA )。
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
byte[] msg = "test_string".getBytes(StandardCharsets.UTF_8);
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();
String encodedString = Base64.getEncoder().encodeToString(s);
System.out.println(encodedString);
//KtLEZJYcqAUZ+tggEKfv0JmpEKM76ICjZb2hTivK20VQnlw9rWrD9JHjdsLGJgccNNQECrlp6GrYPIoBd5qzAg==
JEP 360 Sealed Classes (Preview)
见文章 java15学习笔记-密封类
JEP 371 Hidden Classes
隐藏类(JEP 371)是 Java 15 引入的底层机制,允许在运行时动态生成 不可发现、不可反射访问 的类,主要用于:
框架内部实现:如动态代理、字节码生成工具(如 CGLIB、Byte Buddy)。
性能优化:减少反射调用开销,避免类污染全局命名空间。
核心特性:
不可发现性:无法通过类加载器或反射 API 直接访问。
生命周期隔离:与定义它们的上下文(如方法)绑定,执行结束后可被垃圾回收。
安全性增强:限制外部对动态生成代码的访问,降低反射攻击风险
不可发现性:无法通过类加载器或反射 API 直接访问。
生命周期隔离:与定义它们的上下文(如方法)绑定,执行结束后可被垃圾回收。
安全性增强:限制外部对动态生成代码的访问,降低反射攻击风险。
三、应用场景与案例
动态代理框架:
Spring AOP 使用隐藏类生成代理对象,避免类名冲突和反射开销。
代码生成工具:
Byte Buddy 在运行时生成隐藏类,用于实现 AOP、ORM 等功能。
模块化隔离:
在 JPMS 模块系统中,隐藏类可实现模块内部的私有服务。
四、与传统反射的对比
隐藏类 | 传统反射 | |
可见性 | 仅对定义者可见 | 全局可见 |
性能 | 接近直接调用 | 有显著性能损耗 |
安全性 | 无法通过反射访问 | 存在反射攻击风险 |
生命周期 | 与定义上下文绑定 | 由类加载器管理 |
适用场景 | 框架内部实现、性能敏感 | 通用动态调用 |
五、示例代码
jar包放在了开头下载部分
public static void main(String[] args) throws Throwable {
byte[] bytes = generate();
// 使用自定义的ClassLoader
MyClassLoader cl = new MyClassLoader();
// 加载我们生成的 HelloWorld 类
Class clazz = cl.defineClass("HelloWorld", bytes);
// 反射获取 main 方法
Method main = clazz.getMethod("main", String[].class);
// 调用 main 方法
main.invoke(null, new Object[]{new String[]{}});
}
/**
* 自定义ClassLoader以支持加载字节数组形式的字节码
*
*/
static class MyClassLoader extends ClassLoader {
public Class defineClass(String name, byte[] b) {
// ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
// 所以我们需要定义一个子类将这个方法暴露出来
return super.defineClass(name, b, 0, b.length);
}
}
private static byte[] generate() {
ClassWriter cw = new ClassWriter(0);
// 定义对象头:版本号、修饰符、全类名、签名、父类、实现的接口
cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "HelloWorld",
null, "java/lang/Object", null);
// 添加方法:修饰符、方法名、描述符、签名、抛出的异常
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
// 执行指令:获取静态属性
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
// 加载常量 load constant
mv.visitLdcInsn("""
你是我的小呀小苹果,
怎么爱你都不嫌多
""");
// 调用方法
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
// 返回
mv.visitInsn(Opcodes.RETURN);
// 设置栈大小和局部变量表大小
mv.visitMaxs(2, 1);
// 方法结束
mv.visitEnd();
// 类完成
cw.visitEnd();
// 生成字节数组
return cw.toByteArray();
}
JEP 372 Remove the Nashorn JavaScript Engine
删除Nashorn JavaScript脚本引擎和API,以及jjs工具。引擎、API和工具在Java 11中已被弃用,并明确表示将在未来的版本中删除。
一方面没人用,另一方面ES技术的发展难以支撑。
两个JDK模块将被永久删除:
jdk.scripting.nashorn——包含jdk.nashorn.api.scripting和jdk.nashoren.api.tree包。
jdk.scripting.nashorn.shell——包含jjs工具。
JEP 373 Reimplement the Legacy DatagramSocket API
java.net.DatagramSocket 和 java.net.MulticastSocket API 的旧实现被更简单、更现代化的实现所取代,这样更容易维护和调试。
java.net.datagram.Socket 和 java.net.MulticastSocket 的当前实现是在 JDK 1.0 中引入的,而IPv6仍在开发中。这个 JEP 的目标是替换当前的 MulticastSocket 实现,该实现以难以维护的方式调和了IPv4和IPv6。
JEP 374: Deprecate and Disable Biased Locking
默认情况下禁用偏向锁,并弃用所有相关的命令行选项。
偏向锁是锁上面又加了一个标志类型的锁,当多线程争用资源时,如果是当前线程拥有该资源,偏向锁会避免相同资源的原子操作,从而可以减少无竞争锁的开销。
当多线程以单线程方式使用的对象执行许多同步操作时,偏置锁在历史上比常规锁定技术带来了显著的性能改进。
现实情况下,不会很少甚至不会出现大量线程同步操作。少线程争用的情况下偏向锁的性能甚至不如原子操作。所以决定弃用
使用下面命令可以开启偏向锁
XX:+UseBiasedLocking。
JEP 375: Pattern Matching for instanceof (Second Preview)
这是第二次预览,第一次预览在Java14,后续在Java16正式上线
在Java14之前,instanceof主要用来检查对象的类型,检查匹配后,还需要对其进行类型强转才能使用该类型的变量,这显得很多余也很业余,而且存在手滑转错类型的可能。
java14之前 instanceof使用如下
Object obj = "Java14Test";
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str);
}
在 Java 14 中,使用模式匹配的 instanceof,这可以被简化为:
Object obj = "Java14Test";
if (obj instanceof String s) {
System.out.println(s);
}
实际使用案例
public static void main(String[] args) throws ScriptException {
Object obj = "Java14Test";
Boolean b = getString(obj);
System.out.println(b); //false
Object obj1 = "大傻春";
Boolean b1 = getString(obj1);
System.out.println(b1); //true
}
public static Boolean getString(Object o)
{
return(o instanceof String string ) && string == "大傻春";
}
注意不要和 || 一起使用 例如
(o instanceof String string ) || string == "大傻春";
逻辑上不会出现这种逻辑,另外这种写法编译也报错
JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)
新的垃圾回收期ZGC简称Z垃圾回收期,虽然已经正式生产,但是还是建议使用G1为GC。
自 JDK 11 作为实验性功能引入,并在后续版本(如 JDK 15+)中逐步成熟。ZGC 的核心设计目标是 低延迟 和 大堆内存管理能力,能够将垃圾收集的停顿时间(STW, Stop-The-World)控制在 10ms 以下,甚至达到亚毫秒级。
JEP 378: Text Blocks
文本块最早是在java12提出的概念,但是被撤回JEP 326: Raw String Literals (Preview)->java13开始第一次原来->java14第二次预览->Java15正式上线
String x = """
法外狂徒张三
\s 精神支柱快乐水 \
罗老师
""";
x = x+"xxx";
System.out.println(x);
JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production)
Java12开始就引入了第一版的Shenandoah GC,相比于G1它的新特性在于可以并发的进行垃圾回收,其更像是G1的下一代继承者。它们两者有着相似的堆内存堆内存堆内存布局,在初始标记、并发标记等许多阶段的处理思路上都高度一致,甚至还直接共享了一部分实现代码。
JEP 381: Remove the Solaris and SPARC Ports
目前正在开发的许多项目和功能,如Valhalla、Loom和Panama,都需要对CPU架构和操作系统特定代码进行重大更改。放弃对Solaris和SPARC端口的支持将使OpenJDK社区的贡献者能够加速开发新功能,从而推动平台向前发展。
JEP 383: Foreign-Memory Access API (Second Incubator)
引入API,使Java程序能够安全有效地访问Java堆之外的外部内存。
Foreign-Memory Access API是由JEP 370提出的,并于2019年末针对Java 14作为一个正在孵化的API。该JEP建议结合基于反馈的改进,并在Java15中重新创建API。作为此API刷新的一部分,包含了以下更改:
一个丰富的VarHandle组合子API,自定义内存访问var句柄;
通过Splitterator接口对内存段的并行处理提供有针对性的支持;
增强了对映射内存段的支持(例如,MappedMemory Segment::force);
安全的API指向支持串行限制(例如,在两个线程之间转移线程所有权);
不安全的API指向操作和取消引用来自例如本机调用的地址,或者将这些地址包装到合成内存段中。
JEP 384: Records (Second Preview)
JEP 359于2019年年中提出了记录,并于2020年初在JDK 14中作为第一次预览。此JEP建议重新预览JDK 15中的该功能,以包含基于反馈的改进,并支持Java语言中其他形式的本地类和接口。
java14学习笔记-part1-JEP 359 (record类)
JEP 385: Deprecate RMI Activation for Removal
弃用RMI激活机制,将来将进行删除。RMI激活是RMI的一个过时部分,自Java 8以来一直是可选的。RMI的任何其他部分不会被弃用。
由于一下原因,所以做出此决定。
RMI激活已过时
RMI激活大多未使用
RMI激活会带来持续的维护负担