Java新特性梳理——Java15


highlight: xcode

theme: vuepress

概述

2020 年 9 月 15 日,Java 15 正式发布,(风平浪静的一个版本)共有 14 个 JEP,是时间驱动形式发布的第六个版本。相关文档:https://openjdk.java.net/projects/jdk/15/

语法层面变化

密封类

通过密封的类和接口来增强 Java 编程语言,这是新的预览特性,用于限制超类的使用密封的类和接口限制其他可继承或者实现他们的其他类或接口。

目标:

允许类或接口的开发者来控制那些代码负责实现,提供了比限制使用超类的访问修饰符声明方式更多选择,并通过支持对模式的详尽分析而支持模式匹配的未来发展。

在 Java 中,类层次构造通过集成实现代码的重用,父类的方法可以被许多子类继承。但是,类层次接口的目的并不总是重用代码,有时是对域中存在的各种可能性进行建模。例如图形库支持函的形状类型。当以这种方式使用类层次结构时,我们可能需要限制子类集从而简化建模。

虽然我们可以通过 final 来限定子类继承,但是这是绝对杜绝类子类。而类的密封是允许子类,限定是哪个或者哪些。

引入 Seald class 或 interface,这些 class 或者 interface 只允许被指定的类或者 interface 进行扩展和实现。

使用修饰符 sealed,我们可以将一个类声明为密封类。密封类使用 reserved 关键字 permits 列出可以直接扩展他的类。子类可以是最终的,非密封或者密封的。

示例代码:

```java public class TestSealedClass {

}

/** * sealed 对 Person 类进行密封 * permits 指明哪些类可以继承 * 子类必须是 final 修饰的或者也是密封的 * 如果子类不想被密封,可以使用 non-sealed 修饰 */ sealed class Person permits Worker, Teacher, Cook, Boss, Employee, Student {} final class Cook extends Person {} final class Boss extends Person {} final class Employee extends Person {} final class Teacher extends Person {}

// 密封的子类允许继续有子类 sealed class Student extends Person permits PrimaryStudent, GraduateStudent {} final class PrimaryStudent extends Student {} final class GraduateStudent extends Student {}

// 通过 non-sealed 取消子类密封 non-sealed class Worker extends Person {} class CarWorker extends Worker {} ```

密封接口:指定实现类的接口。

```java public class TestSealedInterface {

}

/** * 只有接口可以继承接口 * 一个接口可以同时继承多个接口 * final 不能修饰接口,密封接口在被继承时,子接口要么使用 sealed non-sealed 修饰 */ sealed interface Myinter1 permits Myinter3 {} sealed interface Myinter2 permits Myinter3 {} sealed interface Myinter3 extends Myinter1, Myinter2 {} non-sealed class MyImpl implements Myinter3 {}

sealed interface I permits A, B, C {} final class A implements I {} sealed class B implements I {} non-sealed class C implements I {}

final class D extends B {} ```

密封接口与模式匹配

```java public class TestSealedClass { public static void test(C c) { if (c instanceof I) { System.out.println( "it is an i"); } else { System.out.println("it is not i"); } }

public static void main(String[] args) {
    test(new C());
}

}

interface I {

}

// 密封类仅仅是控制类的继承和实现关系,不会影响我们的模式匹配 sealed class C implements I permits D, E {} non-sealed class D extends C {} final class E extends C {}

```

密封接口和record

record 类型是隐匿式的 final 类,可以直接实现密封接口。

```java public class TestRecords { public static void main(String[] args) { MyInter1 myInter1 = new Person(10, "曹操"); } }

sealed interface MyInter1 { public void eat(); }

/** * record 默认继承的 java.lang.Record * record可以直接实现密封接口,不需要用sealed 修饰 non-sealed 修饰 * record本身是隐式的final修饰 */ record Person(Integer pid, String pname) implements MyInter1 { @Override public void eat() {

}

}

record Student(Integer pid, String pname) implements MyInter1 { @Override public void eat() {

}

}

record Cook(Integer pid, String pname) implements MyInter1 { @Override public void eat() {

}

}

record Worker(Integer pid, String pname) implements MyInter1 { @Override public void eat() {

}

} ```

