Tuesday, June 21, 2022

Java_15_Features - ALL

 Table of Contents 

  • 15.1 Sealed Classes and Interfaces (Preview)
  • 15.2 Edwards-Curve Digital Signature Algorithm (EdDSA)
  • 15.3 Hidden Classes
  • 15.4 Remove the Nashorn JavaScript Engine
  • 15.5 Pattern Matching for instanceof (Second Preview)
  • 15.6 Records (Second Preview)
  • 15.7 JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector
  • 15.8 Remove the Solaris and SPARC Ports
  • 15.9 Foreign-Memory Access API (Second Incubator)
  • 15.10 Text Blocks
  • 15.1 Sealed Classes and Interfaces (Preview)

    Prior to Java 15, there was no restriction on classes or interfaces regarding which all classes can inherit them. A public interface was available to be implemented by any class, and any public class was available to be extended by any other class – unless declared final.

    Now with Java 15, a class or an interface can be declared sealed class or sealed interface using the modifier sealed. It is a preview feature in Java 15 through JEP 360.

    A sealed class or interface restricts which other classes or interfaces may extend or implement them. Conceptually, it is a more declarative way than access modifiers to restrict the use of a class or interface as a parent class or as a parent interface.

    The reserved keyword permits lists all the classes that can extend the sealed class directly. The listed subclasses can either be finalnon-sealed, or sealed.

    sealed class Account permits CurrentAccount, SavingAccount, LoanAccount { } final class CurrentAccount extends Account {} non-sealed class SavingAccount extends Account {} sealed class LoanAccount extends Account permits HomeloanAccount, AutoloanAccount {} final class HomeloanAccount extends LoanAccount{} final class AutoloanAccount extends LoanAccount{}

    15.2 Edwards-Curve Digital Signature Algorithm (EdDSA)

    Cryptography related stuff, Java 15 implements an additional digital signature scheme using the Edwards-Curve Digital Signature Algorithm (EdDSA) as described by RFC 8032. The EdDSA signature scheme is popular due to its improved security and performance (faster) over other signature schemes, and it is also one of the signatures schemes that are allowed in TLS 1.3.

    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)); } }
    15.3  Hidden Classes

    This JEP introduces hidden classes that are not discoverable and have a limited lifecycle (shorter live), good for developers that generate classes dynamically at runtime. And now we can use this new Lookup::defineHiddenClass API to create a hidden class or interface from bytes.

    Example code to use defineHiddenClass to create a hidden class from a Base64 encoded class, and launch the static lookup method manually.

    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. / 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 { 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(); 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); } }

    Output :

    com.java15.jep371.LookUpProxy/0x0000000800b94440 1

    This JEP also deprecated the Unsafe.defineAnonymousClass API and marked it for removal in the future. Please don’t use this API anymore.

    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);

    15.4 Remove the Nashorn JavaScript Engine

    • Java 8 JEP 174 introduced Nashorn as a replacement for the Rhino Javascript engine.
    • Java 11 JEP 335 deprecated the Nashorn JavaScript Engine and jjs tool.
    • Now, Java 15 removed the Nashorn JavaScript Engine and jjs tool permanently.

    This JEP also removed the below two modules:

    • jdk.scripting.nashorn – contains the jdk.nashorn.api.scripting and jdk.nashorn.api.tree packages.
    • jdk.scripting.nashorn.shell – contains the jjs tool.

    15.5 Pattern Matching for instanceof (Second Preview)

    Java 14 JEP 305 introduced Pattern Matching as a preview feature. This JEP is a second preview of the pattern matching to gain additional feedback, with no change to the API.

    A typical instanceof-and-cast to check the object’s type and cast it.

    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"); } }

    For pattern matching, we can check, cast, and bind in a single line.

    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"); } }

    15.6 Records (Second Preview)

    Java 14 JEP 359 introduced the records as a preview feature. This JEP enhanced the records with features like support sealed types, local records, annotation on records, and Reflection APIs for records.

    Records and Sealed Types

    public sealed interface Fruit permits Apple, Orange {} record Apple() implements Fruit{} record Orange() implements Fruit{}

    Local Records – records declare inside a method.

    The below code snippet use local record to improves the readability of the stream operations.

    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); }

    Annotations on records

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

    Reflection API

    The java.lang.Class added two public methods:

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

    15.7. JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector

    Java 12 JEP 189 introduced the Shenandoah garbage collector as an experimental feature, and now become a product feature in Java 15.

    Before Java 15, we need -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC to enable the Shenandoah GC.

    java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

    In Java 15, we only need -XX:+UseShenandoahGC to enable the Shenandoah GC.

    java -XX:+UseShenandoahGC

    However, the official OpenJDK 15 build didn’t include the Shenandoah GC (just like what happened in Java 12). Read this story – Not all OpenJDK 12 builds include Shenandoah: Here’s why.

    $ 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)

    To try Shenandoah GC, we need other JDK builds like 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)

    P.S The default garbage collector remains G1.

    Further Reading

    • JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector

    15.8. Remove the Solaris and SPARC Ports

    Java 14 JEP 362 deprecated the Solaris/SPARC, Solaris/x64, and Linux/SPARC ports and now it is officially removed in Java 15.

    15.9.  Foreign-Memory Access API (Second Incubator)

    Java 14 JEP 370 introduced a new Foreign-Memory Access API as an Incubator Modules. This JEP made some changes to the APIs, and it will be still in incubator modules.

    Example code:

    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)); } } }

    We need to add --add-modules jdk.incubator.foreign to enable the incubator modules 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

    15.10 Text Blocks

    Finally, the multi-line string or text blocks is a permanent feature in Java 15.


    • Java 12 JEP 326 Raw String Literals
    • Java 13 JEP 355: Text Blocks (Preview)
    • Java 14 JEP 368: Text Blocks (Second Preview)
    • Java 15, permanent feature.

    Example code.:

    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> """;

    Follow on LinkedIn

    You may also like

    Kubernetes AWS Java Coding Question
    Microservices Core Java Spring Boot
    Spring Framework Kafka Miscellaneous