Java 17 | What you need to know

Preface: What is a JEP?

JEP is short for JDK Enhancement Proposal. A JEP is a proposal for an enhancement to the Java language.

The features discussed here are resulting implementations from JEPs. It is worth mentioning that not all JEPs become part of the Java programming language.

For more information, check out What is a JEP in Java?

New features in Java 17

The following are development features supported in Java 17. These are the features you will care about most as a developer.

Sealed Classes (JEP 409)

Sealed interfaces limit what classes can implement them...

public sealed interface Animal permits Cow, Pig {
    void makeNoise();
}

Sealed classes limit what classes can extend them...

public sealed class Habitat permits Ocean, Land {
    ...
}

Sealed classes where introduced in Java 15 as a preview feature. They are now supported in Java 17.

The most common use case for sealed classes is implementing business rules. For example, only Book and Shirt can implement the Product interface for an e-commerce application.

Pattern matching for switch statements(JEP 406)

There are several improvements for pattern matching in switch statements available in Java 17. Please note that these are still preview features...

What's a preview feature in Java?

A preview feature is one that is fully implemented and available with the current version BUT may not be included in future versions.

The reasoning behind preview features is that developers can use and provide feedback for a given feature before it is more permanently adopted.

To use preview features, you must specify the --enable-preview flag when running your Java application.

public class Demo {
    public static void main(String[] args){
        System.out.println(printObject("hello"));
        System.out.println(printObject(100));
        System.out.println(printObject(3));
        System.out.println(printObject(null));
    }
    static String printObject(Object obj) {
        return switch (obj) {
            case String s -> "Object is string: " + s;
            case Integer i && i >= 100 -> "Object is a BIG integer: " + i;
            case Integer i -> "Object is a integer: " + i;
            case null -> "Object is null";
            default -> "Object is something else";
        };
    }
}

A few improvements to point out with this Java 17 (preview) switch statement....

  1. Pattern matching null values is much easier. Before this improvement, input to the switch statement obj would need to be null checked. Now you can simply include a case null.
  2. Refining patterns allows for some syntactic sugar. Notice how we can include two conditions in one case statement using the && operand. Before this improvement, you'd have to nest an if statement under a given case.
  3. if...else chaining now allows for use of arrow functions ->. Additionally, we can automatically cast values without using instanceof or explicit casting i.e. (String)obj;
  4. The break keyword is no longer required for a given case block.

Pseudo-Random Number Generators (JEP 356)

Generating (pseudo) random numbers is now easier. Java 17 makes it easier to swap out different pseudo random number generators (PSNG) within the application...

RandomGenerator xoShiro = RandomGeneratorFactory.of("Xoshiro256PlusPlus").create();
RandomGenerator lxm = RandomGeneratorFactory.of("L32X64MixRandom").create();
System.out.println(xoShiro.nextInt(10));
System.out.println(lxm.nextInt(10));

Java 17 also makes it easy to use streams with PSNG...

RandomGeneratorFactory.all()
        .map(fac -> fac.name())
        .sorted()
        .forEach(System.out::println);

Using the recommended RandomGeneratorFactory we can obtain a stream of all() the available PSNG algorithms, sort, and print them. The output looks like this...

L128X1024MixRandom
L128X128MixRandom
L128X256MixRandom
L32X64MixRandom
L64X1024MixRandom
L64X128MixRandom
L64X128StarStarRandom
L64X256MixRandom
Random
SecureRandom
SplittableRandom
Xoroshiro128PlusPlus
Xoshiro256PlusPlus

Foreign Function & Memory API (JEP 412)

The Foreign Function & Memory API is an incubator feature which aims to replace the JNI API.

What's an incubator feature in java?

Incubator feature are not fully matured but included in the current version. This gives developers the chance to leave valuable feedback before modules/APIs are finalized in future releases.

The foreign function and memory API will replace the inferior JNI programming convention.

What's JNI?

The Java Native Interface (JNI) is a native programming interface that essentially links Java code to native code or libraries written in other languages (C, C++, etc).

