Last Updated:

Java 15 中的新功能

银狐
Java 15 徽标

 

Java 15于 2020 年 9 月 15 日正式发布,请在此处下载 Java 15

Java 15 特性。

Java 15 开发人员功能。

密封类型(预览)、记录(第二预览)、模式匹配(第二预览)、隐藏类、文本块或多行(标准)、外部内存访问 API(第二孵化器)。

1. JEP 339:爱德华兹曲线数字签名算法 (EdDSA)

与密码学相关的东西,Java 15 使用爱德华兹曲线数字签名算法 (EdDSA)实现了一个额外的数字签名方案,如RFC 8032 所述。EdDSA 签名方案因其比其他签名方案提高了安全性和性能(更快)而广受欢迎,它也是TLS 1.3中允许的签名方案之一。

查看 Java 15签名算法

 

EdDSA 数字模式

 

示例代码。


package com.mkyong.java15.jep339;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

public class JEP339 {

    public static void main(String[] args)
        throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {

        KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
        KeyPair kp = kpg.generateKeyPair();

        byte[] msg = "abc".getBytes(StandardCharsets.UTF_8);

        Signature sig = Signature.getInstance("Ed25519");
        sig.initSign(kp.getPrivate());
        sig.update(msg);
        byte[] s = sig.sign();

        System.out.println(Base64.getEncoder().encodeToString(s));

    }
}

进一步阅读

 

2. JEP 360:密封类(预览)

这JEP推出几个新的关键字,sealednon-sealpermits支持密封的类和接口。密封的类和接口限制了谁可以是子类型。

2.1 下面的sealed接口允许三个指定的子类来实现它。


public sealed interface Command
    permits LoginCommand, LogoutCommand, PluginCommand{
    //...
}

2.2 对于不允许的类,它会抛出编译时错误:


public final class UnknownCommand implements Command {
  //...
}

class is not allowed to extend sealed class: Command

2.3 密封类必须有子类并且每个允许的子类必须选择一个修饰符(密封、非密封、最终)来描述它如何继续由其超类发起的密封

final


  // close, dun extends me
  public final class LoginCommand implements Command{
  }

sealed


  // another sealed class
  // sealed class must have subclasses
  public sealed class LogoutCommand implements Command
        permits LogoutAndDeleteCachedCommand {
  }

  // Sealed this class again if you want
  public final class LogoutAndDeleteCachedCommand extends LogoutCommand {
  }

non-sealed


  // open...up to you to play this
  // Create custom plugin by extending this class
  public non-sealed class PluginCommand implements Command {
  }

注意
您注意到关键字了non-sealed吗?我认为这是 Java 中的第一个连字符关键字。但是,这是一个预览功能;关键字可能会在将来的版本中更改。

2.4 这个密封类或允许的子类和模式匹配


  switch (command) {
      case LoginCommand:      // login
      case LogoutCommand:     // logout
      case PluginCommand:     // custom plugin
      // no default needed, only permits 3 sub-classes
  }

PS 密封类在 Java 16,JEP 397 中有第二个预览。

进一步阅读

3. JEP 371:隐藏类

3.1 此 JEP 引入了无法发现且生命周期有限(寿命较短)的隐藏类,这对在运行时动态生成类的开发人员有利。现在我们可以使用这个新的Lookup::defineHiddenClass API 从字节创建一个隐藏的类或接口。

3.2 用于defineHiddenClass从 Base64 编码类创建隐藏类并lookup手动启动静态方法的示例代码。

LookupProxyTest.java

package com.mkyong.java15.jep371;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Base64;

public class LookupProxyTest {

    //Here is the Base64 encoded class.
    /*
    package com.mkyong.java15.jep371;

    public class LookUpProxy{

        public static Integer lookup() {
            return 1;
        }
    }*/
    static final String CLASS_IN_BASE64 =
            "yv66vgAAADcAFQoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKV" +
            "YBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAGbG9va3VwAQAVKClM" +
            "amF2YS9sYW5nL0ludGVnZXI7AQAKU291cmNlRmlsZQEAEExvb2tVcF" +
            "Byb3h5LmphdmEMAAUABgcAEgwAEwAUAQAkY29tL21reW9uZy9qYXZh" +
            "MTUvamVwMzcxL0xvb2tVcFByb3h5AQAQamF2YS9sYW5nL09iamVjdA" +
            "EAEWphdmEvbGFuZy9JbnRlZ2VyAQAHdmFsdWVPZgEAFihJKUxqYXZh" +
            "L2xhbmcvSW50ZWdlcjsAIQADAAQAAAAAAAIAAQAFAAYAAQAHAAAAHQ" +
            "ABAAEAAAAFKrcAAbEAAAABAAgAAAAGAAEAAAADAAkACQAKAAEABwAA" +
            "AB0AAQAAAAAABQS4AAKwAAAAAQAIAAAABgABAAAABgABAAsAAAACAAw=";

