What is new in Java 15
Java 15 reached General Availability on 15 September 2020, download Java 15 here.
Java 15 features.
- 1. JEP 339: Edwards-Curve Digital Signature Algorithm (EdDSA)
- 2. JEP 360: Sealed Classes (Preview)
- 3. JEP 371: Hidden Classes
- 4. JEP 372: Remove the Nashorn JavaScript Engine
- 5. JEP 373: Reimplement the Legacy DatagramSocket API
- 6. JEP 374: Disable and Deprecate Biased Locking
- 7. JEP 375: Pattern Matching for instanceof (Second Preview)
- 8. JEP 377: ZGC: A Scalable Low-Latency Garbage Collector
- 9. JEP 378: Text Blocks
- 10. JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector
- 11. JEP 381: Remove the Solaris and SPARC Ports
- 12. JEP 383: Foreign-Memory Access API (Second Incubator)
- 13. JEP 384: Records (Second Preview)
- 14. JEP 385: Deprecate RMI Activation for Removal
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.
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
- JEP 360: Sealed Classes (Preview)
- Why are we allowing a sealed class to be extended to a non-sealed class?
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.
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
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 thejdk.nashorn.api.scripting
andjdk.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
andjava.net.ServerSocket
. - This time, Java 15 reimplemented the legacy DatagramSocket APIs –
java.net.DatagramSocket
andjava.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.
# 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.
$ 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:
- 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>
""";
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.
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
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
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
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
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
- OpenJDK 15 Project
- Cloudflare – What’s new in TLS 1.3?
- Wikipedia – Edwards-Curve Digital Signature Algorithm (EdDSA)
- RFC 8032
- Pattern Matching for Java
- Biased Locking in HotSpot
- OpenJDK Wiki – ZCG
- Oracle – The Z Garbage Collector
- G1 garbage collector
- Shenandoah garbage collector
- JEP 11: Incubator Modules
- Java version history
good
Java 15 nicely explained.