日志

JDK15真的来了,一起来看看它的新特性

 来源    2020-09-16    1  

简介

一年两次的JDK最新版本JDK15在2020年9月15日正式发布了,这次的JDK15给我们带了隐藏类,EdDSA,模式匹配,Records,封闭类和Text Block等诸多新特性。

一起来看看吧。

JDK15的新特性

JEP 385 Deprecate RMI Activation for Removal

RMI Activation被标记为Deprecate,将会在未来的版本中删除。

RMI大家应该都清楚,RMI就是Remote Method Invocation,翻译成中文就是远程方法调用,是在JDK1.2中引入的。

RMI为java提供了开发分布式系统的强大能力。而J2EE的规范EJB就是使用RMI来实现的bean的远程调用的。

在RMI系统中,远程系统中存在很多分布式对象,如果这些分布式对象一直处于活动状态的话,将会占用很多宝贵的系统资源。

于是RMI引入了一种lazy Activation的方式,这种方式就叫做延迟激活。

这里有两个概念,活动对象和被动对象。

活动对象是在某些系统上的JVM中实例化并对外暴露的远程对象。被动对象是尚未在JVM中实例化(或暴露)但可以进入主动状态的对象。

将被动对象转换为主动对象的过程称为激活。激活要求对象与JVM关联,这可能会将该对象的类加载到JVM中,并且将该对象恢复为之前的状态。

在RMI系统中,我们使用延迟激活。延迟激活将激活对象推迟到客户第一次使用(即第一次方法调用)之前。

既然RMI Activation这么好用,为什么要废弃呢?

因为对于现代应用程序来说,分布式系统大部分都是基于Web的,web服务器已经解决了穿越防火墙,过滤请求,身份验证和安全性的问题,并且也提供了很多延迟加载的技术。

所以在现代应用程序中,RMI Activation已经很少被使用到了。并且在各种开源的代码库中,也基本上找不到RMI Activation的使用代码了。

为了减少RMI Activation的维护成本,在JDK8中,RMI Activation被置为可选的。现在在JDK15中,终于可以废弃了。

JEP 371 Hidden Classes

Hidden Classes是什么呢?

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

通常来说基于JVM的很多语言都有动态生成类的机制,这样可以提高语言的灵活性和效率。

比如在JDK8中引入的lambda表达式,JVM并不会在编译的时候将lambda表达式转换成为专门的类,而是在运行时将相应的字节码动态生成相应的类对象。

另外使用动态代理也可以为某些类生成新的动态类。

那么我们希望这些动态生成的类需要具有什么特性呢?

  1. 不可发现性。因为我们是为某些静态的类动态生成的动态类,所以我们希望把这个动态生成的类看做是静态类的一部分。所以我们不希望除了该静态类之外的其他机制发现。

  2. 访问控制。我们希望在访问控制静态类的同时,也能控制到动态生成的类。

  3. 生命周期。动态生成类的生命周期一般都比较短,我们并不需要将其保存和静态类的生命周期一致。

但是现有的类的定义API ClassLoader::defineClass和Lookup::defineClass是不管类的字节码是如何生成的,他们都是平等对待。

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

比如:

java.lang.reflect.Proxy可以定义隐藏类作为实现代理接口的代理类。

java.lang.invoke.StringConcatFactory可以生成隐藏类来保存常量连接方法;

java.lang.invoke.LambdaMetaFactory可以生成隐藏的nestmate类,以容纳访问封闭变量的lambda主体;

JavaScript引擎可以为从JavaScript程序转换的字节码生成隐藏的类,因为当引擎不再使用它们时,这些类将被卸载。

普通类是通过调用ClassLoader::defineClass创建的,而隐藏类是通过调用Lookup::defineHiddenClass创建的。

这使JVM从提供的字节中派生一个隐藏类,链接该隐藏类,并返回提供对隐藏类的反射访问的查找对象。

调用程序可以通过返回的查找对象来获取隐藏类的Class对象。

