Java 15 中的新功能
Java 15于 2020 年 9 月 15 日正式发布,请在此处下载 Java 15。
Java 15 特性。
- 1. JEP 339:爱德华兹曲线数字签名算法 (EdDSA)
- 2. JEP 360:密封类(预览)
- 3. JEP 371:隐藏类
- 4. JEP 372:移除 Nashorn JavaScript 引擎
- 5. JEP 373:重新实现旧版 DatagramSocket API
- 6. JEP 374:禁用和弃用偏置锁定
- 7. JEP 375:instanceof 的模式匹配(第二次预览)
- 8. JEP 377:ZGC:可扩展的低延迟垃圾收集器
- 9. JEP 378:文本块
- 10. JEP 379:Shenandoah:低暂停时间垃圾收集器
- 11. JEP 381:删除 Solaris 和 SPARC 端口
- 12. JEP 383:外部内存访问 API(第二孵化器)
- 13. JEP 384:记录(第二次预览)
- 14. JEP 385:弃用 RMI 激活以进行删除
Java 15 开发人员功能。
密封类型(预览)、记录(第二预览)、模式匹配(第二预览)、隐藏类、文本块或多行(标准)、外部内存访问 API(第二孵化器)。
1. JEP 339:爱德华兹曲线数字签名算法 (EdDSA)
与密码学相关的东西,Java 15 使用爱德华兹曲线数字签名算法 (EdDSA)实现了一个额外的数字签名方案,如RFC 8032 所述。EdDSA 签名方案因其比其他签名方案提高了安全性和性能(更快)而广受欢迎,它也是TLS 1.3中允许的签名方案之一。
查看 Java 15签名算法。
示例代码。
package com.mkyong.java15.jep339;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class JEP339 {
public static void main(String[] args)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
byte[] msg = "abc".getBytes(StandardCharsets.UTF_8);
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();
System.out.println(Base64.getEncoder().encodeToString(s));
}
}
进一步阅读
2. JEP 360:密封类(预览)
这JEP推出几个新的关键字,sealed
,non-seal
,permits
支持密封的类和接口。密封的类和接口限制了谁可以是子类型。
2.1 下面的sealed
接口允许三个指定的子类来实现它。
public sealed interface Command
permits LoginCommand, LogoutCommand, PluginCommand{
//...
}
2.2 对于不允许的类,它会抛出编译时错误:
public final class UnknownCommand implements Command {
//...
}
class is not allowed to extend sealed class: Command
2.3 密封类必须有子类并且每个允许的子类必须选择一个修饰符(密封、非密封、最终)来描述它如何继续由其超类发起的密封
final
// close, dun extends me
public final class LoginCommand implements Command{
}
sealed
// another sealed class
// sealed class must have subclasses
public sealed class LogoutCommand implements Command
permits LogoutAndDeleteCachedCommand {
}
// Sealed this class again if you want
public final class LogoutAndDeleteCachedCommand extends LogoutCommand {
}
non-sealed
// open...up to you to play this
// Create custom plugin by extending this class
public non-sealed class PluginCommand implements Command {
}
注意
您注意到关键字了non-sealed
吗?我认为这是 Java 中的第一个连字符关键字。但是,这是一个预览功能;关键字可能会在将来的版本中更改。
2.4 这个密封类或允许的子类和模式匹配。
switch (command) {
case LoginCommand: // login
case LogoutCommand: // logout
case PluginCommand: // custom plugin
// no default needed, only permits 3 sub-classes
}
PS 密封类在 Java 16,JEP 397 中有第二个预览。
进一步阅读
3. JEP 371:隐藏类
3.1 此 JEP 引入了无法发现且生命周期有限(寿命较短)的隐藏类,这对在运行时动态生成类的开发人员有利。现在我们可以使用这个新的Lookup::defineHiddenClass API 从字节创建一个隐藏的类或接口。
3.2 用于defineHiddenClass
从 Base64 编码类创建隐藏类并lookup
手动启动静态方法的示例代码。
package com.mkyong.java15.jep371;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Base64;
public class LookupProxyTest {
//Here is the Base64 encoded class.
/*
package com.mkyong.java15.jep371;
public class LookUpProxy{
public static Integer lookup() {
return 1;
}
}*/
static final String CLASS_IN_BASE64 =
"yv66vgAAADcAFQoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKV" +
"YBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAGbG9va3VwAQAVKClM" +
"amF2YS9sYW5nL0ludGVnZXI7AQAKU291cmNlRmlsZQEAEExvb2tVcF" +
"Byb3h5LmphdmEMAAUABgcAEgwAEwAUAQAkY29tL21reW9uZy9qYXZh" +
"MTUvamVwMzcxL0xvb2tVcFByb3h5AQAQamF2YS9sYW5nL09iamVjdA" +
"EAEWphdmEvbGFuZy9JbnRlZ2VyAQAHdmFsdWVPZgEAFihJKUxqYXZh" +
"L2xhbmcvSW50ZWdlcjsAIQADAAQAAAAAAAIAAQAFAAYAAQAHAAAAHQ" +
"ABAAEAAAAFKrcAAbEAAAABAAgAAAAGAAEAAAADAAkACQAKAAEABwAA" +
"AB0AAQAAAAAABQS4AAKwAAAAAQAIAAAABgABAAAABgABAAsAAAACAAw=";
public static void main(String[] args) throws Throwable {
//byte[] array = Files.readAllBytes(
// Paths.get("/home/mkyong/test/LookUpProxy.class"));
//String s = Base64.getEncoder().encodeToString(array);
//System.out.println(s);
testHiddenClass();
}
// create a hidden class and run its static method
public static void testHiddenClass() throws Throwable {
byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);
Class<?> proxy = MethodHandles.lookup()
.defineHiddenClass(classInBytes,
true, MethodHandles.Lookup.ClassOption.NESTMATE)
.lookupClass();
// output: com.mkyong.java15.jep371.LookUpProxy/0x0000000800b94440
System.out.println(proxy.getName());
MethodHandle mh = MethodHandles.lookup().findStatic(proxy,
"lookup",
MethodType.methodType(Integer.class));
Integer status = (Integer) mh.invokeExact();
System.out.println(status);
}
}
输出
com.mkyong.java15.jep371.LookUpProxy/0x0000000800b94440
1
3.3 此 JEP 还弃用了Unsafe.defineAnonymousClass
API,并将其标记为将来删除。请不要再使用此 API。
byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
sun.misc.Unsafe unsafe = (sun.misc.Unsafe)theUnsafe.get(null);
// @Deprecated(since = "15", forRemoval = false)
Class<?> proxy = unsafe.defineAnonymousClass(
LookupProxyTest.class, classInBytes, null);
进一步阅读
4. JEP 372:移除 Nashorn JavaScript 引擎
- Java 8 JEP 174引入了 Nashorn 作为 Rhino Javascript 引擎的替代品。
- Java 11 JEP 335弃用了 Nashorn JavaScript 引擎和
jjs
工具。 - 现在,Java 15
jjs
永久删除了 Nashorn JavaScript 引擎和工具。
此 JEP 还删除了以下两个模块:
jdk.scripting.nashorn
– 包含jdk.nashorn.api.scripting
和jdk.nashorn.api.tree
包。jdk.scripting.nashorn.shell
– 包含 jjs 工具。
进一步阅读
5. JEP 373:重新实现旧版 DatagramSocket API
- Java 13 JEP 353重新实现了旧的 Socket API——
java.net.Socket
和java.net.ServerSocket
. - 这一次,Java 15 重新实现了遗留的 DatagramSocket API——
java.net.DatagramSocket
和java.net.MulticastSocket
.
进一步阅读
6. JEP 374:禁用和弃用偏置锁定
默认情况下,此 JEP 禁用并弃用了偏向锁定。在 Java 15 之前,默认情况下始终启用偏向锁定,从而提高同步内容的性能。
旧的或遗留的 Java 应用程序使用同步集合 API,如Hashtable
和Vector
,并且偏向锁定可能会提高性能。如今,较新的 Java 应用程序通常使用非同步集合HashMap
和ArrayList
,而偏向锁定的性能提升现在通常不太有用。
但是,对于 Java 15,我们仍然可以使用 启用偏向锁定-XX:+UseBiasedLocking
,但它会提示 VM 警告已弃用的 API。
# Java 15
$ java -XX:+UseBiasedLocking name
OpenJDK 64-Bit Server VM warning: Option UseBiasedLocking was deprecated
in version 15.0 and will likely be removed in a future release.
进一步阅读
7. JEP 375:instanceof 的模式匹配(第二次预览)
Java 14 JEP 305引入了模式匹配作为预览功能。此 JEP 是模式匹配的第二次预览,以获取更多反馈,API 没有变化。
一个典型instanceof-and-cast
的检查对象的类型并转换它。
private static void print(Object obj) {
if (obj instanceof String) { // instanceof
String s = (String) obj; // cast
if ("java15".equalsIgnoreCase(s)) {
System.out.println("Hello Java 15");
}
} else {
System.out.println("not a string");
}
}
对于模式匹配,我们可以在一行中检查、转换和绑定。
private static void printWithPatternMatching(Object obj) {
// instanceof, cast and bind variable in one line.
if (obj instanceof String s) {
if ("java15".equalsIgnoreCase(s)) {
System.out.println("Hello Java 15");
}
} else {
System.out.println("not a string");
}
}
PS 模式匹配是 Java 16, JEP 394 中的标准特性。
进一步阅读
8. JEP 377:ZGC:可扩展的低延迟垃圾收集器
Java 11 JEP 333引入了ZGC 垃圾收集器作为实验性功能。
- 此 JEP 修复了一些错误,添加了一些功能和增强功能,现在支持 Linux/x86_64、Linux/aarch64、Windows 和 macOS 等主要平台。
- 此 JEP 还将 Z 垃圾收集器从实验功能更改为产品功能。但是,默认垃圾收集器仍然是G1。
下面的命令启用 ZGC 垃圾收集器。
$ java -XX:+UseZGC className
进一步阅读
9. JEP 378:文本块
最后,多行字符串或文本块是 Java 15 中的永久特性。
历史:
- Java 12 JEP 326 原始字符串文字
- Java 13 JEP 355:文本块(预览)
- Java 14 JEP 368:文本块(第二次预览)
- Java 15,永久功能。
示例代码。
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, World</p>\n" +
" </body>\n" +
"</html>\n";
String java15 = """
<html>
<body>
<p>Hello, World</p>
</body>
</html>
""";
进一步阅读
10. JEP 379:Shenandoah:低暂停时间垃圾收集器
Java 12 JEP 189引入了Shenandoah 垃圾收集器作为实验性功能,现在成为 Java 15 中的产品功能。
在 Java 15 之前,我们需要-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
启用 Shenandoah GC。
java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
在 Java 15 中,我们只需要-XX:+UseShenandoahGC
启用 Shenandoah GC。
java -XX:+UseShenandoahGC
但是,官方的OpenJDK 15版本不包括 Shenandoah GC(就像 Java 12 中发生的那样)。阅读这个故事 –并非所有 OpenJDK 12 版本都包含 Shenandoah:这就是为什么.
$ java -XX:+UseShenandoahGC ClassName
Error occurred during initialization of VM
Option -XX:+UseShenandoahGC not supported
$ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment (build 15+36-1562)
OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)
要尝试 Shenandoah GC,我们需要其他 JDK 构建版本,例如AdoptOpenJDK。
$ java -XX:+UseShenandoahGC ClassName
$ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)
PS 默认垃圾收集器仍然是G1。
进一步阅读
11. JEP 381:删除 Solaris 和 SPARC 端口
Java 14 JEP 362弃用了 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口,现在它在 Java 15 中被正式删除。
进一步阅读
12. JEP 383:外部内存访问 API(第二孵化器)
Java 14 JEP 370引入了一个新的外部内存访问 API 作为孵化器模块。此 JEP 对 API 进行了一些更改,它仍将位于孵化器模块中。
示例代码
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
public class HelloForeignMemory {
public static void main(String[] args) {
VarHandle intHandle = MemoryHandles.varHandle(
int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(1024)) {
MemoryAddress base = segment.baseAddress();
// print memory address
System.out.println(base);
// set value 999 into the foreign memory
intHandle.set(base, 999);
// get the value from foreign memory
System.out.println(intHandle.get(base));
}
}
}
我们需要添加--add-modules jdk.incubator.foreign
以启用孵化器模块jdk.incubator.foreign
。
$ javac --add-modules jdk.incubator.foreign HelloForeignMemory.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
$ java --add-modules jdk.incubator.foreign HelloForeignMemory
WARNING: Using incubator modules: jdk.incubator.foreign
MemoryAddress{ region: MemorySegment{ id=0x27c908f5 limit: 1024 } offset=0x0 }
999
进一步阅读
13. JEP 384:记录(第二次预览)
Java 14 JEP 359引入了记录作为预览功能。此 JEP 通过支持密封类型、本地记录、记录注释和记录反射 API 等功能增强了记录。
13.1 记录和密封类型
public sealed interface Fruit permits Apple, Orange {}
record Apple() implements Fruit{}
record Orange() implements Fruit{}
13.2 本地记录——在方法内部声明的记录。
下面的代码片段使用本地记录来提高流操作的可读性。
List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
// Local record
record MerchantSales(Merchant merchant, double sales) {
}
return merchants.stream()
.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
.map(MerchantSales::merchant)
.collect(toList());
}
private double computeSales(Merchant merchant, int month) {
// some business logic to get the sales...
return ThreadLocalRandom.current().nextDouble(100, 10000);
}
13.3 记录注释
public record Game(@CustomAnno Rank rank) { ... }
13.4 反射API
在java.lang.Class
增加了两个公共方法:
RecordComponent[] getRecordComponents()
boolean isRecord()
PS 记录是 Java 16, JEP 395 中的标准特性。
进一步阅读
14. JEP 385:弃用 RMI 激活以进行删除
该 JEP 弃用了过时的RMI 激活机制。这不会影响 RMI 的其他部分。
进一步阅读
下载源代码
$ git clone https://github.com/mkyong/core-java
$ cd java-15