JDK8新特性
JDK9新特性
JDK10新特性
JDK11新特性
JDK12新特性
JDK13新特性
JDK14新特性
JDK15新特性
JDK16新特性
JDK17新特性
JDK18新特性
JDK19新特性
JDK20新特性
JDK21新特性
密封类用于限制父类的使用,这是JDK15的预览新特性。密封类的目标包括:允许类或接口的开发者控制哪些代码负责实现,提供了比限制使用父类的访问修饰符声明方式更多的选择,并通过支持对模式的详尽分析而支持模式匹配的未来发展。
在java中,类层次结构通过继承实现代码的重用,父类的方法可以被很多子类继承。但是类层次结构的目的不总是重用代码,有时是为了对域中存在的各种可能性进行建模,比如图形库支持的形状类型等,当以这种方式使用类层次结构时,我们可能需要限制子类集从而简化建模。
引入了sealed关键字,来修饰某个类或接口,该类必须被继承,其子类可以是密封、最终类或非密封类(即普通类),也可以通过permits关键字列出允许继承它的子类:
sealed class A {} // 密封类,必须被继承
final class A_1 extends A { } // 密封类的子类可以是final、sealed或non-sealed(即普通类)
non-sealed class A_2 extends A { }
sealed class A_3 extends A permits A_3_1, A_3_2 { } // 密封类可以指定只能哪些子类可以继承它
non-sealed class A_3_1 extends A_3 { }
final class A_3_2 extends A_3 { }
// non-sealed class A_3_3 extends A_3 {} // 没有指定该类继承A_3
JEP31:隐藏类通过启用标准API来定义无法发现且生命周期有限的隐藏类,从而提高JVM效率。JDK内外部框架将能够动态生成类,而这些类就可以被定义为隐藏类。通常来说,基于JVM的很多语言都有动态生成类的机制,从而提高语言的灵活性和效率。
隐藏类天生为框架设计,在运行时生成内部class;隐藏类只能通过反射访问,不能直接被其他类的字节码访问;隐藏类可以独立于其他类加载、卸载,从而减少框架的内存占用。
到底什么是隐藏类呢?隐藏类就是不能直接被其他类的二进制代码使用的类,隐藏类主要被一些框架用来生成运行时类,这些类需要通过反射机制调用。比如JDK8中引入的lambda表达式,JVM不会在编译时将lambda表达式编译成专门的类,而是在运行时将相应的字节码生成相应的类对象。另外使用动态代理也可以为某些类生成新的动态类。
这些动态生成的隐藏类有什么特性呢?
1)、不可发现性:因为我们是用为某些静态的类动态生成的动态类,所以我们希望把这个动态生成的类看作是静态类的一部分,不希望除了该静态类之外的代码访问该动态类;
2)、访问控制:我们希望在访问控制静态类时,也能控制到动态生成的类;
3)、生命周期:动态生成的类生命周期一般比较短,所以我们不需要将其和静态类的生命周期进行绑定。
java中,有一些API可以定义无法被发现且生命周期有限的隐藏类,从而提高JVM语言的实现效率。这些API有:
1)、java.lang.reflect.Proxy可以定义隐藏类作为实现代理接口的代理类;
2)、java.lang.invoke.StringConcatFactory可以生成隐藏类来保存常量连接方法;
3)、java.lang.invoke.LambdaMetaFactory可以生成隐藏的nestmate类,以容纳访问封闭变量的lambda主体。
普通类是通过ClassLoader::defineClass()创建的,隐藏类则是通过Lookup::defineHiddenClass()创建的。这使JVM从提供的自己而终派生一个隐藏类,链接该隐藏类,并返回提供对隐藏类的反射访问的查找对象,调用程序可以通过返回的查找对象来获取隐藏类的Class对象。
该功能来自JDK14,JDK15将此功能进行了二次预览。
我们可以在判断instanceof时,进行临时变量的声明,从而是代码更简洁
public class TestInstanceof {
private String str;
@Override
public boolean equals(Object o) {
return o instanceof TestInstanceof t && t.str.equals(str); // 更简洁的equals(),避免显式强转
}
public void testString(Object o) {
if (o instanceof String str) { // instanceof出来的临时变量仅能作用于if分支,而非else分支
System.out.println(str); // 临时变量str
} else {
System.out.println(str); // 实例字段str
}
}
}
ZGC是JDK11特性引入的新的垃圾回收器,经过多个实验阶段,在JDK15时,完成了转正。自2018年以来,ZGC进行了多次改进,比如并发类卸载、取消使用未使用的内存、对类数据共享的支持、改进的NUMA感知。此外,最大堆大小从4TB增加到了16TB,支持的平台有Linux、Windows和MacOS。
ZGC是一个重新设计的并发垃圾回收器,通过减少STW时间来提高性能。不过默认的GC还是G1,但是现在只需要通过-XX:+UseZGC就可以启用ZGC了(之前要还需要通过-XX:+UnlockExperimentalVMOptions参数配合)。相信不久,凭借ZGC优异的性能,它必然成为默认的垃圾回收器。
相关参数有:ZAllocationSpikeTolerance、ZCollectionInterval、ZFragmentationLimit、ZMarkStackSpaceLimit、ZProactive、ZUncommit、ZUncommitDelay ZGC-specific JFR events(ZAllocationStall、ZPageAllocation、ZPageCacheFlush、ZRelocationSet、ZRelocationSetGroup、ZUncommit)也从experimental变为product。
文本块是在JDK13中首次预览的,而后在JDK14中进行二次预览,最终在JDK15中转正。文本块是一种多行字符串的文字,避免了大量的转义字符出现,以一种可预测的方式自动设置字符串格式,并在需要时使开发人员控制格式,简化java程序的编写任务。
文本块的目标是提高java程序中的字符串可读性,这些字符串表示以非java语言编写的代码。另一个目标是支持从字符串文本迁移,规定任何新构造都可以表达与字符串文本相同的字符串集,解释相同的转移序列,并以与字符串文本相同的方式进行操作。OpenJDK开发者希望添加转移序列来管理显式空白和换行。
public class TestTextBlock {
public static void main(String[] args) {
String s = """
select a, b, c from table test;""";
System.out.println(s);
// 新增两个符号:\表示取消换行,\s表示一个空格
s = """
select a, b, c \
from table test;""";
System.out.println(s);
s = """
select a, b, c\s\
from table test;""";
System.out.println(s);
}
}
输出如下:
select a, b, c from table test;
select a, b, c from table test;
select a, b, c from table test;
介绍
Records类在JDK14中首次预览,在JDK15中是二次预览,用于方便地创建一个常量类。当用record声明一个类是,该类会自动拥有以下功能:
1)、获取成员变量的简单方法,如name()、age(),而不是getter方法;
2)、自动实现equals()、hashCode()和toString(),但也可以覆写;
3)、所有实例字段均为final,不能单独声明实例字段,也不能对实例字段重新赋值;
4)、该类继承自Record类,同时为final类,但是可以实现接口;
5)、可以有静态字段、实例方法和静态方法。
使用
record Person(int age, String name, Person friend) implements Runnable {
// record类是final类,继承自Record类,因此不能有子类,不能是抽象的,也不能继承其他父类
// 但是可以实现接口
// record类自动帮我们实现全参构造方法、hashcode()方法、equals()方法和toString()方法
// int a; // record类不能有实例字段
public static int sCount = 0;
// record类可以有静态字段、实例方法、静态方法,覆写实例字段的get()方法
// 但是不能对实例字段重新赋值,因为它们是final字段
public Person() {
// record类的构造方法必须调用包含全部参数的构造方法
this(0, "", null);
}
public static void test() {}
public void showName() {
System.out.println(name);
}
@Override
public void run() {
}
}
public class testRecord {
public static void main(String[] args) {
Person p = new Person(24, "szc", null);
Person p1 = new Person(23, "Jason", p);
System.out.println(p1.toString());
}
}
输出如下:
Person[age=23, name=Jason, friend=Person[age=24, name=szc, friend=null]]
这是一个新功能,新加入了Edwards-Curve Digital Signature Algorithm(爱德华兹曲线数字签名算法)算法,该算法是一种现代的椭圆曲线方案,在许多其他加密库(如OpenSSL和BoringSSL)和区块链领域中得到了支持。
与JDK里现有的签名方案相比,EdDSA算法具有更高的安全性和性能,也具有JDK中现有签名方案的优点,但只在SunEC提供商中实现。
该提案(JEP 373)是JEP 353的后续,重新实现了遗留的套接字API。java.net.datagram.Socket和java.net.MulticastSocket的当前实现可以追溯到JDK1.0,那时IPv6还在开发中,因此当前的多播套接字实现尝试对IPv4和IPv6的调和。
具体修改如下:
1)、通过java.net.datagram的基础实现,重新实现旧版DatagramSocket API;
2)、更改java.net.DatagramSocket和java.net.MulticastSocket为更简单和现代化的底层实现,提高了JDK的可维护性和稳定性。
新的实现易于调试和维护,并且协同了Project Loom中正在探索的虚拟线程机制。
JDK15中默认情况下会禁用偏向锁协定,并弃用所有相关命令行选项。这么做的目的是确定是否需要继续支持偏置锁定的高维护成本的遗留同步优化,HotSpot虚拟机使用该优化来减少非竞争锁的开销。尽管某些java应用程序在金庸偏向锁后可能会出现性能下降,但偏向锁的性能提高不像以前那么明显。
偏向锁的命令行参数为-XX:+UseBiasedLocking,被废弃的所有相关命令有:BiasedLockingStartupDelay, BiasedLockingBulkRebiasThreshold, BiasedLockingBulkRevokeThreshold, BiasedLockingDecayTime, UseOptoBiasInlining, PrintBiasedLockingStatistics和PrintPreciseBiasedLockingStatistics
Shenandoah回收器是从JDK12引入的回收器,通过与正在运行的java线程同时进行疏散工作来减少STW时间。Shenandoah的STW时间与堆大小无关,即不管java堆是200MB还是200GB,都将具有一样的暂停时间。
Shenandoah与ZGC的异同:
1)、性能目标一致,但还是ZGC更优;
2)、Shenandoah只存在于OpenJDK中,而ZGC是根正苗红的Oracle JDK出品。
现在,在OpenJDK中,只需要-XX:+UseShenandoahGC即可启用该回收器,不用-XX:+UnlockExperimentalVMOptions配合
该API的目的是允许Java程序安全有效地访问java堆之外的外部存储器,如本机内存、持久层、托管堆等。
有许多Java程序是访问外部内存的,比如Ignite和MapDB。新API将有助于避免与垃圾回收相关的成本、与跨进程共享内存和将文件映射到内存进行序列化反序列化内存内容相关的不可预测性。但是,该API目前没有为访问外部内存提供令人满意的解决方案,但是在新提案中,API应该不会破坏JVM的安全性。
外部存储器访问API在JDK14中被作为incubating(孵化)API引入,在JDK15中处于二次孵化状态,并提供了改进。
删除了对Solaris/SPARC、Solaris/x64和Linux/SPARC端口的源码与构建支持,相关内容在JDK14中被标记为废弃,在JDK15中正式移除。
许多正在开发的项目和功能(如Valhalla、Loom、Panama等)需要进行重大更改已使用CPU架构和操作系统的特定代码。近年来,Solaris和SPARC都已经被Linux操作系统和英特尔处理器取代,放弃对Solaris和SPARC的支持将使OpenJDK的开发者加速开发新功能,推动平台向前发展。
Nashorn是在JDK中提出的js脚本执行引擎,该功能是JDK8的新特性,但在JDK11中就被标记为废弃,JDK15中彻底移除。
JDK11中取而代之的是GraalVM,这是一个运行时平台,支持Java和其他基于Java字节码的语言,以及其他语言,如JS、Ruby、Python或LLVM等,且性能是Nashorn的两倍以上。
JDK15移除了Nashorn JS引擎和jjs工具,具体就是jdk.scripting.nashorn和jdk.scripting.nashorn.shell这两个模块被移除了。
GraalVM是在HotSpot虚拟机上进行增强而成的跨语言全栈虚拟机,支持的语言包括Java、Scala、Groovy、Kotlin、C、C++、JavaScript、Ruby、Python、R等。
RMI激活被标记为Deprecated,在未来的版本中会被移除。RMI激活机制是RMI中一个过时的部分,自JDK8以来一直是可选的非必选项。RMI激活机制增加了持续的维护负担,不过RMI其他部分暂时不会被启用。
在RMI系统中,我们通过延迟激活将激活对象推迟到客户第一次使用(即第一次方法调用)之前。不过,对于现代应用程序而言,分布式系统大多是基于Web的,而Web服务器已经解决了防火墙穿越、请求过滤、身份验证和安全性的问题,并且也提供了很多延迟加载技术。因此,在现代应用程序中,RMI激活已经很少被用到了,而且在各种开源的代码库中也基本找不到RMI激活机制的使用代码了。
为了减少RMI激活机制的维护成本,JDK8将RMI激活置为可选,到了JDK15,该机制被废弃。
1)、支持Unicode13.0
2)、给CharSequence类新增了isEmpty()方法:
/**
* Returns {@code true} if this character sequence is empty.
*
* @implSpec
* The default implementation returns the result of calling {@code length() == 0}.
*
* @return {@code true} if {@link #length()} is {@code 0}, otherwise
* {@code false}
*
* @since 15
*/
default boolean isEmpty() {
return this.length() == 0;
}
3)、为TreeMap提供了putIfAbsent()、computeIfAbsent()、computeIfPresent()、compute()、merge()方法的覆写实现;
4)、jcmd的GC.heap_dump命令支持gz选项,以dump出gzip压缩版的heap,压缩等级默认为1,可选范围为[1, 9],1压缩速率最快,9压缩速率最慢,但压缩比最高;
5)、jdk.net.ExtendedSocketOptions新增了SO_INCOMING_NAPI_ID支持;
6)、新增了jdk.tls.client.SignatureSchemes和jdk.tls.server.SignatureSchemes用于配制TLS签名;
7)、支持certificate_authorities扩展
移除项:-XX:+UseAdaptiveGCBoundary
废弃项:-XX:ForceNUMA;Native SunEC实现从JDK15开始默认禁用。
已知问题:HttpClient现在没有覆盖SSLContext默认参数中指定的协议。
其他事项:
1)、当DatagramPacket没有设置port时,该getPort()方法返回值为0;
2)、优化了G1的堆分块大小的计算,因为默认的G1回收器是通过堆分块(Region)进行垃圾回收的。