JEP 339 Edwards-Curve Digital Signature Algorithm (EdDSA)

实现了EdDSA椭圆曲线签名算法。

这里就不多讲椭圆曲线签名算法了,如果又想了解的朋友可以给我留言。

JEP 375 Pattern Matching for instanceof (Second Preview)

Pattern Matching 就是说可以在做pattern mathching的时候,直接对该对象进行类型的转换。

现在这个特性还是预览版本的。

我们看一下具体的例子:

if (obj instanceof String) {
    String s = (String) obj;
    // use s
}

在Pattern Matching之前,我们使用instanceof之后,还需要对该对象进行强制类型转换才能使用。

但是在Pattern Matching之后,我们可以这样用:

if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}

是不是很方便。

JEP 384 Records (Second Preview)

Record是一种轻量级的class,可以看做是数据结构体。和scala中的case有点相似。

举个自定义User的例子看一下Record是怎么用的:

public record Address(
        String addressName,
        String city
) {
}
public record CustUser(
        String firstName,
        String lastName,
        Address address,
        int age
) {}

上面我们定义了两个类,CustUser和Address。CustUser中引用了Address。

Record和普通的类的区别就在于Record多了一个括号括起来的定义的字段。

Record类默认是final的,里面的字段默认是private final的。

要想知道Record到底是怎么工作的,我们可以使用javap来对编译好的class文件反编译,运行javap CustUser,可以得到下面的结果:

警告: 二进制文件CustUser包含com.flydean.records.CustUser
Compiled from "CustUser.java"
public final class com.flydean.records.CustUser extends java.lang.Record {
  public com.flydean.records.CustUser(java.lang.String, java.lang.String, com.flydean.records.Address, int);
  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public java.lang.String firstName();
  public java.lang.String lastName();
  public com.flydean.records.Address address();
  public int age();
}

上面可以看到final class CustUser继承自java.lang.Record。

并且自动添加了默认带有所有字段的构造函数。各个自动的获取方法,并实现了toString,hashCode和equals方法。

天啦,太完美了,我们想要的它居然都有。

如果上面的javap还不是很清楚的话,大家可以借助IDE的反编译功能,打开CustUser.class文件看一看:

public final class CustUser extends java.lang.Record {
    private final java.lang.String firstName;
    private final java.lang.String lastName;
    private final com.flydean.records.Address address;
    private final int age;

    public CustUser(java.lang.String firstName, java.lang.String lastName, com.flydean.records.Address address, int age) { /* compiled code */ }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public java.lang.String firstName() { /* compiled code */ }

    public java.lang.String lastName() { /* compiled code */ }

    public com.flydean.records.Address address() { /* compiled code */ }

    public int age() { /* compiled code */ }
}

注意,上面的反编译我们可以看到,record中的所有字段都是final的,只能在初始化的时候设置。并且方法里面也没有提供其他可以改变字段内容的方法。

所以我们得出了一个震世惊俗的结论:record是immutable的。

上面的例子中我们只使用了小括号里面的内容,大括号还是空的呀。可不可以像其他正常的类一样,添加点方法或者构造函数进去呢?

答案是肯定的。

先看一个整体的方案:

public record CustUserWithBody(
        String firstName,
        String lastName,
        Address address,
        int age
) {
    public String fullName(){
        return firstName+ lastName;
    }

    public CustUserWithBody{
        if (age < 18) {
            throw new IllegalArgumentException( "男大当婚,女大当嫁,18岁未到,不许出嫁!");
        }
    }
}

我们在record的主题中,定义了一个方法和一个构造函数。

先看这个方法,在方法中我们可以访问到record中定义的变量,但是千万不要尝试去修改他们,因为他们是final的,你会得到一个变异错误。

再看这个构造函数,这个构造函数没有小括号,只有大括号,这种构造函数叫做Compact constructor。你无法在record中定义正常的构造函数,因为会得到一个编译错误。

