Java15的主要新特性总结

目录

概述

变动说明

重要变更和信息

下载地址

Java15新特性总结

1、JEP 378:文本块(正式特性)

功能进化

2、JEP 371:隐藏类

隐藏类示例

3、JEP 360:密封的类和接口(预览特性)

密封类

什么是密封类

示例代码

4、JEP 384:Record类(第二次预览)

功能进化

一个示例类

紧凑型构造函数

使用限制

与record相关的API

5、JEP 375:模式匹配的 instanceof(第二次预览)

功能进化

6、JEP 339:Edwards-Curve 数字签名算法 (EdDSA)

7、JEP 372:移除 Nashorn JavaScript 引擎

8、JEP 373:重新实现 DatagramSocket API

9、JEP 374:禁用偏向锁定

10、JEP 377:ZGC—可伸缩低延迟垃圾收集器(正式特性)

功能进化

11、JEP 379:Shenandoah—低暂停时间垃圾收集器(正式特性)

功能进化

12、JEP 381:移除 Solaris 和 SPARC 平台的支持

13、JEP 383:外部存储器访问 API (二次孵化器版)

14、JEP 385: 废除 RMI 激活

17、移除的APIs、工具、容器

Oracle JDK和OpenJDK之间的差异


概述

JDK 15 于 2020 年 9 月 15 日正式发布。。

JEP(Java Enhancement Proposal)Java增强提案

CSR(Compatibility & Specification Review) 兼容性和规范审查

变动说明

官网:

Java Platform, Standard Edition Java Language Updates, Release 15

JDK 15 Release Notes

JDK 15

更多参考:

JDK 15 Documentation - Home 更多版本:Java Platform, Standard Edition Documentation - Releases

Java Platform, Standard Edition Oracle JDK Migration Guide, Release 15

重要变更和信息

JDK 15 包含 14 个 新特性 ,分别为:

而其中与开发过程中直接相关的特性主要包括:JEP 360(密封的类和接口(预览特性))、JEP 371(隐藏类)、JEP 375(模式匹配的 instanceof(第二次预览))、JEP 378(文本块)、JEP 384(Record (第二次预览))等。

下载地址

您可以从这个链接下载生产就绪的OpenJDK版本。文件为压缩包,解压并设置环境变量就可以使用。

当然你也可以从这个链接下载Oracle JDK版本(但是需要注意商用限制),更多版本下载

Java15新特性总结

1、JEP 378:文本块(正式特性)

JEP 378tools/javac

功能进化
Java版本特性类型JEP特性
Java 13预览特性JEP 355引入文本块,支持多行字符串定义
Java 14预览特性JEP 368改进文本块,增加了2个转义字符:\(不换行)和\s(空格)。
Java 15正式特性JEP 378成为正式特性

此功能首次在Java SE 13中预览,在本版本中是永久性的。这意味着它可以用于为Java SE 15编译的任何程序,而无需启用预览功能。

将文本块添加到Java语言中。文本块是一个多行字符串文字,它避免了大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。

文本块是通过三个双引号(""")来定义的,这标志着字符串的开始和结束。在这两个标志之间的所有文本,包括换行符和空格,都将被包含在字符串中。

我们以前从外部copy一段文本串到Java中,会被自动转义,如有一段以下字符串:

  <html>
   <body>
       <p>天地玄黄,宇宙洪荒</p>
   </body>
 </html>

将其复制到Java的字符串中,会展示成以下内容:

 "<html>\n"  
 "    <body>\n"  
 "        <p>天地玄黄,宇宙洪荒</p>\n"  
 "    </body>\n"  
 "</html>\n";

即被自动进行了转义,这样的字符串看起来不是很直观,在JDK 13中,可以使用文本块定义。文本块是通过三个双引号(""")来定义的,这标志着字符串的开始和结束。在这两个标志之间的所有文本,包括换行符和空格,都将被包含在字符串中。

示例代码:

 @Test
 public void test() {
     // 旧写法
     String html = "<html>\n" + //
         "   <body>\n" + //
         "       <p>天地玄黄,宇宙洪荒</p>\n" + //
         "   </body>\n" + //
         "</html>";
     System.out.println(html);
 ​
     // 新写法。
     String html1 = """
         <html>
         <body>
         <p>天地玄黄,宇宙洪荒</p>
         </body>
         </html>
         """;
         System.out.println(html1);
 }

