一、Java 16
发布于2021年3月16日。Java 16主要特性有:
JEP 338:Vector API (Incubator) 向量 API(第一轮孵化)
JEP 387:Elastic Metaspace 弹性元空间
JEP 389:Foreign Linker API (Incubator) 外部链接器API (第一轮孵化)
JEP 393:Foreign-Memory Access API (Third Incubator)外部内存访问 API(第三轮孵化)
JEP 394:Pattern Matching for instanceof instanceof的模式匹配(转正)
JEP 395:Records Records(转正)
JEP 397:Sealed Classes (Second Preview) 封闭类(第二次预览)
更多内容请读者自行阅读:OpenJDK Java 16官方文档
1.1 JEP 338:向量API(第一轮孵化)
现代计算中,数据密集型操作非常常见,例如科学计算、机器学习和图形处理等,这些可以通过向量化来加速(同时对多个数据执行相同操作),但是,Java缺乏这种向量计算能力。
所以在Java 16引入向量API,旨在解决这些问题,通过提供一种机制,使得在 Java 中编写复杂的矢量算法成为可能,利用 HotSpot 对矢量化的现有支持,但采用一种使矢量化更加可预测和稳健的用户模型。手动编写的矢量循环可以表达高性能算法(例如矢量化的 hashCode
或专门的数组比较),这些算法可能是自动矢量化器无法优化的。
举个例子:
//简单标量计算
void scalarComputation(float[] a,float[] b,float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i]*a[i]*b[i]*b[i]) * -1.0f;
}
}
//与上述等效的向量计算
void vectorComputation(float[] a,float[] b,float[] c) {
int i =0;
VectorSpecies<Float> species = FloatVector.SPECIES_PREFERRED;
int upper = species.loopBound(a.length);
for(;i<upper;i+=species.length()){
var va=FloatVector.fromArray(species,a,i);
var vb=FloatVector.fromArray(species,b,i);
var vc=va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c,i);
}
}
ps:由于一直处于孵化阶段,所以不排除会有重大变化,进入预览阶段再详细介绍。
1.2 JEP 387:弹性元空间
自Java 8引入元空间以来,它一直因对外内存使用率过高而颇具争议,原因是很容易以错误的方式触发元空间分配器,从而导致过多的内存浪费。
此特性将元空间内存按较小的块分配,并将未使用的元空间内存返回给操作系统来提高弹性,从而提高应用程序性能并降低内存占用。
1.3 JEP 389:外部链接器API(第一轮孵化)
该特性是基于外部内存访问API基础上建立起来的,旨在提供纯Java访问原生代码的API,该API将大大简化绑定原生库原本复杂且容易出错的过程。
1.4 JEP 393:外部内存访问API(第三轮孵化)
外部内存访问 API 最初由 JEP 370 提出,并于 2019 年底作为孵化 API 针对 Java 14。
后来由 JEP 383 重新孵化,集成与Java 15中。
Java 16建议继续孵化。
1.5 JEP 394:instanceof模式匹配(转正)
instanceof 的模式匹配由 JEP 305 提出,并在 JDK 14 中作为预览功能提供。
它由 JEP 375 重新提出,并在 JDK 15 中提供,用于第二轮预览。
在Java 16成为正式特性。
摘自Java 14的例子:
public static void main(String[] args) {
Object obj = new Object();
if(obj instanceof String){
//强转为String
String str = (String) obj;
String trim = str.trim();
}
//Java 14
if(obj instanceof String str){
//不必强转,直接使用
String trim = str.trim();
}
}
1.6 JEP 395:Records(转正)
Records由 JEP 359 提出,并在 JDK 14 中作为预览功能提供。
JEP 384 对设计进行了细微改进,并在 JDK 15 中作为预览功能第二次提供。
在Java 16成为正式特性。
Record类型允许在代码中使用紧凑的语法形式来声明类,而且这些类能够作为不可变数据类型的封装持有者。
以下内容摘自Java14对Records的介绍:
Java 14以预览功能的形式引入了一个新特性:Records,主要目的是提供一种简洁的语法来声明类似数据的小型不可变对象,用于解决长期以来Java中定义纯数据载体类带来的繁琐问题。
Java语言架构师Brian Goetz曾经写过一篇文章(http://cr.openjdk.java.net/~briangoetz/amber/datum.html )提到:开发人员想要创建纯数据甾体类通常必须编写大量低价值、重复的、容易出错的代码,比如构造函数、getter/setter、equal()、hashCode()等方法。以至于很多人选择使用IDE的功能自动生成这些方法,还有一些会选择使用一些第三方类库,比如Lombok:
那么纯数据载体指的是什么呢,举个例子:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {}
//省略getter/setter()、toString()、hashCode()等方法
}
我们再看看Records的写法:
public record Student1(String name, int age) {
}
//使用
public static void main(String[] args) {
Student1 student=new Student1("张三",12);
int age = student.age();
String name = student.name();
}
反编译一下:
public final class Student1 extends Record {
private final String name;
private final int age;
public Student1(String string, int n) {
this.name = string;
this.age = n;
}
@Override
public final String toString() {
return ObjectMethods.bootstrap("toString", new MethodHandle[]{Student1.class, "name;age", "name", "age"}, this);
}
@Override
public final int hashCode() {
return (int)ObjectMethods.bootstrap("hashCode", new MethodHandle[]{Student1.class, "name;age", "name", "age"}, this);
}
@Override
public final boolean equals(Object object) {
return (boolean)ObjectMethods.bootstrap("equals", new MethodHandle[]{Student1.class, "name;age", "name", "age"}, this, object);
}
public String name() {return this.name;}
public int age() { return this.age;}
}
通过反编译得到的类(妥妥的是一个语法糖啊),我们可以看到:
生成了一个final修饰的Student1类,说明这个类不能有子类了。
类中有两个private final修饰的属性,所以record中的属性都是private final修饰的。
一个构造函数、两个getter方法还有hashCode()、toString()、equals()都是自动生成的。
还能干啥?前面的例子中我们创建了一个record,感觉空荡荡的,还能有其它成员变量和方法吗?当然能:
public record Student1(String name, int age) {
//只能添加静态字段
static int x;
//静态方法
public static void doSomething() {
x++;
}
//实例方法
public String getInfo(){
return "姓名:"+name+",年龄:"+age;
}
//构造函数
public Student1{
if(age>=35){
throw new IllegalArgumentException("年龄过大!!!");
}
}
}
就介绍这么多吧,总结一下它的优缺点:
优点:record非常适合作为再各层间传递数据载体,比如DTO;且它是不可变的,这也说明可以确保线程安全。
缺点:由于是不可变的,所以没有setter方法,通用性比较弱;且如果参数过多,写起来和看起来都不如传统的写法。
1.3 JEP 397:封闭类(二次预览)
封闭类由Java 15作为预览功能引入的,主要目的是为了限制类的继承情况,Java 16第二次预览,仅做了细微改动。
以下内容摘自Java 15对封闭类的介绍:
Java 15之前,Java认为"代码重用"始终是一个终极目标,所以一个类或接口都可以被任意的类继承或实现。
但是在很多场景中,这样做反而容易造成错误,比如某些公司只要具有985或211学历的人;那么在Java中创建Company抽象类时,应该只允许Work985和Work211类去扩展它。
通过这种方式,我们希望确保在域内不会出现误用Worker抽象类的情况,为了解决类似问题,Java 15引入了封闭类:
封闭类用sealed修饰,它的所有子类都必须在同一个模块或包内。
封闭类使用permits来指定允许继承的子类。
封闭类的子类可以被声明为non-sealed或final。non-sealed可以被继承,final则不能。
//密封类
//接口写法
public sealed interface Company permits Worker985,Worker211 {
}
//抽象类写法
public abstract sealed class Company1 permits Worker985,Worker211 {
}
//子类
public final class Worker985 implements Company {
}
//非密封子类
public non-sealed class Worker211 implements Company {
}
二、Java 17(LTS版本)
Java 17于2021年9月14日发布,是一个LTS版本,共有14个特性,本节将精选以下几个特性介绍:
JEP 356:Enhanced Pseudo-Random Number Generators 增强的伪随机数生成器
JEP 406:Pattern Matching for switch (Preview) switch的模式匹配(一次预览)
JEP 409:Sealed Classes 封闭类(转正)
JEP 412:Foreign Function & Memory API (Incubator) 外部函数和内存 API (第一轮孵化)
JEP 414:Vector API (Second Incubator) 向量API(第二轮孵化)
更多内容请读者自行阅读:OpenJDK Java 17官方文档
2.1 JEP 356:伪随机数生成器增强
在Java 17之前,Java主要依赖java.util.Random和其子类来生成伪随机数:
Random:最基本的随机数生成器,线程同余(LCG)算法,效率不高,线程不安全。
ThreadLocalRandom:解决Random线程不安全的问题,同样是LCG算法,效率不高。
SecureRandom:生成的随机数具有高安全性,使用了更加复杂和不可预测的算法。
这些生成器在某些场景下不够高效且功能性不够多,针对这些问题,Java 17为随机数提供了一个全新的接口RanmdomGenerator,它的子类有:
Streamablegenerator:可以产生随机数流的生成器。
JumpableGenerator:可以“跳跃”到其序列中一个远端点的随机数生成器。
LeapableGenerator:类似于JumpableGenerator,但提供了更精细的控制。
ArbitrarilyJumpableGenerator:可以跳到其序列中任意点的生成器。
SplittableGenertor:可以分裂成两个或多个独立运行的随即谁生成器。
举个例子:
public static void main(String[] args) {
//指定生成器的算法,默认是L32X64MixRandom
RandomGenerator randomGenerator = RandomGenerator.of("L64X128MixRandom");
randomGenerator.ints().limit(10).forEach(System.out::println);
}
下面是Java 17提供的11个伪随机算法:
2.2 JEP 406:Switch模式匹配(一次预览)
Java 17引入了模式匹配,增强了Switch的类型检查,允许case标签中不仅带有变量,还能带有模式匹配。
在Java 17之前,我们可能需要使用多个if-else来判断对象的类型,比如:
public static void main(String[] args) {
Object obj = new Object();
if(obj instanceof String str){
System.out.println("字符串:"+str);
}else if(obj instanceof Integer i){
System.out.println("整数:"+i);
}else if(obj instanceof List<?> list){
System.out.println("集合:"+list);
}else {
System.out.println("unknown");
}
}
Java 17就可以用Swicth模式匹配来简化代码:
public static void main(String[] args) {
Object obj = new Object();
switch (obj){
case String str->System.out.println("字符串:"+str);
case Integer i->System.out.println("整数:"+i);
case List<?> list->System.out.println("集合:"+list);
default -> System.out.println("unknown");
}
}
2.3 JEP 409: 封闭类(转正)
密封类由 JEP 360 提出,并在 JDK 15 中作为预览功能提供。
JEP 397 再次提出了这些建议,并对其进行了改进,并在 JDK 16 中作为预览功能提供。
JDK 17封闭类成为正式特性,与 JDK 16 相比没有变化。
ps:参考1.3小节
2.4 JEP 412:外部函数和内存API(第一轮孵化)
改进了 JDK 14 和 JDK 15 中引入的孵化 API,使 Java 程序能够与 Java 运行时之外的代码和数据进行互操作。通过有效地调用外部函数(即 JVM 之外的代码)和安全地访问外部内存,这些 API 使 Java 程序能够调用本地库和处理本地数据,而不会像 Java 本地接口 (JNI) 那样脆弱和复杂。
该JEP 中提出的 API 是两个孵化 API 的演变:外部内存访问 API 和外部链接器 API。
外部内存访问 API 最初由 JEP 370 提出,并于 2019 年底作为孵化 API 针对 Java 14;
它在 Java 15 中由 JEP 383 和 Java 16 中的 JEP 393 重新孵化。外部链接器 API 最初由 JEP 389 提出,并于 2020 年底针对 Java 16,也是一个孵化 API。
第一个预览版本在Java 19提出,所以将会在Java 19再详细介绍。
2.5 JEP 414:向量API(第二轮孵化)
ps:参考1.1小节。
向量API 由 JEP 338 提出,并作为孵化 API 集成到 Java 16 中。
在Java 17进行了第二轮孵化。
Java 17的相关改进:
对 API 的增强,以支持字符操作,例如 UTF-8 字符解码。具体来说,添加了用于在短向量和字符数组之间复制字符的方法,以及用于与整数向量进行无符号比较的新向量比较操作符。
对 API 的增强,以便在字节向量和布尔数组之间进行转换。
对 x64 上的超越函数和三角函数逐元素操作的内部支持,使用 Intel 的短向量数学库(SVML)。
End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。