在这个Compact constructor中,我们可以对定义的字段进行数据校验。如上所述。

JEP 360 Sealed Classes (Preview)

在Java中,类层次结构通过继承实现代码的重用,父类的方法可以被许多子类继承。

但是,类层次结构的目的并不总是重用代码。有时,其目的是对域中存在的各种可能性进行建模,例如图形库支持的形状类型或金融应用程序支持的贷款类型。

当以这种方式使用类层次结构时,我们可能需要限制子类集从而来简化建模。

因为我们引入了sealed class或interfaces,这些class或者interfaces只允许被指定的类或者interface进行扩展和实现。

举个例子:

package com.example.geometry;

public abstract sealed class Shape
    permits Circle, Rectangle, Square {...}

上面的例子中,我们指定了Shape只允许被Circle, Rectangle, Square来继承。

上面的例子中并没有指定类的包名,我们可以这样写:

package com.example.geometry;

public abstract sealed class Shape 
    permits com.example.polar.Circle,
            com.example.quad.Rectangle,
            com.example.quad.simple.Square {...}

JEP 378 Text Blocks

Text Blocks是为了解决在java中输入多行数据的问题。

比如:

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";

可以写成:

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;
String query = "SELECT \"EMP_ID\", \"LAST_NAME\" FROM \"EMPLOYEE_TB\"\n" +
               "WHERE \"CITY\" = 'INDIANAPOLIS'\n" +
               "ORDER BY \"EMP_ID\", \"LAST_NAME\";\n";

可以写成:

