JDK各版本新特性(下)
What's new from Java?
java14
一、switch表达式(标准版)
switch表达式在java12与java13中进行了两次增强,经过测试之后,在java14中此修改已经正式转正。
详情请看本文上方java12与java13中的内容,或见java12 jep325和java13 jep354。
二、instanceof 的模式匹配(预览版)
在此前,我们使用instanceof 来判断一个对象是否属于某个指定的类或其子类的实例,如果是,我们再进行转换,如:
if (obj instanceof String) {
String s = (String) obj;
// use s
}
在上方代码可以看出,我们总共进行了三步:
- obj对象是否为String类?
- obj转为String
- 新的局部变量s的声明
这种模式很简单,所有 Java 程序员都能理解,但由于几个原因,它不是最理想的。第一步为true之后第二步显得有点多此一举。因为类型匹配了之后下一步往往都是类型转换了。于是有了java14中的新语法:
//如果obj为String类型,那么可以直接在if语句中转换并定义为s
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
三、java打包工具
许多 Java 应用程序需要以一流的方式安装在本机平台上,而不是简单地放在类路径或模块路径上。应用程序开发人员仅提供一个简单的 JAR 文件是不够的;他们必须提供适合本机平台的可安装包。这允许以用户熟悉的方式分发、安装和卸载 Java 应用程序。
java14中引入了jpackage工具,使用 jpackage 命令可以把 JAR 包打包成不同操作系统支持的软件格式。
常见平台的打包格式如下:
- Linux:deb和rpm
- macOs:pkg和dmg
- Windows:msi和exe
四、更有用的NullPointerExceptions
通过准确描述哪个变量为空来提高 JVM 生成的 NullPointerExceptions 的可用性。
在java14之前,如果在一行代码中有多个表达式时出现了 NullPointerExceptions 的话我们无法确定到底是哪个对象为 null。在java14,现在会直接的告诉你哪个对象为null。如下:
public static void main(String[] args) {
String s1 = "string1";
String s2 = null;
int countLength = s1.length() + s2.length();
System.out.println(countLength);
}
java14前报错如下:
Exception in thread "main" java.lang.NullPointerException
at com.zzl.jdk14.NullPointerTest.main(NullPointerTest.java:12)
java14报错
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "s2" is null
at com.zzl.jdk14.NullPointerTest.main(NullPointerTest.java:12)
打印详细错误信息命令(默认为不开启):
-XX:{+|-}ShowCodeDetailsInExceptionMessages
五、字符串文本块(二次预览版)
字符串文本块是java13新增的功能,详情请看上方。
在本次更新中,新增了两个转义符:
\:表示结尾不换行\s:表示一个空格
示例:
String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et\sdolore\smagna aliqua.\
""";
//输出
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor et dolore magna aliqua.
六、Record(预览版)
Record是 Java 语言中一种新的类型声明。像枚举一样,Record是类的受限形式。Record放弃了类通常享有的自由:将 API 与表示分离的能力。作为回报,Records获得了相当程度的简洁性。
定义一个Record:
record Point(int x, int y) { }
因为Records的语义声称是简单、透明的数据持有者,所以Records会默认存在许多特性:
- 所有属性都是
final的 - 会自动创建一个
public的构造方法,用于初始化所有属性 - 会自动构建
public的equals和hashCode方法,它们的实现表示如果两条记录具有相同的类型并且包含相同的状态,则它们是相等的 - 会自动构建
public的toString方法,包括所有记录组件的字符串表示形式及其名称
Class类新增反射API:
-
RecordComponent[] getRecordComponents()
java.lang.reflect.RecordComponent是一个新类。此数组的元素对应于记录的组件,其顺序与它们在记录声明中出现的顺序相同。可以从RecordComponent数组中的每一个中提取附加信息,包括其名称、类型、泛型类型、注释及其访问器方法。 -
boolean isRecord() 如果给定的类被声明为 Record,则该方法返回 true
七、JFR事件流
公开 JDK Flight Recorder 数据以进行持续监控。HotSpot VM 使用 JFR 发出超过 500 个数据点,其中大部分无法通过解析日志文件之外的其他方式获得。模块jdk.jfr 中的包 jdk.jfr.consumer 扩展了异步订阅事件的功能。用户可以直接从磁盘存储库中读取录制数据或流式传输,而无需转储录制文件。
基本示例:
public static void main(String[] args) throws IOException, ParseException {
Configuration c = Configuration.getConfiguration("default");
try (var rs = new RecordingStream(c)) {
//打印GC信息
rs.onEvent("jdk.GarbageCollection", System.out::println);
//打印CPU负载
rs.onEvent("jdk.CPULoad", System.out::println);
//打印JVM信息
rs.onEvent("jdk.JVMInformation", System.out::println);
rs.start();
}
}
八、其它
-
G1 的 NUMA 感知内存分配
通过实施 NUMA 感知内存分配来提高大型机器上的 G1 性能。
通过
+XX:+UseNUMA参数来启用此功能。 -
macOS上的ZGC(实验性)
将 ZGC 垃圾收集器移植到 macOS。
通过
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC参数来在macOS中启用。 -
Windows上的ZGC(实验性)。
将 ZGC 垃圾收集器移植到 Windows。
通过
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC参数来在Windows中启用。
java15
一、密封类(预览版)
使用密封的类和接口增强 Java 编程语言。密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。
在 Java 中,类层次结构允许通过继承重用代码:超类的方法可以被许多子类继承(并因此重用)。
在此之前,在这方面提供了有限的工具:要么创建一个类 final,因此它有零个子类,要么将一个类或其构造函数设为包私有,因此它只能在同一个包中具有子类。如:
package java.lang;
abstract class AbstractStringBuilder {...}
public final class StringBuffer extends AbstractStringBuilder {...}
public final class StringBuilder extends AbstractStringBuilder {...}
在java15开始,可以通过将 sealed 修饰符应用于其声明来密封类,用 permits 关键字来指定哪些类可以继承、实现此类,密封的类或接口只能由允许这样做的类和接口扩展或实现。如:
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square {...}
permits 指定的类必须位于超类附近:在同一个模块中(如果超类在命名模块中)或在同一个包中(如果超类在未命名模块中)。子类继承、实现父密封类之后,每个允许的子类必须使用一个且只有一个修饰符 final、sealed 和 non-sealed。
- final:最终类,不能再有子类
- sealed:密封类,可以指定允许继承的类
- non-sealed:非密封类,允许所有类继承
示例如下:
package com.example.geometry;
//定义一个Shape的抽象密封类
public abstract sealed class Shape
permits Circle, Rectangle, Square {...}
//定义一个最终的Circle类并继承Shape,Circle将不能再有子类
public final class Circle extends Shape {...}
//定义了一个继承Shape的Rectangle密封类,并指定了TransparentRectangle、FilledRectangle才能够继承它
public sealed class Rectangle extends Shape
permits TransparentRectangle, FilledRectangle {...}
public final class TransparentRectangle extends Rectangle {...}
public final class FilledRectangle extends Rectangle {...}
//定义了一个非密封的Square,它可以被任意类继承
public non-sealed class Square extends Shape {...}
二、隐藏类
隐藏类,即不能被其他类的字节码直接使用的类。隐藏类旨在供在运行时生成类并通过反射间接使用它们的框架使用。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类卸载。
许多基于 JVM 的语言实现依赖于动态类生成来实现灵活性和效率。例如,在 Java 语言的情况下,javac 不会class 在编译时将 lambda 表达式转换为专用文件,而是发出字节码,动态生成并实例化一个类,以便在需要时生成与 lambda 表达式对应的对象。类似地,非 Java 语言的运行时通常通过使用动态代理来实现这些语言的高阶特性,动态代理也动态生成类。
基本示例:
/**
* 提前准备一个类,并编译成为字节码
*/
public class HideClass {
public static void test(){
System.out.println("HideClass::test");
}
}
public class HideClassTest {
public static void main(String[] args) throws Throwable {
//加载编译后的字节码
FileInputStream inputStream = new FileInputStream("E:\\HideClass.class");
byte[] bytes = inputStream.readAllBytes();
//从字节数组加载类
Class<?> proxy = MethodHandles.lookup().defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.NESTMATE).lookupClass();
//打印类名
System.out.println(proxy.getName());
//获取HideClass的test方法
MethodHandle methodHandle = MethodHandles.lookup().bind(proxy.getConstructor().newInstance(), "test", MethodType.methodType(Void.TYPE));
//反射调用test方法
methodHandle.invokeExact();
}
}
//输出
com.zzl.jdk15.HideClass/0x0000000800bb2040
HideClass::test
三、Record(二次预览)
记录是 Java 语言中的一种新类。记录的目的是声明一小组变量将被视为一种新的实体。在java14中上线预览,详情看上方java14。
在java15中,Record进行了增强:
-
Record支持密闭类型
package com.example.expression; public sealed interface Expr permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...} public record ConstantExpr(int i) implements Expr {...} public record PlusExpr(Expr a, Expr b) implements Expr {...} public record TimesExpr(Expr a, Expr b) implements Expr {...} public record NegExpr(Expr e) implements Expr {...} -
支持注解,如:
public final class Card { private final @MyAnno Rank rank; private final @MyAnno Suit suit; @MyAnno Rank rank() { return this.rank; } @MyAnno Suit suit() { return this.suit; } ... }可以转为更简洁中的Record如下:
public record Card(@MyAnno Rank rank, @MyAnno Suit suit) { ... }注意:
- 如果Record组件上的注解适用于字段声明,则注释出现在相应的
private字段上 - 如果Record组件上的注解适用于方法声明,则该注释出现在相应的访问器方法上
- 如果Record组件上的注解适用于形式参数,则注释出现在规范构造函数的相应形式参数(如果未显式声明)或紧凑构造函数的相应形式参数(如果显式声明)
- 如果Record组件上的注解适用于类型,则传播规则与声明注释相同,只是注解出现在相应的类型使用而不是声明上
- 如果Record组件上的注解适用于字段声明,则注释出现在相应的
四、其它
-
爱德华兹曲线数字签名算法
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519"); KeyPair kp = kpg.generateKeyPair(); byte[] msg = "www.zzlcjj.xyz".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)); } -
重新实现旧的 DatagramSocket API
-
移除 Nashorn JavaScript 引擎
-
弃用和禁用偏向锁定
-
ZGC:可扩展的低延迟垃圾收集器(生产)
将 Z 垃圾收集器从实验功能更改为产品功能。
通过
-XX:+UseZGC命令行选项启用 -
Shenandoah:低暂停时间垃圾收集器(生产)
通过
-XX:+UseShenandoahGC命令行选项启用 -
删除 Solaris 和 SPARC 端口
-
支持Unicode 13.0
-
新增CharSequence类的
isEmpty()默认方法,用于判断字符串序列是否为空