一、Java 14
发布于2020年3月17日。Java 14主要新特性如下:
-
JEP 305:Pattern Matching for instanceof (Preview)instanceof 的模式匹配(预览)
-
JEP 358:Helpful NullPointerExceptions 有用的 NullPointerExceptions
-
JEP 359:Records (Preview) Records(预览)
-
JEP 361:Switch Expressions (Standard)Switch 表达式(转正)
-
JEP 363:Remove the Concurrent Mark Sweep (CMS) Garbage Collector删除并发标记清除 (CMS) 垃圾回收器
-
JEP 366:Deprecate the ParallelScavenge + SerialOld GC Combination弃用 ParallelScavenge + SerialOld GC 组合
-
JEP 368:Text Blocks (Second Preview)文本块(第二次预览)
-
JEP 370:Foreign-Memory Access API (Incubator) 外部内存访问 API (第一轮孵化)
更多内容请读者自行阅读:OpenJDK Java 14官方文档
1.1 JEP 305:instanceof 的模式匹配(一次预览)
Java 14也对instanceof的使用进行了改进,看下面例子:
public static void main(String[] args) {Object obj = new Object();if(obj instanceof String){//强转为StringString str = (String) obj;String trim = str.trim();}//Java 14if(obj instanceof String str){//不必强转,直接使用String trim = str.trim();}
}
1.2 JEP 358:改进NEP异常提示信息
public class NEPTest {public static void main(String[] args) {User user=new User();System.out.println(user.getAddress().getCity().trim());}
@Datastatic class User{private String name;private Address address;}@Datastatic class Address{private String city;}
}
程序执行会报NPE异常,异常信息在Java 14进行了改进,我们先开java 14版本之前的报错信息:
Exception in thread "main" java.lang.NullPointerExceptionat com.lky.features.java14.NEPTest.main(NEPTest.java:11)
通过异常信息很容易就能定位到报错行,但是分辨不出是"user.getAddress()"是null还是"getAddress().getCity()"是null,我们再来看下Java 14的报错信息:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.lky.features.java14.NEPTest$Address.getCity()" because the return value of "com.lky.features.java14.NEPTest$User.getAddress()" is nullat com.lky.features.java14.NEPTest.main(NEPTest.java:11)
从报错信息一眼就可以看出是""User.getAddress()" 为null导致的。
1.3 JEP 359:Records(一次预览)
Java 14以预览功能的形式引入了一个新特性:Records,主要目的是提供一种简洁的语法来声明类似数据的小型不可变对象,用于解决长期以来Java中定义纯数据载体类带来的繁琐问题。
ps:Java 16才成为正式特性。
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;}@Overridepublic final String toString() {return ObjectMethods.bootstrap("toString", new MethodHandle[]{Student1.class, "name;age", "name", "age"}, this);}@Overridepublic final int hashCode() {return (int)ObjectMethods.bootstrap("hashCode", new MethodHandle[]{Student1.class, "name;age", "name", "age"}, this);}@Overridepublic 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.4 JEP 361: Swicth表达式(正式特性)
switch 表达式由 JEP 325 于 2017 年 12 月提出。JEP 325 于 2018 年 8 月作为预览功能针对 JDK 12。
JEP 354 提出了一个新的声明 yield,并恢复了 break 的原意。JEP 354 于 2019 年 6 月作为预览功能针对 JDK 13。
对 JDK 13 的反馈表明,switch 表达式已准备好在 JDK 14 中成为最终的永久表达式,无需进一步的更改。
最终版本可以参考以下代码示例:
public static String swicthDay(String x){String type;//Java 12:如果加一个if判断分支,那就不能用->直接返回了switch (x){case "Monday","Tuesday","Wednesday","Thursday","Friday"->type="WorkDay";case "Saturday","Sunday"->type="FreeDay";default -> {if(x.isEmpty()){type="null";}else {type="noDay";}}};
//java 13:可以用yield直接返回type = switch (x){case "Monday","Tuesday","Wednesday","Thursday","Friday"->"WorkDay";case "Saturday","Sunday"->"FreeDay";default -> {if(x.isEmpty()){yield "null";}else {yield "noDay";}}};return type;}
1.5 JEP 363:删除并发标记清除 (CMS) 垃圾回收器
也是由于高性能和高效率的垃圾回收器(G1、ZGC和Shenandoah等)层出不穷,CMS的技术也相对过时,Java 14正式删除了CMS垃圾回收器及其相关代码。
1.6 JEP 366:弃用ParallelScavenge+SerialOld GC组合
随着JVM发展,高性能和高效率的垃圾回收器(G1、ZGC和Shenandoah等)层出不穷,而对于ParallelScavenge+SerialOld这个组合已经不具备优势了,用的也越来越少,所以Java 14弃用了这个组合。
1.7 JEP 368:文本块(二次预览)
Java 13引入了文本块("""),Java 14对其进一步增强,新增了两个转义符:""和"\s":
" \ ":表示行尾,不引入换行符。
"\s":表示单个空格。
public static void main(String[] args) {String str1= """Hello,\World""";System.out.println(str1);//Hello,WorldString str2= """Hello\sWorld""";System.out.println(str2);//Hello World
}
1.8 JEP 370:外部内存访问API(第一轮孵化)
Java 14将外部内存访问 API作为孵化项目引入 ,以允许 Java 程序安全高效地访问 Java 堆外内存。
为什么要引入?
现在许多Java库或程序都会访问外部内存,比如Ignite、mapDB、RocketMQ、memcached和Netty的ByteBuf API等,通过访问外部内存,他们可以避免与GC打交道、在多个进程之间共享内存以及可以通过将文件映射到内存(通过mmap等)来序列化内存内容。
但是,现在Java提供的外部内存API并不好用。
Java 14之前,开发人员如果想要操作堆外内存,通常的做法就是使用ByteBuffer、Unsafe或JNI等方式,但无论哪种方式都无法有效解决安全性和高效性等2个问题,并且堆外内存的释放也是一个头疼的问题。
ps:这个在Java 17会与另一个孵化项目合并,并在Java 19进行第一次预览,所以更多内容请参考Java 19。
二、Java 15
Java 15于2020年9月15日发布,主要特性如下:
-
JEP 360:Sealed Classes (Preview) 封闭类(预览)
-
JEP 371:Hidden Classes 隐藏类
-
JEP 374:Disable and Deprecate Biased Locking 禁用和弃用偏向锁
-
JEP 375:Pattern Matching for instanceof (Second Preview)instanceof 的模式匹配(二次预览)
-
JEP 377:ZGC: A Scalable Low-Latency Garbage Collector ZGC:可扩展的低延迟垃圾回收器(转正)
-
JEP 378:Text Blocks 文本块(转正)
-
JEP 379:Shenandoah: A Low-Pause-Time Garbage Collector Shenandoah:低暂停时间垃圾收集器(转正)
-
JEP 383:Foreign-Memory Access API (Second Incubator) 外部内存访问 API(第二个孵化器)
-
JEP 384:Records (Second Preview) Records(第二次预览)
更多内容请读者自行阅读:OpenJDK Java 15官方文档
2.1 JEP 360:封闭类(一次预览)
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 {
}
2.2 JEP 371:隐藏类
这是一种不能被其他类直接使用的类,Java 15引入隐藏类主要针对的时库和框架的开发者,所以大多数程序员就都用不到了;
简单的说,隐藏类不是通过常规的Java代码创建的,而是通过特定的API调用在运行时动态生成的。
2.3 JEP 374:禁用偏向锁定
在之前文章Java并发编程第4讲——Java中的锁(万字详解)介绍synchronized锁升级过程的时候提到了过:
偏向锁:指在只有一个线程访问对象的情况下,也就是没有竞争,可以直接执行代码,并在对象头中记录该线程的ID作为偏向锁的持有者。
标记目的是JVM会假定这个线程会再次获得该锁,因此在下次获取锁的时候就可以省略一些与锁相关的检查和更新操作,从而提高性能。
但是,偏向锁增加了JVM的复杂性,并且在多线程竞争激烈的情况下,他可能不会带来预期的性能提升,所以在Java 15中,默认禁用了偏向锁,但我们可以通过-XX:UesBiaseLocking来启用它。
2.4 instanceof的模式匹配(二次预览)
模式匹配是由 JEP 305 在 2017 年年中提出的,并在 2019 年末作为预览语言功能针对 JDK 14。
此 JEP 建议在 JDK 15 中重新预览该功能,相对于 JDK 14 中的预览没有变化。
示例还可参考1.1小节。
2.5 JEP 377:ZGC垃圾回收器(转正)
ZGC 通过 JEP 333 集成到 JDK 11 中,当时还是处于实验阶段。经过多个版本的迭代,ZGC在Java 15终于可以正式使用了,不过默认的垃圾回收器还是G1,可以通过下列启动参数使用:
java -XX:+useZGC className
2.6 JEP 378:文本块(转正)
JEP 355 于 2019 年 6 月作为预览功能针对 JDK 13。
对 JDK 13 的反馈建议,在 JDK 14 中应该再次预览文本块,并添加两个新的转义序列。
对 JDK 14 的反馈表明,文本块在 JDK 15 中已经成为正式特性,没有进一步的更改。
2.7 Shenandoah垃圾回收器"转正"
Shenandoah垃圾回收器由 JEP 189 集成到 JDK 12 中(实验特性),经过几个版本迭代,在Java 15成为正式特性,不过默认的垃圾回收器还是G1,可以用java -XX:+UseShenandoahGC className启用。
以下类容来自Java 12:
Java 12引入Shenandoah垃圾回收器,一个专门用来处理大堆的低延迟垃圾回收器,性能几乎和ZGC相同(目标是99.9%的暂停时间小于10ms),不同的是ZGC是openJDK开发的,Shenandoah则是由RedHat开发的。
Shenandoah使用了一种用于并发压缩堆时维护对象引用的技术(布鲁克斯指针)。这种技术允许收集器在压缩堆时并行地移动对象,而不必暂停应用程序,这就允许Shenandoah能够在应用线程运行的同时进行垃圾回收,从而减少停顿,还有最重要的一点是暂停时间于堆大小无关,无论是200M或者200G的堆,暂停时间都一样。
2.8 JEP 383:外部内存访问 API(第二个孵化器)
参见1.8小节。
2.9 JEP 384:Records(二次预览)
Records由 JEP 359 于 2019 年年中提出,并于 2020 年初在 JDK 14 中作为预览功能提供。
此 JEP 建议在 JDK 15 中重新预览该功能,以合并基于反馈的改进,并支持 Java 语言中其他形式的本地类和接口。
Java 15仅作了细微调整,详情请参考1.3小节。