Java 17, the latest Long-Term Support (LTS) release is out, and the newest major update is a sign of evolution. There aren't many new features - as a matter of fact, there is only one (if we count only stable additions). Sealed classes is the only new language feature that Java developers will have to memorize - Pattern Matching, Vector API, and Foreign Function & Memory API either have an "Incubator", “Second Incubator” or "Preview" status.
Tomasz Rębelski, ITMAGINATION’s Java Architect Competence Leader, has shared five of the most important changes to the language, along with a longer list of changes worth taking a look at.
First, let’s look at the only language novelty. Their use case is straightforward - they allow specifying which subclasses can extend or implement our superclass. Crucially, the new feature in the same form was ready for a preview in JDK 16.
There are some requirements for you to fulfill to use sealed classes, however:
1. The sealed class and its permitted subclasses must belong to the same module, and, if declared in an unnamed module, to the same package.
2. Every permitted subclass must directly extend the sealed class.
3. Every permitted subclass must use a modifier to describe how it propagates the sealing initiated by its superclass:
- A permitted subclass may be declared final to prevent its part of the class hierarchy from being extended further. (Record classes are implicitly declared final.)
- A permitted subclass may be declared sealed to allow its part of the hierarchy to be extended further than envisaged by its sealed superclass, but in a restricted fashion.
- A permitted subclass may be declared non-sealed so that its part of the hierarchy reverts to being open for extension by unknown subclasses. A sealed class cannot prevent its permitted subclasses from doing this. (The modifier non-sealed is the first hyphenated keyword proposed for Java.)
One of the goals of Project Jigsaw (which was completed in 2017 with JDK 9) was to
Improve the security and maintainability of Java SE Platform Implementations in general, and the JDK in particular;
Access to the newly implemented internals was limited, leveraging modules, but that was not enough. Back in 2017, the team has consciously decided to not encapsulate anything that had existed in Java 8. In JDK 16, however, maintainers went further by limiting access to all but critical APIs (such as sun.misc.Unsafe). It was still possible to relax the restriction by using a command line flag, --illegal-access=permit. That will not be possible going forward. Any use of this option will now have no effect other than issuing a warning.
What data do we often deserialize? Data that comes from untrusted, unverified, or unauthorized sources. This action was, is, and will be inherently dangerous - what we receive determines how our objects will look like. This issue had been addressed before - “deserializing filters” debuted in Java 9, though, as noted by the developers, “[t]his approach does not scale.”
This is why the team behind the language decided to take another look at the issue. The new member of the java.io package allows for easier, and safer, deserialization of data, thanks to the addition of a factory of dynamic filters that adapt to the context.
An important side-note: previously, Java provided no way of securing against deserialization attacks! For the first time in 20 years, language maintainers decided to harden the JVM, and provide some protection against these attacks.
First, we need to implement a class such as this, FilterInThread one
We can then use it in our code
java.util.Random is now augmented with new Pseudo-Random Number Generators (PRNG). The goals were clear - make them all easier to use interchangeably and eliminate code duplication in existing classes, among others.
Now there is a new interface to supply a consistent API for all existing and new PRNGs. This interface is called RandomGenerator. This interface is then extended by four other interfaces: SplittableRandomGenerator, JumpableRandomGenerator, LeapableRandomGenerator, and finally, ArbitrarilyJumpableRandomGenerator. To locate and construct instances of the new interface, there is a class called RandomGeneratorFactory.
The last novelty chosen by Tomasz Rębelski is an API that provides a way for Java programs to work with code and data from outside the Java runtime. It’s a part of Project Panama, which aims to connect the Java Virtual Machine with native code. The new API’s main use case is exactly that; one of the goals is specifically calling native libraries and processing native data “without the brittleness and danger of JNI [Java Native Interface].”
First, let’s look at how you would call a C library function radixsort to then use it to sort foud strings that start out as a Java array:
Then, let’s look at how you would go about storing an int value outside the JVM:
Finally, let’s look at finding and invoking another C function, strlen:
To summarize, the new release is not a groundbreaking one. Far from it. Most Java developers will in fact not even notice it. It is, however, a solid one, with many “silent” improvements to the platform, making it safer, and faster. It is also a release that is crucial to examine since it is the newest Long Term Support version - it will continue to receive Free Public Updates for as long as 2029, and will have Extended Support until September 2031, at the latest.
Finally, we want to acknowledge that writing something about each change in this release would make this post a book instead. We did, however, prepare a list of noteworthy changes, as put together by Tomasz Rębelski:
Like what you’re reading? Make sure to subscribe to our weekly newsletter!