Java 17 New Features
September 7, 2021

Java 17 New Features

Java Updates

Java 17 is just around the corner, scheduled for September 14th, with releases from various vendors being released on the day or the days following it. What makes Java 17 special is of course that both Oracle and the OpenJDK community have decided that this will be a long-term support release, like that of Java 11, and Java 8 before it.

Since the introduction of the rapid release cadence starting with Java 10, many vendors besides Oracle have stepped up to produce production ready binaries with various levels of support, including Amazon, Azul, BellSoft, Microsoft, SAP, and Eclipse Adoptium (previously AdoptOpenJDK).

So, what’s in store for the ones waiting to jump on the Java 17 train? Continue reading to learn about:

What's In? Java 17 New Features

macOS on AArch64 Support

One of prominent JVM features added in this version is the support for macOS on AArch64architecture with JEP 391, thus adding support for the new line of CPUs (M1) Apple released with their computers last year. For people running on these platforms, this is not exactly news, as some vendors have already released versions of the JDK supporting this architecture, even backporting the support all the way back to Java 8. Nevertheless, the official stamp of approval is still important for the future maintenance and support for the platform. For comparison, support for Linux/AArch64 platform was added in Java 9 and for Windows/AArch64 in Java 16.

Sealed Classes

The Sealed Classes feature has finished its preview phase and is now a standard part of the language and platform as of Java 17, as defined in JEP 409. Sealed Classes allows a developer to explicitly declare the permitted sub-types of a type, thus preventing others from unintendedly extending or implementing it.

For a closer look at what this feature entails, see the post we did about the feature when it entered its first preview phase for Java 15. Since then, some minor updates to the specification were made, including improvements to the narrowing reference conversion, allowing the compiler to produce a compile-time error if you try to cast a sealed type to a non-permitted subtype. 

Other New Features

Java 17 also brings a new rendering pipeline (JEP 382) for AWT/Swing applications running on macOS, utilizing Apple’s Metal API instead of OpenGL, as well as new API and enhancements for generating random numbers (JEP 356).

What's Out? Java 17 Deprecations, Removals, and Restrictions

Java 17 also brings several deprecations, removals, and added restrictions.

Encapsulation of JDK Internals

One removal is the completion of the encapsulation of JDK Internals (JEP 403). This was first introduced in Java 9 and would give runtime warnings if a user tried to use reflection or like to bypass the normal restrictions for using otherwise internal APIs. A command-line argument was also added to control this behavior.

Since Java 9, various APIs have been introduced to provide a standardized way of doing the most common things people would use these internal APIs for. In Java 16 the default was changed from giving warning to denying access by throwing exception, but still preserving the command-line argument to change the behavior.

Now with Java 17, the command-line argument is removed and thus also the ability to disable this restriction, meaning all unauthorized access to those internal APIs are now strongly encapsulated.

Always-Strict Floating-Point Semantics

Another “removal” is the restoration of Always-Strict Floating-Point Semantics (JEP 306). Java 1.2 introduced changes to the default floating-point semantics in Java, allowing in essence for the JVM to trade a tiny bit of floating-point precision for performance. For methods and classes where the strict semantics needed to be applied, a strictfp keyword was introduced. Since then, new instruction sets have been added to CPUs, causing strict floating-point semantics to be operate without undue overhead, so the motivation to have a default and a strict semantic is no longer there.

Java 17 removes the previous default semantic, and all floating-point operations are now done as strict. The keyword strictfp is still there but has no effect and produces a compile-time warning.

Ahead-of-Time (AOT) Compilation

Java 9 introduced ahead-of-time (AOT) compilation as an experimental feature using the Graal compiler, a JIT compiler written in Java. Java 10 made the Graal compiler useable as the JIT compiler in the OpenJDK using the added JVMCI interface. Since then, the Graal compiler has gone through massive improvements and is now also its own JVM in the form of GraalVM. The benefits of having it part of OpenJDK have diminished and with JEP 410, it has been removed.

RMI Activation

RMI Activation has been removed with JEP 407, after having been made optional in Java 8 and finally deprecated and marked for removal in Java 15. RMI Activation enabled a way to activate distributed objects on-demand resources via RMI, but saw little use, and better alternatives exists today. The rest of RMI is untouched by the removal of the Activation part.

Applet API

The Applet API has finally been marked for removal with JEP 398, it was previously deprecated in Java 9. The Applet API provided a way to embed Java AWT/Swing controls into a web page in the browser, but no modern browser today supports this, so Applets has essentially been irrelevant for the past decade or more.

Security Manager

The biggest deprecation is without a doubt that of the Security Manager (JEP 411). Security Manager has been around since Java 1.0 and was generally intended to limit what Java could do on the local machine, such as limit access to files, network, etc., or to try to sandbox untrusted code, by disallowing use of reflection, and certain APIs.