    public static void main(String[] args) throws Throwable {

        //byte[] array = Files.readAllBytes(
        //      Paths.get("/home/mkyong/test/LookUpProxy.class"));
        //String s = Base64.getEncoder().encodeToString(array);
        //System.out.println(s);

        testHiddenClass();

    }

    // create a hidden class and run its static method
    public static void testHiddenClass() throws Throwable {

        byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);

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

        // output: com.mkyong.java15.jep371.LookUpProxy/0x0000000800b94440
        System.out.println(proxy.getName());

        MethodHandle mh = MethodHandles.lookup().findStatic(proxy,
                "lookup",
                MethodType.methodType(Integer.class));

        Integer status = (Integer) mh.invokeExact();
        System.out.println(status);

    }

}

输出

终端

com.mkyong.java15.jep371.LookUpProxy/0x0000000800b94440
1

3.3 此 JEP 还弃用了Unsafe.defineAnonymousClassAPI,并将其标记为将来删除。请不要再使用此 API。


  byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);

  Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
  theUnsafe.setAccessible(true);
  sun.misc.Unsafe unsafe = (sun.misc.Unsafe)theUnsafe.get(null);

  // @Deprecated(since = "15", forRemoval = false)
  Class<?> proxy = unsafe.defineAnonymousClass(
          LookupProxyTest.class, classInBytes, null);

进一步阅读

4. JEP 372:移除 Nashorn JavaScript 引擎

  • Java 8 JEP 174引入了 Nashorn 作为 Rhino Javascript 引擎的替代品。
  • Java 11 JEP 335弃用了 Nashorn JavaScript 引擎和jjs工具。
  • 现在,Java 15jjs永久删除了 Nashorn JavaScript 引擎和工具。

此 JEP 还删除了以下两个模块:

  • jdk.scripting.nashorn– 包含jdk.nashorn.api.scriptingjdk.nashorn.api.tree包。
  • jdk.scripting.nashorn.shell – 包含 jjs 工具。

进一步阅读

5. JEP 373:重新实现旧版 DatagramSocket API

  • Java 13 JEP 353重新实现了旧的 Socket API——java.net.Socketjava.net.ServerSocket.
  • 这一次,Java 15 重新实现了遗留的 DatagramSocket API——java.net.DatagramSocketjava.net.MulticastSocket.

进一步阅读

6. JEP 374:禁用和弃用偏置锁定

默认情况下,此 JEP 禁用并弃用了偏向锁定。在 Java 15 之前,默认情况下始终启用偏向锁定,从而提高同步内容的性能。

旧的或遗留的 Java 应用程序使用同步集合 API,如HashtableVector,并且偏向锁定可能会提高性能。如今,较新的 Java 应用程序通常使用非同步集合HashMapArrayList,而偏向锁定的性能提升现在通常不太有用。

但是,对于 Java 15,我们仍然可以使用 启用偏向锁定-XX:+UseBiasedLocking,但它会提示 VM 警告已弃用的 API。

终端

# Java 15
$ java -XX:+UseBiasedLocking name

 OpenJDK 64-Bit Server VM warning: Option UseBiasedLocking was deprecated
in version 15.0 and will likely be removed in a future release.

进一步阅读

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

Java 14 JEP 305引入了模式匹配作为预览功能。此 JEP 是模式匹配的第二次预览,以获取更多反馈,API 没有变化。

一个典型instanceof-and-cast的检查对象的类型并转换它。


  private static void print(Object obj) {

      if (obj instanceof String) {        // instanceof

          String s = (String) obj;        // cast

          if ("java15".equalsIgnoreCase(s)) {
              System.out.println("Hello Java 15");
          }

      } else {
          System.out.println("not a string");
      }

  }

对于模式匹配,我们可以在一行中检查、转换和绑定。


  private static void printWithPatternMatching(Object obj) {

      // instanceof, cast and bind variable in one line.
      if (obj instanceof String s) {         

          if ("java15".equalsIgnoreCase(s)) {
              System.out.println("Hello Java 15");
          }

      } else {
          System.out.println("not a string");
      }

  }

PS 模式匹配是 Java 16, JEP 394 中的标准特性。

进一步阅读

8. JEP 377:ZGC:可扩展的低延迟垃圾收集器

Java 11 JEP 333引入了ZGC 垃圾收集器作为实验性功能。

  • 此 JEP 修复了一些错误,添加了一些功能和增强功能,现在支持 Linux/x86_64、Linux/aarch64、Windows 和 macOS 等主要平台。
  • 此 JEP 还将​​ Z 垃圾收集器从实验功能更改为产品功能。但是,默认垃圾收集器仍然是G1