隐藏类

该提案通过启用标准 API 来定义无法发现且有有限生命周期的隐藏类,从而提高 JVM 上所有语言的效率。JDK 内部和外部的框架将能够动态生成类,而这些类可以定义隐藏类。通常来说基于 JVM 的很多语言都有动态生成类的机制,这样可以提高语言的灵活性和效率。

  • 隐藏类天生为框架设计的,在运行时生成内部的 class
  • 隐藏类只能通过反射访问,不能直接被其他类的字节码访问。
  • 隐藏类可以独立于其他类加载和卸载,这样可以减少框架的内存占用。

什么是 hidden class?

就是不能直接被其他 class 的二进制代码使用的 class。主要被一些框架用来生成运行时类,但是这些类不能被用来直接使用的,是通过反射来调用的。

比如 Java 8 中引入的 Lambda 表达式,编译时不会将 Lambda 表达式转换为专门的类,而是在运行时将相应的字节码动态生成相应的类对象。另外使用动态代理也可以为某些类生成新的动态类。

我们希望这样的动态类有哪些特征呢?

  • 不可发现性:因为我们是为某些静态的类动态生成的动态类,所以我们希望这个动态生成的类看作是静态类的一部分,所以我们不希望除了该静态类以外的其他机制发现。
  • 访问控制:我们希望在访问控制静态类的同时,也能控制到动态生成的类。
  • 生命周期:动态生成类的声明周期一般都比较短,我们不需要将其保存和静态类的生命周期一致。

因此,我们需要一些 API 来定义无法发现的且具有有限生命周期的隐藏类,这将有助于提高基于 JVM 的语言实现效率。比如:

  • java.lang.reflect.Proxy:可以定义隐藏类作为实现代理接口的代理类。
  • java.lang.invoke.StringConcatFactory:可以生成隐藏类来保存常量连接方法。
  • java.lang.invoke.LambdaMetaFactory:可以生成隐藏的 nestmate 类,以容纳访问封闭变量的 Lambda 主体。

普通类是通过调用 ClassLoader::defineClass 创建的,而隐藏类是通过调用 Lookup::defineHiddenClass 创建的,这使 JVM 提供的字节派生一个隐藏类,链接该隐藏类,并返回提供对隐藏类的反射访问的查找对象。调用程序可以通过返回的查找对象来获取隐藏类的 Class 对象。

instanceof模式匹配

Java 14 中作为预览语言功能引入 instanceof 模式匹配,在 Java 15 中出于第二次预览,而没有任何更改。

record类型

Java 14 中引入了 record 类型,只用一个 record 可以很方便的创建一个常量类,就是一个实体类,简化专门用于存储数据的类的创建语法。在 Java 15 中,record 类型处于二次预览

text block

Java 13 开始引入文本块,Java 14 进行二次预览,Java 15 中成为一个正式的标准

关于JVM

ZGC

ZGC 是 Java 11 引入的新的垃圾收集器,经历了多个阶段。自从终于成正式特性自 2008 年以来,ZGC 已经增加了许多改进:并发类卸载、取消未使用的内存、对类数据实现共享的支持到 NUMA 感知。此外,最大的堆从 4T 增加到了16T。支持平台包括 Linux、Windows 和 MacOS。ZGC 是一个重新设计的并发垃圾收集器,通过 GC 停顿时间来提高性能,但是这并不是替换默认的 G1 垃圾收集器,只不过之前需要 -XX:+UnlockExperimentalVMOptions -XX:+UseZGC。现在只需要 -XX:+UseZGC 就可以,相信不久的将来它必然会成为默认的垃圾回收器。

Shenandoah GC算法转正

这是一个 Java 12 引入的回收算法,该算法通过正在运行的 Java 线程同时进行疏散工作来减少 GC 暂停时间。Shenandoah 的暂停时间与堆大小无关,无论是 200M 还是 200G,都具有机会一致的暂停时间。

Shenandoah 和 ZGC 对比:

  • 相同点:性能几乎认为是相同的。
  • 不同点:ZGC 是 Oracle JDK 的,而 Shenandoah 只存在于 Open JDK 中,因此使用时需要注意 JDK 版本。