The deprecation of the Security Manager started in Java 12, where a command-line argument was added that disallow the use of it, and from Java 18 forward, that command-line argument will default to disallowing setting a Security Manager at runtime. The change in Java 17 means that a runtime warning will be produced by the JVM when trying to set a Security Manager, either from command line or dynamically at runtime.

Incubator and Preview Features

Many were wondering if Java 17 would have any incubator and preview features, seeing as it was promoted to a long-term supported version, and potentially supporting a feature that changed or didn’t make the cut for a long time seemed unwise. But here we are, and Java 17 has two incubator modules and one preview language feature!

Vector API

The Vector API (JEP 414) is going through its second incubator phase. This API enables developers to express vector computation that the JIT compiler can then compile to the appropriate vector instructions supported by the CPU architecture the JVM is running on (for instance utilizing the SSE and AVX instruction sets).

Previously, a developer would either have to rely on scalar operations or use/develop platform-specific native libraries. Implementing the Vector API in Java also enables a graceful fallback for things which the current architecture doesn’t have the necessary instructions for, and instead must fall back to be computed in a different way.

While not part of this JEP, the standardization of the Vector API also enables the classes in the JDK to utilize it. Methods such as Arrays.mismatch, which today has an intrinsic vectorized implementation for some platforms, could be rewritten to have it all run in Java instead, thus removing the need to write and maintain multiple platform-specific implementations inside the JVM.

Foreign Function & Memory API

Another incubator module is the Foreign Function & Memory API (JEP 412), which is technically a merger and evolution of two previous incubator modules from Java 16: the Foreign Linker API (JEP 389) and Foreign-Memory Access API (JEP 393). These two combined allows for a way to access native code and memory, using statically-typed code written in Java, with the goal of being able to replace the use of JNI in such situations.

We took a look at the Memory API when it was first introduces as an incubator module in Java 14 in this post. Since then, improvements have been made to the API, including support for shared regions and a better separation between the MemorySegment and MemoryAddress interfaces. Likewise, we also took a quick peek at the Linker API when it was first introduced as an incubator module, which was mentioned in our post rounding up of new features in Java 16.

Pattern Matching for Switch

The final language preview feature in Java 17 is the inclusion of Pattern Matching for switch (JEP 406). This language feature extends the switch expressions and statement to also be able to switch based on type, similar to the syntax introduced by Pattern Matching for instanceof (JEP 394), that was standardized in Java 16.

Previously if you wanted to do different things based on the dynamic type of an object, you would have make an if-else if chain with instanceof checks, like:

String type(Object o) {
  if (o instanceof List) {
    return "A List of things.";
  }
  else if (o instanceof Map) {
    return "A Map! It has keys and values.";
  }
  else if (o instanceof String) {
    return "This is a string.";
  }
  else {
    return "This is something else.";
  }
}

Using a combination of the switch expression and the new pattern matching for switch, it can be simplified to something like:

String type(Object o) {
  return switch (o) {
    case List l -> "A List of things.";
    case Map m -> "A Map! It has keys and values.";
    case String s -> "This is a string.";
    default -> "This is something else.";
  };
}

As you may have noticed, there is a variable declaration as part of the check as well, which like in Pattern Matching for instanceof means that the object has been type checked, cast, and is accessible from that variable now within its scope.

This preview feature is yet another step in the direction of pattern matching. The next is just around the corner, adding the ability to deconstruct records and arrays.

Should You Upgrade to Java 17?

Short answer: Yes.

Long answer: Yes, you should always upgrade to the latest version, but perhaps not on day 1. Your application and the libraries you’re using might not have been updated yet with support for Java 17, so you might have to wait a while until that happens. If you’re stuck on an LTS version, like Java 8 or Java 11, there are plenty of features, both in the language and in the JVM itself, that warrants an upgrade to Java 17, and with it being a long-term support release, there are good chances your production environment eventually will be updated to that version as well.

If you’re starting a new project, or still in the middle of getting a project ready, moving to Java 17 sooner rather than later is probably the best option, as it lowers the migration costs. It also allows the developers on the project to use all the latest features, while on the ops side, to take advantage of all the improvements that has happened there the last many years, including improved support for running in containers, and new low-latency garbage collector implementations.

Final Thoughts

I was initially a bit hesitant about the new release cadence of Java, but the number of features and improvements that has been happening over the last 4 years is staggering. I’m still a bit on the fence about having incubator and preview features in a version that is known to be promoted to long-term support, but on one hand I also see the validity in the argument that innovation shouldn’t be put on pause just because a version gets promoted. But only the future will tell how that is going to turn out.

Interested in learning more? Join my webinar on September 14th. Along with my colleagues Rod Cope, Perforce CTO and John Saboe, OpenLogic Enterprise Architect — we pull back the curtain on Java 17’s new features, updates, and what it all means for enterprises using Java.

Register Now