下面的命令启用 ZGC 垃圾收集器。

终端

$ java -XX:+UseZGC className

进一步阅读

9. JEP 378:文本块

最后,多行字符串或文本块是 Java 15 中的永久特性。

历史:

示例代码。


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

  String java15 = """
                  <html>
                      <body>
                          <p>Hello, World</p>
                      </body>
                  </html>
                  """;

进一步阅读

10. JEP 379:Shenandoah:低暂停时间垃圾收集器

Java 12 JEP 189引入了Shenandoah 垃圾收集器作为实验性功能,现在成为 Java 15 中的产品功能。

在 Java 15 之前,我们需要-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC启用 Shenandoah GC。

终端

java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

在 Java 15 中,我们只需要-XX:+UseShenandoahGC启用 Shenandoah GC。

终端

java -XX:+UseShenandoahGC

但是,官方的OpenJDK 15版本不包括 Shenandoah GC(就像 Java 12 中发生的那样)。阅读这个故事 –并非所有 OpenJDK 12 版本都包含 Shenandoah:这就是为什么.

终端

$ java -XX:+UseShenandoahGC ClassName

  Error occurred during initialization of VM
  Option -XX:+UseShenandoahGC not supported

$ java -version

  openjdk version "15" 2020-09-15
  OpenJDK Runtime Environment (build 15+36-1562)
  OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)

要尝试 Shenandoah GC,我们需要其他 JDK 构建版本,例如AdoptOpenJDK

终端

$ java -XX:+UseShenandoahGC ClassName

$ java -version

  openjdk version "15" 2020-09-15
  OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
  OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)

PS 默认垃圾收集器仍然是G1

进一步阅读

11. JEP 381:删除 Solaris 和 SPARC 端口

Java 14 JEP 362弃用了 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口,现在它在 Java 15 中被正式删除。

进一步阅读

12. JEP 383:外部内存访问 API(第二孵化器)

Java 14 JEP 370引入了一个新的外部内存访问 API 作为孵化器模块。此 JEP 对 API 进行了一些更改,它仍将位于孵化器模块中。

示例代码

HelloForeignMemory.java

import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;

import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;

public class HelloForeignMemory {

    public static void main(String[] args) {

        VarHandle intHandle = MemoryHandles.varHandle(
            int.class, ByteOrder.nativeOrder());

        try (MemorySegment segment = MemorySegment.allocateNative(1024)) {

            MemoryAddress base = segment.baseAddress();

            // print memory address
            System.out.println(base);                 

            // set value 999 into the foreign memory
            intHandle.set(base, 999);                 

            // get the value from foreign memory
            System.out.println(intHandle.get(base));  

        }

    }

}

我们需要添加--add-modules jdk.incubator.foreign以启用孵化器模块jdk.incubator.foreign

终端

$ javac --add-modules jdk.incubator.foreign HelloForeignMemory.java

warning: using incubating module(s): jdk.incubator.foreign
1 warning


$ java --add-modules jdk.incubator.foreign HelloForeignMemory

WARNING: Using incubator modules: jdk.incubator.foreign
MemoryAddress{ region: MemorySegment{ id=0x27c908f5 limit: 1024 } offset=0x0 }
999

进一步阅读

13. JEP 384:记录(第二次预览)

Java 14 JEP 359引入了记录作为预览功能。此 JEP 通过支持密封类型、本地记录、记录注释和记录反射 API 等功能增强了记录。

13.1 记录和密封类型

Fruit.java

public sealed interface Fruit permits Apple, Orange {}

record Apple() implements Fruit{}
record Orange() implements Fruit{}

13.2 本地记录——在方法内部声明的记录。

下面的代码片段使用本地记录来提高流操作的可读性。


  List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {

      // Local record
      record MerchantSales(Merchant merchant, double sales) {
      }

      return merchants.stream()
              .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
              .sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
              .map(MerchantSales::merchant)
              .collect(toList());
  }

  private double computeSales(Merchant merchant, int month) {
      // some business logic to get the sales...
      return ThreadLocalRandom.current().nextDouble(100, 10000);
  }

13.3 记录注释


public record Game(@CustomAnno Rank rank) { ... }

13.4 反射API

java.lang.Class增加了两个公共方法:

  • RecordComponent[] getRecordComponents()
  • boolean isRecord()

PS 记录是 Java 16, JEP 395 中的标准特性。

进一步阅读

14. JEP 385:弃用 RMI 激活以进行删除

该 JEP 弃用了过时的RMI 激活机制。这不会影响 RMI 的其他部分。

进一步阅读

下载源代码

$ git clone https://github.com/mkyong/core-java

$ cd java-15

参考