Main Tutorials

What is new in Java 15

Java 15 logo

Java 15 reached General Availability on 15 September 2020, download Java 15 here.

Java 15 features.

Java 15 developer features.

Sealed types (preview), records (second preview), pattern matching (second preview), hidden classes, text blocks or multi-lines (standard), foreign-memory access APIs (second incubator).

1. JEP 339: 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.

Review the Java 15 Signature Algorithms.

EdDSA digital schema

Example code.


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

    }
}

Further Reading

2. JEP 360: Sealed Classes (Preview)

This JEP introduced few new keywords, sealed, non-seal, permits to support sealed classes and interfaces. The sealed classes and interfaces restrict who can be a subtype.

2.1 The below sealed interface allowed three specified sub-classes to implement it.


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

2.2 For a not permitted class, it throws compile-time errors:


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

class is not allowed to extend sealed class: Command

2.3 The sealed class must have subclasses and Every permitted subclass must choose a modifier (sealed, non-seal, final) to describe how it continues the sealing initiated by its superclass

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 {
  }

Note
Did you notice the keyword non-sealed? I think this is the first hyphen keyword in Java. However, this is a preview feature; the keyword may change in a future release.

2.4 This sealed class or permitted-sub-classes and pattern matching.


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

P.S The sealed class has a second preview in Java 16, JEP 397.

Further Reading

3. JEP 371: Hidden Classes

3.1 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.

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

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

    }

}

Output

Terminal

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

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

Further Reading

4. JEP 372: 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.

Further Reading

5. JEP 373: Reimplement the Legacy DatagramSocket API

  • Java 13 JEP 353 reimplemented the legacy Socket APIs – java.net.Socket and java.net.ServerSocket.
  • This time, Java 15 reimplemented the legacy DatagramSocket APIs – java.net.DatagramSocket and java.net.MulticastSocket.

Further Reading

6. JEP 374: Disable and Deprecate Biased Locking

This JEP disable and deprecated the biased locking by default. Before Java 15, the biased locking is always enabled by default, giving performance gains for synchronized stuff.

The older or legacy Java application uses synchronize collections APIs like Hashtable and Vector, and the biased locking may giving performance gains. Nowadays, the newer Java application generally uses the non-synchronized collections HashMap and ArrayList, and the performance gains of biased locking are generally less useful now.

However, for Java 15, we still can enable the biased locking by using -XX:+UseBiasedLocking, but it will prompt VM warning for deprecated API.

Terminal

# 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.

Further Reading

7. JEP 375: 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");
      }

  }

P.S The pattern matching is a standard feature in Java 16, JEP 394.

Further Reading

8. JEP 377: ZGC: A Scalable Low-Latency Garbage Collector

Java 11 JEP 333 introduced the ZGC garbage collector as an experimental feature.

  • This JEP fixed some bugs, added some features and enhancements, and now supported major platforms like Linux/x86_64, Linux/aarch64, Windows, and macOS.
  • This JEP also changes the Z Garbage Collector from an experimental feature into a product feature. However, the default garbage collector remains G1.

The below command enables the ZGC garbage collector.

Terminal

$ java -XX:+UseZGC className

Further Reading

9. JEP 378: Text Blocks

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

History:

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

Further Reading

10. 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.

Terminal

java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

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

Terminal

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.

Terminal

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

Terminal

$ 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

11. JEP 381: 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.

Further Reading

12. JEP 383: 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

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

        }

    }

}

We need to add --add-modules jdk.incubator.foreign to enable the incubator modules jdk.incubator.foreign.

Terminal

$ 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

Further Reading

13. JEP 384: 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.

13.1 Records and Sealed Types

Fruit.java

public sealed interface Fruit permits Apple, Orange {}

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

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

13.3 Annotations on records


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

13.4 Reflection API

The java.lang.Class added two public methods:

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

P.S The record is the standard feature in Java 16, JEP 395.

Further Reading

14. JEP 385: Deprecate RMI Activation for Removal

This JEP deprecated the obsolete RMI Activation mechanism. This will not affect other parts of RMI.

Further Reading

Download Source Code

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

$ cd java-15

References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
mkold
1 year ago

good

Sekhar Kumar.
3 years ago

Java 15 nicely explained.