Using JNI has traditionally allowed Java applications to access memory outside of the JVM.

The main advantage of JNI is flexibility in triggering code from lower level libraries and performance benefits of avoiding garbage collection etc.

The main disadvantage of JNI is security and introducing memory issues.

While still in the incubator phase, the new Foreign Function and Memory API presents a standardized, more secure API for accessing both heap and native memory.

For example...

MemorySegment memorySegment = MemorySegment.allocateNative(200);

This allocates 200 bytes of native (off-heap) memory for use in the Java application.

Context-Specific Deserialization Filters (JEP 415)

Deserializing data in Java poses a security risk. Remember that deserializing is the process of reading in byte streams of data to construct input classes the programming language understands.

Without deserialization filters in place, untrusted data can be deserialized into malicious code that is then executed on the JVM.

Context-Specific deserialization filters address this security concern...

public class Demo {
    public static void main(String[] args) throws Exception{
        byte[] bytes = convertObjectToStream(new HashMap());
        InputStream inputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream s= new ObjectInputStream(inputStream);
        s.setObjectInputFilter(createFilter());
        s.readObject();
    }
   private static ObjectInputFilter createFilter() {
       return filterInfo -> {
           Class<?> clazz = filterInfo.serialClass();
           if (clazz != null) {
               return (HashMap.class.isAssignableFrom(clazz))
                       ? ObjectInputFilter.Status.REJECTED
                       : ObjectInputFilter.Status.ALLOWED;
           }
           return ObjectInputFilter.Status.UNDECIDED;
       };
   }

    private static byte[] convertObjectToStream(Object obj) {
        ByteArrayOutputStream boas = new ByteArrayOutputStream();
        try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
            ois.writeObject(obj);
            return boas.toByteArray();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        throw new RuntimeException();
    }
}

The example above throws a InvalidClassException because our createFilter() rejects HashMap class deserialization.

Please note that deserialization filters aren't new to Java 17. Rather, JEP 415 adds enhancements for working with both global and context specific filters...

ObjectInputFilter.Config.setSerialFilterFactory((f1, f2) -> ObjectInputFilter.merge(f2,f1));

Deprecated features in Java 17

Features are marked deprecated when better alternatives exist and / or feature may be removed in future versions.

Deprecate Applet API (JEP 398)

Applets were small Java programs launched from web browsers. Such applications were popularized in the early days of the web because they offered superior performance to JavaScript (especially for 3D rendering).

The advancements of JavaScript have since left the development of applets largely irrelevant. Most major browsers discontinued support for Applets in 2017. The Applet API has been deprecated since Java 9.

Deprecate Security Manager (JEP 411)

The Security Manager was essentially an object defining the security policy for an application. While used extensively with web based applets (also deprecated), the use cases for the Security Manager in today's world are few and far between.

Removed features in Java 17

Remove RMI Activation (JEP 407)

Originally intended to allow remote object activation for idle/crashed applications, this feature has been removed.

Remove AOT / JIT Compilers (JEP 410)

An experimental ahead-of-time (AOT) compiler was introduced in Java 9 with limited success. It was repackaged and introduced again in Java 10 as the just-in-time (JIT) compiler.

With little adoption and lack of enthusiasm/support from the community, this feature has been removed in Java 17.

A new release process...

Java 17 follows the new long term support (LTS) release cadence. A new LTS version of Java is released every 2 years instead of every 3 years now.

This means the next version of Java featuring LTS will be Java 21 (September 2023).

For more information on why LTS releases are so important, check out Which Version of Java Should You Use?

Java platform developers have also adopted a six month feature release model. This means experimental/preview features can be pushed to developers sooner to optimize feedback and exposure from the community.

This both improves the feedback cycle and how often new features are being released by Java team.

Conclusion

While this article doesn't cover every single feature / improvement, it includes the majority of things real world Java developers should care about when considering Java 17.

Your thoughts?

|

also worth mentioning is Vector API and strict floating point decimals...