String query = """
               SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
               WHERE "CITY" = 'INDIANAPOLIS'
               ORDER BY "EMP_ID", "LAST_NAME";

非常的方便。

总结

好了,JDK15的新特性全都介绍完了。希望大家能够喜欢。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jdk15-release-new-features/

本文来源:flydean的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

numpy – xarray或dask真的支持内存映射吗?
问答在我的实验到目前为止,我尝试过: > xr.open_dataset with chunks arg,它将数据加载到内存中. >设置NetCDF4DataStore,并调用ds ['fie ...
1
数组 – 警告“同时访问参数’self’……”真的适用于此吗?
问答我写了一个Array的扩展,允许我弹出最后一个元素并立即将它添加到另一个数组: extension Array { mutating func popLast(to otherArray: inout ...
1
java – Spring 3.0真的支持JSR-286吗?
问答Spring 3.0 Portlet MVC真的支持JSR-286又名Porlet 2.0吗?我已经看过轶事提到它,但没有任何记录形式. 如果是这样,有没有人成功实现了Spring 3.0的JSR-2 ...
1
java – MVP视图真的如此愚蠢?
问答我正在努力学习MVP,但有些事情让我望而却步;如果Presenter将视图用作界面,那么View不能只是一个简单的控件渲染.想象一下,尝试编写一个打字练习游戏,其中文字随机生成到用户界面中,用户必须在 ...
2
ios – 如何创建逼真的.scn文件?
问答看着苹果样品AR应用程序,有许多逼真的物体(杯子,蜡烛等).但是,在Xcode上使用场景工具包编辑器很明显,这只允许您创建基本对象. 我的问题是,什么软件/文件可用于创建逼真的scn对象?我确信有一些 ...
1
真的,为什么XCode总是报告每次编译错误两次?
问答所以,得到这个:自从我开始使用XCode来处理我的软件开发(目标C)以来,我已经容忍了每个编译错误(每个)被报告两次的事实.我从来没有错误计数是一个奇数! 因此,如果我有一个语法错误,Xcode会报告 ...
1
如何告诉GCC输入文件是汇编程序,即使它真的是.c?
问答我有一个不寻常的情况:我有一个汇编程序文件,它具有.c扩展名,我无法更改构建脚本,其中此文件被定义为"C"类型.但是,如果我将其重命名为.asm或* .s,我可以使用相同的GCC选 ...
1
Android:短片真的需要2个字节吗?
问答我正在尝试决定如何设计我的应用程序. 我有大约300个类的实例,就像这样: public class ParamValue { protected String sValue = null; prot ...
1
java – 数组真的是同质的吗?
问答在下面的代码中,EX1和EX2提供了齐次理论,但在EX3中它保存了多种类型的值.那么,我们怎么说阵列是同质的呢?这背后的确切理论是什么? public class Test { public stat ...
1
Objective C真的编译好了吗?它不是更像Visual Basic / .NET运行时吗?那么是什么阻止它可以移植到其他平台?
问答从语法上讲它是C的超集.但是由于消息是在运行时发送和处理的,这意味着它不能像c这样的纯编译语言,但它需要像Visual Basic或.Net运行时这样的运行时. 那么是什么阻止它通过将此运行时转换为. ...
1
c – pop_back()是否真的使std :: vector上的* all *迭代器无效?
问答std::vector<int> ints; // ... fill ints with random values for(std::vector<int>::iterato ...
1
CruiseControl.NET,Nant真的有必要吗?
问答我正在尝试为CI设置CruiseControl.NET.我无法找到仅设置文件系统观察程序的参考和示例来启动解决方案文件的构建.我遇到的一个问题是我发现使用Nant的例子需要设置构建脚本.和Nant一起 ...
2
cuda – 我的GTX680真的很棒
问答我正在尝试测试我的GTX680的计算性能,因为我有点怀疑它的真实性能.我想知道是否有人也可以测试他的GTX 680,如果给出相同的结果,或者告诉我它可以做得更好,以便从卡中获得更多性能 我写过这个小程 ...
2
安全性 – 完全交换网络上的密码嗅探真的是一个问题吗?
问答我管理许多需要用户telnet访问的linux服务器. 目前,用户的凭证本地存储在每个服务器上,并且密码往往非常弱,并且不需要更改它们. 登录很快就会与Active Directory集成,这是一个更 ...
2
为什么〜(真的是真的)不是真的吗?布尔运算符(否定)适用于`unsigned char`s,但不适用于bools? (C )
问答我听说除了0以外通常"一切"都是真的.但现在我发生了很奇怪的事情--或者我只是认为我这样做是正确的,而我却没有.这是发生了什么: 当我想检查a是否等价时,我可以使用NOT(异或). ...
1
java – 生成初始化向量真的需要SecureRandom还是足够随机?
问答为了使流密码能够抵御重复的密钥攻击,IV不应该重复.但是SecureRandom在这方面是否优于简单的非安全随机(或仅用于生成不可预测的序列)? 假设我使用AES CBC模式的固定大小的消息,并且我为 ...
1
语言无关 – 我曾经读过静态类非常困难,甚至无法调试.这是真的吗?为什么?
问答我曾经读过静态类非常困难,甚至无法调试.这是真的吗?为什么? 如果一个例子有帮助,这里有一个我用来访问数据库的PHP类(我不认为这是一个特定于PHP的问题): <?php class DB { ...
1
objective-c – 我们真的需要安全发布宏吗?
问答相当多的人似乎使用了诸如此类的宏 #define SAFE_RELEASE(X) [X release]; X = nil; (包括我自己). 我一直在重新评估我为什么要使用它并希望画出一些意见. 使 ...
1
javascript – HTML5文件API真的会改变用户的文件上传体验吗?
问答我正在使用HTML5 File API将文件上传到服务器. 它提供以下惊人的东西, >上传支持progress事件的对象,可以向最终用户显示良好的进度UI. > XHR.send(File ...
1
我的iPhone应用程序真的需要锁定吗?
问答我正在开发一个相对简单的iPhone应用程序,它有一个多轮定时器,有许多设置,如轮数和圆长.我们允许在定时器运行时更​​新某些设置,这意味着定时器可能正在从设置正在写入的同一内存中读取.没有关键的代码 ...
2