执行结果:

 <html>
    <body>
        <p>天地玄黄,宇宙洪荒</p>
    </body>
 </html>
 ​
 <html>
     <body>
         <p>天地玄黄,宇宙洪荒</p>
     </body>
 </html>

2者输出结果是一样的。但是显然新写法写起来也简单,也更易读。

SQL语句也可以使用:

 String query = """
                     SELECT `id`, `name` FROM `users`
                     WHERE `sex` = 1
                     ORDER BY `age`, `create_time`;
                 """;

文本块可以用来定义一段格式化文本,而不需要想原来每一行都需要增加换行处理。

使用文本块好处:

  1. 多行字符串的简化:在之前的java中编写多行字符串时,只能需要通过使用\n来实现换行,通过+来连接多个字符串。文本块则只需要将文本通过三个双引号(""")包裹起来。

  2. 格式化和缩进处理:采用字符串拼接的方式,是无法格式化和缩进处理的,而使用文本块则会自动处理字符串的格式化和缩进。

  3. 无需处理特殊字符:在之前的java中对于特殊字符需要进行转义处理,但使用文本块后,则不需要。

文本块接受另外两个转义序列,分别是:\\s:(参见《程序员文本块指南》)。

  • \ (终止换行符):这个转义符用于去除行尾换行符,使当前文本的下一行不换行,而是直接拼在当前文本后面。

  • \s (空格标识符):这个转义符表示这是一个空格,在它前面的空格也会保留,如果没有这个标识,前面的空格不会保留。

示例代码:

 @Test
 public void test() {
     // 注意里面三山,文本比较长,不方便阅读。但是如果直接换行会影响最后的输出。这里需要一个标识符,标识一下不换行,换行只为了方便阅读。这就是\的作用。
     //
     String text = """
         三山五岳,汉语成语,泛指华夏大地各名山。
         三山,有三种说法:(1)是指华夏远古神话传说中的三条龙脉:喜马拉雅山脉(盘古开天辟地、共工怒触不周山)、昆仑山脉(玉帝居庭玉京山、嫦娥奔月)、天山山脉(西王母娘居庭、女娲炼石补天);\
         (2)是道教传说中的三座仙山:蓬莱(蓬壶)、方丈山(方壶)、瀛洲(瀛壶);\
         (3)今人所喜欢的三座旅游名山:黄山、庐山、雁荡山。
         五岳指东岳泰山、西岳华山、南岳衡山、北岳恒山、中岳嵩山。
         三山五岳遍布华夏大地,是中华民族的摇篮,是华夏祖先最早定居的地方,对中华民族的历史文化发展与研究有着重要的意义。
         """;
         System.out.println(text);
 ​
     // 这里面五岳后面分别跟了0、1、2、3、4个空格,但是直接输出最后的空格会被忽略掉。
     String wuyue1 = """
         东岳泰山
         西岳华山
         南岳衡山
         北岳恒山
         中岳嵩山
         """;
         System.out.println(wuyue1);
     // 这里面五岳后面分别跟了0、1、2、3、4个空格,但是直接输出最后的空格会被忽略掉。用\s就不会忽略。注意\s本身标识空格,所以实际上最终输出的空格比前面说的多1个。
     String wuyue2 = """
         东岳泰山\s
         西岳华山 \s
         南岳衡山  \s
         北岳恒山   \s
         中岳嵩山    \s
         """;
         System.out.println(wuyue2);
 ​
 }

执行结果:

 三山五岳,汉语成语,泛指华夏大地各名山。
 三山,有三种说法:(1)是指华夏远古神话传说中的三条龙脉:喜马拉雅山脉(盘古开天辟地、共工怒触不周山)、昆仑山脉(玉帝居庭玉京山、嫦娥奔月)、天山山脉(西王母娘居庭、女娲炼石补天);(2)是道教传说中的三座仙山:蓬莱(蓬壶)、方丈山(方壶)、瀛洲(瀛壶);(3)今人所喜欢的三座旅游名山:黄山、庐山、雁荡山。
 五岳指东岳泰山、西岳华山、南岳衡山、北岳恒山、中岳嵩山。
 三山五岳遍布华夏大地,是中华民族的摇篮,是华夏祖先最早定居的地方,对中华民族的历史文化发展与研究有着重要的意义。
 ​
 东岳泰山
 西岳华山
 南岳衡山
 北岳恒山
 中岳嵩山
 ​
 东岳泰山 [这是人为添加的一个符号为了显示空格的位置]
 西岳华山  [这是人为添加的一个符号为了显示空格的位置]
 南岳衡山   [这是人为添加的一个符号为了显示空格的位置]
 北岳恒山    [这是人为添加的一个符号为了显示空格的位置]
 中岳嵩山     [这是人为添加的一个符号为了显示空格的位置]

2、JEP 371:隐藏类

JEP 371core-libs/java.lang.invoke

Java 15 引入隐藏类主要是为了改进框架和库的开发,尤其是那些需要动态生成类的场景,它主要针对的某些特定的使用场景,而不是为了日常应用程序开发。

隐藏类有如下几个特点:

  • 不可见性:隐藏类对Java语言使用者是不可见的,也就是说,它们不能被常规Java代码直接使用或者通过反射API访问。

  • 单一加载器局限性:隐藏类是由特定的类加载器加载的,它们的生命周期与加载它们的类加载器紧密相连。这意味着它们只对加载它们的类加载器可见。

  • 非继承性:隐藏类不可以被显式继承,也就是说,你不能声明一个类显式地继承一个隐藏类。

  • 链接上的限制:虽然隐藏类与常规类一样,会被链接到它们的定义类加载器中,但是它们不会被启动类加载器或任何其他类加载器所链接,这防止了它们被其他类加载器意外加载。

隐藏类的主要使用场景还是框架,他就是为框架而生,是 JVM 内部的一部分,对于 CRUD Boy来说,通常是透明的。

隐藏类示例

第一步:创建类

先创建一个普通的Java类

 public class HiddenClassesModel {
 ​
     public static String print() {
         return "初唐四杰";
     }
 ​
     public String show() {
         return "王勃、杨炯、卢照邻、骆宾王";
     }
 ​
 }

第二步:编译

编译一下获取class文件。然后使用Base64对文件内编码获取字符串,代码如下:

public static void main(String[] args) throws IOException, URISyntaxException {
    String filePath = "HiddenClassesModel.class";
    // 1、需要定义父目录的路径,这个需要根据实际路径修改
    String parentDir = "target/test-classes/com/ld/mytest/test/java15/hiddenclasses";
    System.out.println("Paths路径:" + Paths.get(parentDir, filePath).toAbsolutePath());
    byte[] b = Files.readAllBytes(Paths.get(parentDir, filePath));
    System.out.println(Base64.getEncoder().encodeToString(b));

    // 2、根据当前类动态处理路径
    String baseName = HiddenClassesTest.class.getName();
    int index = baseName.lastIndexOf('.');

    if (index != -1) {
        filePath = baseName.substring(0, index).replace('.', '/') + "/" + filePath;
    }
    System.out.println("文件路径:" + filePath);
    System.out.println("资源路径:" + HiddenClassesTest.class.getClassLoader().getResource(filePath));

    b = Files.readAllBytes(Paths.get(HiddenClassesTest.class.getClassLoader().getResource(filePath).toURI()));
    System.out.println(Base64.getEncoder().encodeToString(b));

}

执行一下,获取到内容如下:

yv66vgAAADwAFwcAAgEAOmNvbS9sZC9teXRlc3QvdGVzdC9qYXZhMTUvaGlkZGVuY2xhc3Nlcy9IaWRkZW5DbGFzc2VzTW9kZWwHAAQBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWAQAEQ29kZQoAAwAJDAAFAAYBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQA8TGNvbS9sZC9teXRlc3QvdGVzdC9qYXZhMTUvaGlkZGVuY2xhc3Nlcy9IaWRkZW5DbGFzc2VzTW9kZWw7AQAFcHJpbnQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwgAEQEADOWIneWUkOWbm+adsAEABHNob3cIABQBACfnjovli4PjgIHmnajngq/jgIHljaLnhafpgrvjgIHpqoblrr7njosBAApTb3VyY2VGaWxlAQAXSGlkZGVuQ2xhc3Nlc01vZGVsLmphdmEAIQABAAMAAAAAAAMAAQAFAAYAAQAHAAAALwABAAEAAAAFKrcACLEAAAACAAoAAAAGAAEAAAADAAsAAAAMAAEAAAAFAAwADQAAAAkADgAPAAEABwAAACMAAQAAAAAAAxIQsAAAAAIACgAAAAYAAQAAAAYACwAAAAIAAAABABIADwABAAcAAAAtAAEAAQAAAAMSE7AAAAACAAoAAAAGAAEAAAAKAAsAAAAMAAEAAAADAAwADQAAAAEAFQAAAAIAFg==

这个内容就是第一步写的类。

第三步:通过反射加载上面生成的类,并调用隐藏类中的hello函数,代码如下:

@Test
void testHiddenClasses() throws Throwable {
    // 1. 加载encode之后的隐藏类。字符串即上面返回的结果
    String classStr = "yv66vgAAADwAFwcAAgEAOmNvbS9sZC9teXRlc3QvdGVzdC9qYXZhMTUvaGlkZGVuY2xhc3Nlcy9IaWRkZW5DbGFzc2VzTW9kZWwHAAQBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWAQAEQ29kZQoAAwAJDAAFAAYBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQA8TGNvbS9sZC9teXRlc3QvdGVzdC9qYXZhMTUvaGlkZGVuY2xhc3Nlcy9IaWRkZW5DbGFzc2VzTW9kZWw7AQAFcHJpbnQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwgAEQEADOWIneWUkOWbm+adsAEABHNob3cIABQBACfnjovli4PjgIHmnajngq/jgIHljaLnhafpgrvjgIHpqoblrr7njosBAApTb3VyY2VGaWxlAQAXSGlkZGVuQ2xhc3Nlc01vZGVsLmphdmEAIQABAAMAAAAAAAMAAQAFAAYAAQAHAAAALwABAAEAAAAFKrcACLEAAAACAAoAAAAGAAEAAAADAAsAAAAMAAEAAAAFAAwADQAAAAkADgAPAAEABwAAACMAAQAAAAAAAxIQsAAAAAIACgAAAAYAAQAAAAYACwAAAAIAAAABABIADwABAAcAAAAtAAEAAQAAAAMSE7AAAAACAAoAAAAGAAEAAAAKAAsAAAAMAAEAAAADAAwADQAAAAEAFQAAAAIAFg==";
    byte[] classInBytes = Base64.getDecoder().decode(classStr);

    Lookup lookup = MethodHandles.lookup();

    Class<?> hiddenCls = lookup.defineHiddenClass(classInBytes, true, MethodHandles.Lookup.ClassOption.NESTMATE).lookupClass();

    // 输出类名
    System.out.println(hiddenCls.getName());
    // 输出类有哪些函数
    for (Method method : hiddenCls.getDeclaredMethods()) {
        System.out.println(method.getName());
    }
    // 2. 调用hello函数
    var mh = lookup.findStatic(hiddenCls, "print", MethodType.methodType(String.class));
    String result = (String) mh.invokeExact();
    System.out.println("print方法执行:" + result);

    // 创建隐藏类的实例
    Object hiddenClsInstance = hiddenCls.getConstructor().newInstance();

    mh = lookup.findVirtual(hiddenCls, "show", MethodType.methodType(String.class));
    // 调用 print 方法
    result = (String) mh.invoke(hiddenClsInstance);
    System.out.println("show方法执行:" + result);
}

执行结果:

print
show
print方法执行:初唐四杰
show方法执行:王勃、杨炯、卢照邻、骆宾王

整体就分为两个步骤:

  1. 生成字节码

  2. 反射调用方法。使用 LookupMethodHandlesMethodType 来创建隐藏类和调用方法。

这里的classStr内容就是上一步我们处理好的隐藏类内容,先通过Base64将该内容解码,然后通过反射机制来将这个隐藏类的代理创建出来。

可以看到这里创建隐藏类的时候用的是java.lang.invoke.MethodHandles.Lookup#defineHiddenClass,而创建普通类则是用ClassLoader::defineClass(保护属性,无法直接在外部访问,一般使用反射的访问)的。

public Lookup defineHiddenClass(byte[] bytes, boolean initialize, ClassOption... options) throws IllegalAccessException

这里的三个参数分别是:

  • bytes:符合java虚拟机规范的字节码

  • initialize:是否要初始化类

  • options:java类的类型,详见java.lang.invoke.MethodHandles.Lookup.ClassOption

我们需要注意的,隐藏类,是一个高级特性,主要用于库和框架的开发者,并不是面向普通的应用程序猿。而且,由于涉及到底层的字节码操作和类加载机制,所以在使用这一特性时,我们需要对Java的内部工作机制有深入的理解。

3、JEP 360:密封的类和接口(预览特性)

JEP 360

这是一个预览特性。

密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。

继承,作为面向对象语言的三大特性之一,我们工作过程中经常使用,可以重写父类的方法。

通常开发项目时,我们会先将接口提供出来,然后根据情况给出不同的基础实现类,子类再基础这些基础实现类进行扩展,我们可能并不希望子类直接继承接口,当然直接继承接口的写法从代码上看没有任何问题,但存在安全隐患。一般我们会通过开发约束对,这样的情况说一些要求,但是这样并不能杜绝这类问题。

密封类

为了进一步增强继承的限制能力,Java 15 引入密封类来精确控制类的继承问题 ,目前版本为预览特性。

什么是密封类

密封类的主要目的是提供一种更加精确地控制类继承的方法,通过这种方式,类的设计者可以指定一个类它能够被哪些类继承,它增强了类的封装性和安全性。由于密封类限制了类的继承,所以它使得代码更加可预测和易于维护。

  • 密封类(接口)用 sealed 修饰,则它的所有子类都必须在同一个模块或者包内,并且这些子类必须被显式地声明为该密封类的直接子类。

  • 密封类(接口)的子类可以被声明为non-sealed(非密封的)或final(最终的)。non-sealed的子类可以被进一步继承,而final的子类则不能。

  • 密封类(接口)使用 permits 来指定它的子类。

示例代码

这里我们以诗人为例,简化一下,我们这里只讨论汉朝诗人、唐朝诗人、宋朝诗人,代码如下:

// 诗人基类
public class Poet {
}

// 汉朝诗人
public class HanPoet extends Hero{
}
// 唐朝诗人
public class TangPoet extends Poet{
}

// 宋朝诗人
public class SongPoet extends Hero{
}

接下来我们每个类别下面定义2个诗人:

  • 汉朝诗人(HanPoet):司马相如(SiMaXiangRu)、班固(BanGu)、

  • 唐朝诗人(TangPoet):李白(Libai)、杜甫(DuFu)

  • 宋朝诗人(SongPoet):苏轼(SuShi)、陆游(LuYou)

其中李白(Libai)继承自唐朝诗人(TangPoet),我们可以为唐朝诗人做一些公共处理,比如朝代是唐朝,但是有没有这种可能,有程序猿把李白的父类定义为诗人(Poet),那么我们为唐朝诗人定义的那些处理,李白就需要全部重新实现。这显然破坏了继承的实用性。

  • 使用 sealed 修饰Poetpermits 限定子类为: HanPoetTangPoetSongPoet ,只允许这3个类继承,如下:

// 英雄基类,限制子类为:汉朝诗人(HanPoet)、唐朝诗人(TangPoet)、宋朝诗人(SongPoet)
public sealed class Poet permits HanPoet,TangPoet,SongPoet {
}
  • 第二层基类,继续使用 sealed 修饰

// 汉朝诗人,限制子类为:司马相如(SiMaXiangRu)、班固(BanGu)
public sealed class HanPoet extends Hero permits SiMaXiangRu,BanGu{
}

// 唐朝诗人,限制子类为:李白(Libai)、杜甫(DuFu)
public sealed class TangPoet extends Hero permits Libai,DuFu{
}

// 宋朝诗人,限制子类为:苏轼(SuShi)、陆游(LuYou)
public sealed class SongPoet extends Hero permits SuShi,LuYou{
}
  • 第三层为具体诗人,他们继承第二层的诗人类型,使用extends继承即可,同时需要表示为non-sealedfinal,由于我们不希望类再往下了,所以定义为 final

public final class SiMaXiangRu extends HanPoet{
}

public final class Libai extends TangPoet{
}

public final class SuShi extends SongPoet{
}

这样,子类就不能随便继承父类了。

4、JEP 384:Record类(第二次预览)

JEP 384core-libs/java.lang

功能进化
Java版本特性类型JEP特性
Java 14预览特性JEP 359引入Record类作为预览特性
Java 15预览特性JEP 384修正及优化,语法上同上一版没有区别

JDK14中,引入了一个新类java.lang.Record。这是一种新的类型声明。Records 允许我们以一种简洁的方式定义一个类,我们只需要指定其数据内容。对于每个Record类,Java 都会自动地为其成员变量生成 equals(), hashCode(), toString() 方法,以及所有字段的访问器方法(getter),为什么没有 setter方法呢?因为Record的实例是不可变的,它所有的字段都是 final 的,这就意味着一旦构造了一个Record实例,其状态就不能更改了。

与枚举一样,记录也是类的受限形式。它非常适合作为“数据载体”,即包含不想更改的数据的类,以及只包含最基本的方法(如构造函数和访问器)的类。

与前面介绍的其他预览特性一样,这个预览特性也顺应了减少Java冗余代码的趋势,能帮助开发者编写更精炼的代码。

一个示例类

定义一个长方形类

final class Rectangle implements Shape {
    final double length;
    final double width;
    
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    
    double length() { return length; }
    double width() { return width; }
}

它具有以下特点:

  • 所有字段都是final

  • 只包含构造器:Rectangle(double length, double width)和2个访问器方法:length()width()

您可以用record表示此类:

record Rectangle(float length, float width) { }

一个record由一个类名称(在本例中为Rectangle)和一个record属性列表(在本示例中为float lengthfloat width)组成。

record会自动生成以下内容:

  • 为每个属性生成一个private final的字段

  • 为每个属性生成一个与组件名相同的访问方法;在本例中,这些方法是Rectangle::length()Rectangle::width()

  • 一个公开的构造函数,参数包括所有属性。构造函数的参数与字段对应。

  • equals()hashCode()方法的实现,如果两个record类型相同并且属性值相等,那么它们是相等的

  • toString()方法的实现,包括所有字段名和他们的值。

紧凑型构造函数

如果你想在record自定义一个构造函数。那么注意,它与普通的类构造函数不同,record的构造函数没有参数列表:这被称为紧凑型构造函数。

例如,下面的record``HelloWorld有一个字段message。它的自定义构造函数调用Objects.requireNonNull(message),如果message字段是用null值初始化的,则抛出NullPointerException。(自定义记录构造函数仍然会初始化所有字段)

record HelloWorld(String message) {
    public HelloWorld {
        java.util.Objects.requireNonNull(message);
    }
}

测试代码:

@Test
public void test() {
    HelloWorld h1 = new HelloWorld(null); // new HelloWorld("天地玄黄宇宙洪荒"); //用这个测试,可以发现字段还是会初始化的
    System.out.println(h1);
}

这个测试代码执行报java.lang.NullPointerException异常。

使用限制

以下是record类使用的限制:

  • Record类不能继承任何类

  • Record类不能声明实例字段(与record组件相对应的 private final字段除外);任何其他声明的字段都必须是静态的

  • Record类不能是抽象的;它是final

  • Record类的成员变量是final

除了这些限制之外,record类的行为类似于常规类:

  • 可以在类中声明record;嵌套recordstatic

  • record可以实现接口

  • 使用new关键字实例化record

  • 您可以在record的主体中声明静态方法、静态字段、静态初始值设定项、构造函数、实例方法和嵌套类型

  • 可以对recordrecord的属性进行注释

record相关的API

java.lang.Class类有2个方法与record相关:

  • RecordComponent[] 返回类型getRecordComponents(): 返回record的所有字段列表。

  • boolean isRecord(): 与isEnum()类似,如果是record则返回true

5、JEP 375:模式匹配的 instanceof(第二次预览)

JEP 375tools/javac

功能进化
Java版本特性类型JEP特性
Java 14预览特性JEP 305引入instanceof模式匹配为预览特性
Java 15预览特性JEP 375修正及优化,语法上同上一版没有区别

6、JEP 339:Edwards-Curve 数字签名算法 (EdDSA)

JEP 339

Edwards-Curve 数据签名算法(EdDSA),一种根据 RFC 8032 规范所描述的 Edwards-Curve 数字签名算法(EdDSA)实现加密签名,实现了一种 RFC 8032 标准化方案,但它不能代替 ECDSA。

7、JEP 372:移除 Nashorn JavaScript 引擎

JEP 372

Nashorn JavaScript引擎是在Java 8引入的,用于替代旧的Rhino引擎。在JDK11的时候已经被标记为deprecated。在JDK15中只是把他们删除而已。

8、JEP 373:重新实现 DatagramSocket API

JEP 373

重新实现了老的DatagramSocket API接口,更改了java.net.DatagramSocketjava.net.MulticastSocket为更加简单、现代化的底层实现,更易于维护和调试。

新的底层实现将很容易使用虚拟线程,目前正在Loom项目中进行探索。这也是JEP 353的后续更新版本,JEP 353 已经重新实现了Socket API

9、JEP 374:禁用偏向锁定

JEP 374

偏向锁是一种用于减少同步操作开销的技术,它在Java 6中被引入。偏向锁的实现思路是,如果一个线程获得了锁,并且没有其他线程竞争这个锁,那么JVM会假定这个线程将再次获得锁,因此在未来的锁操作中省略了一些与锁相关的检查和更新操作,从而提高性能。

偏向锁定增加了JVM的复杂性,但是在多线程竞争激烈的情况下,它可能不会带来预期的性能提升,有点得不偿失。所以在 Java 15 中,默认情况下禁用了偏向锁,并弃用所有相关的命令行选项(BiasedLockingStartupDelay, BiasedLockingBulkRebiasThreshold, BiasedLockingBulkRevokeThreshold, BiasedLockingDecayTime, UseOptoBiasInlining, PrintBiasedLockingStatistics and PrintPreciseBiasedLockingStatistics)。但是可以通过设置参数 -XX:+UseBiasedLocking 来启用它。

禁用偏向锁定之后,JVM在执行同步操作时将不再考虑偏向模式,这简化了锁的实现。

10、JEP 377:ZGC—可伸缩低延迟垃圾收集器(正式特性)

JEP 377

ZGC 是Java 11引入的新的垃圾收集器,经过了多个实验阶段,在 Java 15 终于成为正式特性。

功能进化
java版本特性类型JEP特性
Java 11预览特性JEP 333引入 ZGC 作为实验特性
Java 15正式特性JEP 377成为正式特性

11、JEP 379:Shenandoah—低暂停时间垃圾收集器(正式特性)

JEP 379

Shenandoah 是一种低暂停时间垃圾收集器,在 Java 12 中作为预览特性引入,Java 15 被提升为正式特性。

功能进化
java版本特性类型JEP特性
Java 12预览特性JEP 189引入 Shenandoah GC 作为实验性垃圾收集器
Java 15正式特性JEP 379成为正式特性

12、JEP 381:移除 Solaris 和 SPARC 平台的支持

JEP 381

移除了 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口的源代码及构建支持。这些端口在 JDK 14 中就已经被标记为 deprecated 了,JDK 15 被移除也不奇怪。

SolarisSPARC已被 Linux 操作系统和英特尔处理器取代。维护对它们的支持已经没什么必要。

13、JEP 383:外部存储器访问 API (二次孵化器版)

JEP 383

14、JEP 385: 废除 RMI 激活

JEP 385

RMI激活机制已被弃用,可能会在平台的未来版本中删除。RMI激活是RMI的一个过时部分,自Java 8以来一直是可选的。它允许RMI服务器JVM在收到客户端的请求时启动(“激活”),而不需要RMI服务器JVM连续运行。RMI的其他部分没有被否决。详见JEP 385。

17、移除的APIs、工具、容器

参考:

Oracle JDK和OpenJDK之间的差异

尽管官方已经声明了让OpenJDKOracle JDK二进制文件尽可能接近的目标,但至少对于JDK 15来说,这两个选项之间仍然存在一些差异。

目前的差异是:

  • Oracle JDK提供了安装程序(msirpmdeb等),它们不仅将JDK二进制文件放置在系统中,还包含更新规则,在某些情况下还可以处理一些常见的配置,如设置常见的环境变量(如Windows中的JAVA_HOME)和建立文件关联(如使用JAVA启动.jar文件)。OpenJDK仅作为压缩档案(tar.gz.zip)提供。

  • Usage Logging仅在Oracle JDK中可用。

  • Oracle JDK要求使用Java加密扩展(JCE(Java Cryptography Extension ))代码签名证书对第三方加密提供程序进行签名。OpenJDK继续允许使用未签名的第三方加密提供程序。

  • java -version命令输出结果不同。Oracle JDK将输出java并包含LTS。Oracle生成的OpenJDK将显示OpenJDK,不包括Oracle特定的LTS标识符。

  • Oracle JDK将在OTN许可证下发布。任何许可证文件都需要指向OTNOpenJDK将在GPLv2wCP下发布,并将包括GPL许可证。

  • Oracle JDK将在FreeType许可证下分发FreeType,而OpenJDK则将在GPLv2下分发。因此,\legal\java.desktop\freetype.md的内容将有所不同。

  • Oracle JDKJava cupsteam图标,而OpenJDK有Duke图标。

  • Oracle JDK源代码包括ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.的说明,OpenJDK源代码包括GPL许可条款。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值