JDK 15已经在2020年9月15日发布!详情见 JDK 15 官方计划。下面是对 JDK 15 全部新特性的详细解析!html
JEP 339: Edwards-Curve Digital Signature Algorithm (EdDSA)java
与其余签名方案相比,EdDSA 具备更高的安全性和性能,而且已有不少其余加密库(如 OpenSSL 和 BoringSSL)支持此签名方案。EdDSA 是 TLS 1.3的可选组件,且是 TLS 1.3 中仅有的三种签名方案之一。用户能够没必要再使用第三方库了。git
JEP 360: Sealed Classes (Preview)github
在 Java 语言中,代码的重用是经过类的继承实现的:多个子类能够继承同一个超类(并重用超类的方法)。可是重用代码并非类层次结构的惟一目的,有时类层次结构仅仅是对某个领域的建模。以这种方式使用类层次结构时,限制子类集合能够简化建模。算法
好比在图形库中,Shape 类的开发者可能只但愿有限个数的类扩展 Shape 类,开发者并不想为未知子类编写防护代码。之前的 Java 并不会限制 Shape 类的扩展属性,Shape 类能够拥有任意数量的子类。在封闭类(Sealed Classes)中,类层次结构是封闭的,可是代码仍然能够在有限范围内重用。shell
经过 sealed
修饰符将一个类声明为密封类,permits子句指定容许扩展密封类的类。例以下面的 Shape 类声明指定了三个可扩展的子类。编程
package com.example.geometry; public abstract sealed class Shape permits Circle, Rectangle, Square {...}
在子类数量不多时,在密封类的源文件中声明子类会很方便。当以这种方式声明子类时,密封类能够省略 allows 子句,同时 Java 编译器将从源文件中推断容许的子类。例以下面的例子:安全
package com.example.geometry; abstract sealed class Shape {...} ... class Circle extends Shape {...} ... class Rectangle extends Shape {...} ... class Square extends Shape {...}
密封类可让代码更简洁,代码能够明确推断出全部容许的子类。好比传统的 if-else 和 instanceof 代码编译器分析起来很困难,没法肯定子句是否覆盖了全部容许的子类。下面的方法会致使编译期错误:数据结构
int getCenter(Shape shape) { if (shape instanceof Circle) { return ... ((Circle)shape).center() ... } else if (shape instanceof Rectangle) { return ... ((Rectangle)shape).length() ... } else if (shape instanceof Square) { return ... ((Square)shape).side() ... } }
在以后支持模式匹配的版本中,编译器能够直接推断出 Shape 全部容许的子类,不须要 default 语句。此外,若是缺乏子类的任意一个,编译器就会报错。多线程
int getCenter(Shape shape) { return switch (shape) { case Circle c -> ... c.center() ... case Rectangle r -> ... r.length() ... case Square s -> ... s.side() ... }; }
package java.lang.constant; public sealed interface ConstantDesc permits String, Integer, Float, Long, Double, ClassDesc, MethodTypeDesc, DynamicConstantDesc {...} // ClassDesc is designed for subclassing by JDK classes only public sealed interface ClassDesc extends ConstantDesc permits PrimitiveClassDescImpl, ReferenceClassDescImpl {...} final class PrimitiveClassDescImpl implements ClassDesc {...} final class ReferenceClassDescImpl implements ClassDesc {...} // MethodTypeDesc is designed for subclassing by JDK classes only public sealed interface MethodTypeDesc extends ConstantDesc permits MethodTypeDescImpl {...} final class MethodTypeDescImpl implements MethodTypeDesc {...} // DynamicConstantDesc is designed for subclassing by user code public non-sealed abstract class DynamicConstantDesc implements ConstantDesc {...}
JVM 在运行时识别密封类和接口,并防止未经受权的子类和子接口扩展密封类。
尽管 sealed 关键字是类修饰符,可是 ClassFile 中并无 ACC_SEALED 标志。相反,密封类的类文件具备 PermittedSubclasses属性,该属性隐式指示密封修饰符,并显式指定容许的子类:
PermittedSubclasses_attribute { u2 attribute_name_index; u4 attribute_length; u2 number_of_classes; u2 classes[number_of_classes]; }
java.lang.Class
将增长以下 public 方法:
隐藏类不能被其余类的字节码直接使用,适合在运行时生成类、并经过反射间接使用隐藏类的框架。
许多机遇 JVM 构建的语言都经过动态生成类,提升灵活性和效率。好比在 Java 语言中,javac 不会在编译时将 lambda 表达式转换成特殊的类文件,而是保存在字节码中,该字节码能够把 lambda 表达式动态生成为相应的对象。一样地,非 Java 语言常常经过运行时生成动态代理,来实现语言的高级特性。
语言的实现者一般但愿将动态生成的类,在逻辑上成为静态生成类的实现的一部分:
动态生成类仅是静态生成类的实现细节
这一目标。不幸的是,类定义的标准 API(ClassLoader::defineClass 和 Lookup::defineClass)并不在乎该类的字节码是动态生成(在运行时)仍是静态生成(在编译时)的。若是标准 API 能够定义没法被发现,且具备有限生命周期的隐藏类,则动态生成类的 JDK 内部和外部框架均可以定义隐藏类,这样作能够提升 JVM 语言实现的效率。好比:
JEP 372: Remove the Nashorn JavaScript Engine
Java 11 中已经将该引擎标记为废弃,并明确表示要在未来的版本中删除它。Nashorn JavaScript 引擎最开始是 JDK 8 经过 JEP 174 继承的,用来代替 Rhino 脚本引擎,当时 Nashorn JavaScript 引擎是 ECMAScript-262 5.1 标准的完整实现。可是随着 ECMAScript 语言构造以及 API 的修改,咱们发现 Nashorn 难以维护。
JDK 的两个模块会永久删除:
JEP 373: Reimplement the Legacy DatagramSocket API
Java.net.DatagramSocket 和 java.net.MulticastSocket API 重构了基础实现,使得其更易于维护和调试。新的实现能更好地适应虚拟线程的工做。
JEP 374: Disable and Deprecate Biased Locking
默认状况下禁用偏向锁,并废弃全部相关的命令行。
偏向锁是 HotSpot 虚拟机使用的一项优化技术,可以减小无竞争锁定时的开销。偏向锁的目的是假定 monitor 一直由某个特定线程持有,直到另外一个线程尝试获取它,这样就能够避免获取 monitor 时执行 cas 的原子操做。monitor 首次锁定时偏向该线程,这样就能够避免同一对象的后续同步操做步骤须要原子指令。从历史上看,偏向锁使得 JVM 的性能获得了显著改善。
可是过去看到的性能提高,在如今看来已经不那么明显了。受益于偏向锁的应用程序,每每是使用了早期 Java 集合 API的程序(JDK 1.1),这些 API(Hasttable 和 Vector) 每次访问时都进行同步。JDK 1.2 引入了针对单线程场景的非同步集合(HashMap 和 ArrayList),JDK 1.5 针对多线程场景推出了性能更高的并发数据结构。这意味着若是代码更新为使用较新的类,因为没必要要同步而受益于偏向锁的应用程序,可能会看到很大的性能提升。此外,围绕线程池队列和工做线程构建的应用程序,性能一般在禁用偏向锁的状况下变得更好。
偏向锁为同步系统引入了许多复杂的代码,而且对 HotSpot 的其余组件产生了影响。这种复杂性已经成为理解代码的障碍,也阻碍了对同步系统进行重构。所以,咱们但愿禁用、废弃并最终删除偏向锁。
JEP 375: Pattern Matching for instanceof (Second Preview)
经过对 instanceof 运算符进行模式匹配,来增长 Java 语言。模式匹配可使应用程序更简洁、安全地提取特定对象。
不少程序都会判断一个表达式是否具备某种类型或结构,而后有条件地进一步处理,好比下面的 instanceof-and-cast 用法:
if (obj instanceof String) { String s = (String) obj; // use s }
上述代码作了 3 件事:
这种模式很简单,可是这样的写法并非最优的。第 2 步的类型转换是重复的,同时重复可能会带来错误。模式匹配容许简明地表达对象的所需“形状”(模式),并容许各类语句和表达式针对其输入来测试“形状”(匹配)。从 Haskell 到 C# 等不少语言都接受了模式匹配。
在下面的代码中,短语 String s 是类型测试模式:
if (obj instanceof String s) { // can use s here } else { // can't use s here }
下面的代码也是正确的:
if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}
JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)
ZGC 已经在 JEP 333时集成到了 JDK 11 中,当时是做为实验特性引入的。在 JDK 11 发布以来,咱们收到了不少积极的反馈,并修复了许多 bug,添加了不少新功能。更重要的是,ZGC 已经支持全部主流平台:
如今能够经过 -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
命令选项启用 ZGC。
Java语言增长文本块功能。文本块是多行字符串文字,能避免大多数状况下的转义问题。
在Java中,HTML, XML, SQL, JSON等字符串对象都很难阅读和维护。
使用one-dimensional
的字符串语法:
String html = "<html>\n" + " <body>\n" + " <p>Hello, world</p>\n" + " </body>\n" + "</html>\n";
使用two-dimensional
文本块语法:
String html = """ <html> <body> <p>Hello, world</p> </body> </html> """;
使用one-dimensional
的字符串语法:
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" + "WHERE `CITY` = 'INDIANAPOLIS'\n" + "ORDER BY `EMP_ID`, `LAST_NAME`;\n";
使用two-dimensional
文本块语法:
String query = """ SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB` WHERE `CITY` = 'INDIANAPOLIS' ORDER BY `EMP_ID`, `LAST_NAME`; """;
使用one-dimensional
的字符串语法:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); Object obj = engine.eval("function hello() {\n" + " print('\"Hello, world\"');\n" + "}\n" + "\n" + "hello();\n");
使用two-dimensional
文本块语法:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); Object obj = engine.eval(""" function hello() { print('"Hello, world"'); } hello(); """);
文本块是Java语言的新语法,能够用来表示任何字符串,具备更高的表达能力和更少的复杂度。
文本块的开头定界符是由三个双引号字符(""")组成的序列,后面跟0个或多个空格,最后跟一个行终止符。内容从开头定界符的行终止符以后的第一个字符开始。
结束定界符是三个双引号字符的序列。内容在结束定界符的第一个双引号以前的最后一个字符处结束。
与字符串文字中的字符不一样,文本块的内容中能够直接包含双引号字符。容许在文本块中使用\“,但不是必需的或不建议使用。
与字符串文字中的字符不一样,内容能够直接包含行终止符。容许在文本块中使用\n,但不是必需或不建议使用。例如,文本块:
""" line 1 line 2 line 3 """
等效于字符串文字:
"line 1\nline 2\nline 3\n"
或字符串文字的串联:
"line 1\n" + "line 2\n" + "line 3\n"
JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production)
Shenandoah GC 由JEP 189 集成到 JDK 12 中。如今,Shenandoah GC 已经能够用于生产环境了!(Shenandoah GC 的具体特性之后会有专门的文章讲解,本篇文章略过)。
JEP 381: Remove the Solaris and SPARC Ports
删除了对 Solaris/SPARC、Solaris/x64和 Linux/SPARC 端口支持的源代码,并从新构建 JDK。这些代码在 JDK 14中已经被标记为废弃的,并明确表示在将来版本中会删除。
JEP 383: Foreign-Memory Access API (Second Incubator)
引入新的能使 Java 程序安全高效访问 Java 堆内存以外的外部内存的 API。
JEP 384: Records (Second Preview)
经过 Records
(不知道如何翻译,囧……)加强Java编程语言。Records提供了一种紧凑的语法来声明类,这些类是浅层不可变数据的透明持有者。
咱们常常听到这样的抱怨:“Java太冗长”、“Java规则过多”。首当其冲的就是充当简单集合的“数据载体”的类。为了写一个数据类,开发人员必须编写许多低价值、重复且容易出错的代码:构造函数、访问器、equals()、hashCode()和toString()等等。
尽管IDE能够帮助开发人员编写数据载体类的绝大多数编码,可是这些代码仍然冗长。
从表面上看,将Records是为了简化模板编码而生的,可是它还有“远大”的目标:modeling data as data
。records应该更简单、简洁、数据不可变。
records是Java的一种新的类型。同枚举同样,records也是对类的一种限制。records放弃了类一般享有的特性:将API和表示解耦。可是做为回报,records使数据类变得很是简洁。
一个record具备名称和状态描述。状态描述声明了record的组成部分。例如:
record Point(int x, int y) { }
由于records在语义上是数据的简单透明持有者,因此记录会自动获取不少标准成员:
records不能扩展任何类,而且不能声明私有字段之外的实例字段。声明的任何其余字段都必须是静态的。
records类都是隐含的final类,而且不能是抽象类。这些限制使得records的API仅由其状态描述定义,而且之后不能被其余类实现或继承。
也能够显式声明从状态描述自动派生的任何成员。能够在没有正式参数列表的状况下声明构造函数(这种状况下,假定与状态描述相同),而且在正常构造函数主体正常完成时调用隐式初始化(this.x=x)。这样就能够在显式构造函数中仅执行其参数的验证等逻辑,并省略字段的初始化,例如:
record Range(int lo, int hi) { public Range { if (lo > hi) /* referring here to the implicit constructor parameters */ throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); } }
RecordDeclaration: {ClassModifier} record TypeIdentifier [TypeParameters] (RecordComponents) [SuperInterfaces] [RecordBody] RecordComponents: {RecordComponent {, RecordComponent}} RecordComponent: {Annotation} UnannType Identifier RecordBody: { {RecordBodyDeclaration} } RecordBodyDeclaration: ClassBodyDeclaration RecordConstructorDeclaration RecordConstructorDeclaration: {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody
下面的方法会被加到java.lang.Class
中:
JEP 385: Deprecate RMI Activation for Removal
将 RMI Activation
机制标记为废弃,以便在未来的某个版本删除掉。RMI Activation 是 RMI 的过期部分,可是这并不表示会弃用 RMI 的其余部分。
以上就是 JDK 15
的所有新特性,咱们能够看到 G1 GC 已经退出历史舞台,新的 ZGC
、Shenandoah GC
已经登上历史舞台。同时也丢弃了像自旋锁
这种历史包袱,增长了许多诸如文本块
等简洁的语法特性。咱们能够预见 Java 的性能会愈来愈好,同时也会愈来愈简洁。(固然简洁程度跟 Kotlin 这种新兴语言是比不了的,毕竟彻底没有历史包袱)。欢迎你们关注个人公众号。
coding 笔记、点滴记录,之后的文章也会同步到公众号(Coding Insight)中,但愿你们关注^_^
代码和思惟导图在 GitHub 项目中,欢迎你们 star!