Shenandoah 在 Java 12 作为实验性特性引入,在 Java 15 变为正式标准,之前需要通过 -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC 参数,现在只需要添加 -XX:+UseShenandoahGC 即可。

其他变化

edDSA签名算法

Edwards-Curve Digital Singnature Algorithm:数字曲线签名算法。

这是一个新功能,新加基于 EdWardS-Curve 数字签名算法。与 JDK 中现有的签名方案相比,EdDSA 具有更高的安全性和性能,因此备受关注。它已经在 OpenSSL 和 BoringSSL 等加密库中得到支持,在区块链领域用的比较多。

EdDSA 是一种现代椭圆曲线方案,具有 JDK 中现有签名方案的优点,EdDSA 将只在 SunECMA 提供中实现。

禁用偏向锁定

Disable and Deprecate Biased Locking:禁用偏向锁定。

在默认情况下禁用偏向锁定,并弃用所有的相关命令选项。目标是确定是否需要继续支持偏置锁定的高维护成本的遗留同步优化。HotSpot 虚拟机使用该优化来减少非竞争锁的开销。尽管某些 Java 应用程序在禁用偏向锁后可能会出现性能下降,但是偏向锁的性能提高通常不像以前那么明显。

该特性默认禁用了 biased locking(-XX:+UseBisaedLocking),并且废弃了所有相关的命令行选型。

重新实现DatagramSocketAPI

Reimplement the legcy DatagramSocketAPI:重新实现 DatagramSocketAPI。

作为 JEP 353 的后续,该方案重新实现了遗留的套接字 API。java.net.datagram.Socketjava.netMulticastSocket 的当前实现可以追溯到 JDK1.0,当时 IPV6 还在开发中。因此,当前的套接字实现尝试调和 IPV4 和 IPV6 难以维护的方式。

具体实现:

  • 通过替换 java.net.datagram 的基础实现,重新实现旧版 DatagramSocket API。
  • 更改 java.net.DatagramSocketjava.net.MulticastSocket 为更加简单,现代化的底层实现,提高了 JDK 的可维护性和稳定性。

外部存储API

Foreign-Memory Access API(Second Incubator):外部存储器访问 API。

目的是引入一个 API,以允许 Java 程序安全。有效地访问 Java 对之外的外部存储器。如本机、持久和托管堆。

有许多 Java 程序访是访问外部内存的,比如 Ignite 和 MapDB。该 API 将有助于避免与垃圾收集相关的成本以及与跨进程共享内存以及通过将文件映射到内存来序列化和返序列化内存内容相关的不可预测性。该 Java API 目前没有为访问外部内存停工令人满意的解决方案。但是在新的提议中,API 不应该破坏 JVM 的安全性。

Foreign-Memory Access API 在 Java 14 中作为 incubating API 引入,在 Java 15 中处于 Second Incubator,提供了改进。

相关推荐
breaksoftware1 小时前
Java版Flink使用指南——分流导出
java·flink·ruby
hit、run2 小时前
通过,注解@value,读取配置文件中的数据(并设置默认值)
java
小鹿( ﹡ˆoˆ﹡ )2 小时前
Java:构造函数与对象
java·开发语言·python
机智的土拨鼠2 小时前
解决本地操作云服务器上的Redis
java·linux·服务器·windows·redis·ubuntu
如果'\'真能转义说3 小时前
Java常用的API_02(正则表达式、爬虫)
java·爬虫·正则表达式
真的想不出名儿3 小时前
正则表达式
java·正则表达式
搁浅小泽5 小时前
C语言编译和编译预处理
c语言·开发语言
云边有个稻草人5 小时前
【C语言】自定义类型:联合和枚举
c语言·开发语言·算法
tonylua5 小时前
Python: 从 2.7 升级到 3,我比 vue 慢了一点点
开发语言·前端·javascript·vue.js·python
hummhumm6 小时前
数据结构第18节 散列表 - 应用
java·数据结构·spring boot·spring cloud·java-ee·maven·散列表