Java

Mastering Java 17: New Features and Best Practices

What is Java 17?

Java 17, released on September 15, 2021, is a Long-term Support(LTS) release by Oracle, and this implies that the release will receive security and bug fixing until September 2026. Java 17 is the strong foundation you can rely on to run your most critical enterprise applications. If you are upgrading your code to Java 8 or Java 11, or creating brand-new projects, Java 17 provides you with new tools, which simplify, make safer and more efficient coding. This developer cheatsheet, on the other hand, will provide you with a highlight of Java 17, such as:

  • Sealed classes
  • Pattern Matching
  • Better Random Number Generation
  • Examples and Best Practices

So that you can integrate Java 17 into your work without any issues. So, go ahead and find out how these features can make your development faster and your apps work better.

Top 5 New Features in Java 17

Sealed Classes – Put Limits on Who Can Extend Your Classes

What it does: Sealed classes allow you to control to the finest level which classes may extend (inherit) your class. It is like making your class hierarchy a club where only members are allowed.

The usefulness of it:

  • It stops undesirable extensions of your classes
  • Makes your code easier to predict and safer
  • Ideal to display fixed sets of values (such as shapes, payment methods, etc).

How to use it:

public abstract sealed class Shape permits Circle, Rectangle {
    public abstract double getArea();
}

public final class Circle extends Shape {
    private final double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public final class Rectangle extends Shape {
    private final double length, width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override
    public double getArea() {
        return length * width;
    }
}

Key points:

  • Make use of `sealed` and `permits` to establish strict hierarchies.
  • Merge pattern matching for safer type handling.

When to Use:

  • When there is a fixed list of related classes (such as the various types of vehicles, modes of payment or user roles)
  • When you do not want anybody to expand your class inadvertently.

Switch Pattern Matching – The Intelligent Switch Statements

Note: In Java 17, it is a preview feature and requires the `–enable-preview` flag to compile and run. Should not be used in production since it can change in future releases.

What it does: Switch statements can now check types and retrieve data in a single operation, rather than simply checking values. No more casting!

The usefulness of it:

  • Fewer lines of code
  • Fewer casting bugs
  • Easier to read and much more expressive

How to use it:

public static double getPerimeter(Shape shape) {
    return switch (shape) {
        case Rectangle r -> 2 * r.length() + 2 * r.width();
        case Circle c -> 2 * c.radius() * Math.PI;
        default -> throw new IllegalArgumentException("Unrecognized shape");
    };
}

Another example:

public sealed interface Payment permits CreditCard, PayPal {
    String process();
}

public final class CreditCard implements Payment {
    private final String cardNumber;
    public CreditCard(String cardNumber) { this.cardNumber = cardNumber; }
    public String process() { return "Processed card: " + cardNumber; }
}

public final class PayPal implements Payment {
    private final String email;
    public PayPal(String email) { this.email = email; }
    public String process() { return "Processed PayPal: " + email; }
}

public static String processPayment(Payment payment) {
    return switch (payment) {
        case CreditCard c -> c.process();
        case PayPal p -> p.process();
    };
}

Important note: This is a preview feature, and thus, you require special flags:

javac --enable-preview --release 17 MyClass.java
java --enable-preview MyClass

When to use:

  • When you require processing various objects
  • Together with sealed classes to have full type safety
  • When you require a cleaner and readable code

Better Random Number Generation

What it brings: Java 17 brings a newer, more flexible mechanism to generate random numbers with better performance, particularly in multi-threading environments.

The usefulness of it:

  • Better performance than the old random class
  • Larger algorithm set to select
  • More suitable when using multiple threads in the apps

Basic usage:

import java.util.random.RandomGenerator;

public class RandomExample {
    public static void main(String[] args) {
        RandomGenerator generator = RandomGenerator.getDefault();
        int number = generator.nextInt(100);
        System.out.println("Random number: " + number);
    }
}

For special algorithms:

RandomGenerator generator = RandomGenerator.of("L128X256MixRandom");
int number = generator.nextInt(100);
System.out.println("Random number: " + number);

For multi-threaded apps:

import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;

public class SplittableRandomExample {
    public static void main(String[] args) {
        RandomGenerator.SplittableGenerator source = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("L128X256MixRandom").create();
        source.splits(5).forEach(split -> System.out.println(split.nextInt(100)));
    }
}

When to use:

  • New projects should use the new Random class instead of the old one
  • When you require improved performance of multi-threaded apps
  • When you desire to have greater control over random number generation

Foreign Function & Memory API (Experimental)

What it does: Enables Java to respond to the native code (such as C/C++ libraries) without the complicated JNI (Java Native Interface).

The usefulness of it:

  • Easy to call native code as compared to JNI
  • Better performance
  • Better memory management

Note: This feature is still in the experimental phase and it may change. Set the flag set to —add-modules jdk.incubator.foreign. Examples are complex; refer to [Oracle JDK 17 Docs] for details.

When to use:

  • When you have to call native libraries
  • In case of critical performance and where you need direct memory control
  • It is only at experimental/research projects (not yet in production)

Other Helpful Features

Easy Hex Formatting

How it works: Easy conversion of a Hexadecimal string to a byte array and vice versa.

import java.util.HexFormat;

public class HexFormatExample {
    public static void main(String[] args) {
        HexFormat hexFormat = HexFormat.of().withUpperCase();
        String hexString = hexFormat.toHexString(new byte[]{10, 20, 30});
        System.out.println(hexString); // Outputs: 0A141E
    }
}

Better Time Handling

What it does: The new InstantSource interface is easier to use to get the current time, which is especially useful during testing.

import java.time.InstantSource;
import java.time.Instant;

public class InstantSourceExample {
    public static void main(String[] args) {
        InstantSource instantSource = InstantSource.system();
        Instant now = instantSource.instant();
        System.out.println("Current time: " + now);
    }
}

Other Critical Improvement

FeatureWhat It DoesWhy You Care
macOS ARM SupportJava now works on Apple M1/M2 chipsBetter performance on modern Macs
Better macOS GraphicsNew rendering system using Apple MetalFaster graphics on macOS
Console Charset APIBetter handling of text encodingFewer character display issues
Security EnhancementsBetter protection against malicious codeSafer applications

Best Practices for Java 17

Sealed Classes:

  • Apply them in case you have a definite collection of associated classes.
  • Put all the similar classes in one package.
  • Log the classes that can be extended.

Pattern Matching:

  • Good with Sealed classes: the compiler makes sure you deal with every case.
  • Takes care of preview features in production code.
  • Tests thoroughly.

Random Numbers:

  • In most cases, use RandonGenerator.getDefault().
  • Select specific algorithms if high performance is required.
  • In parallel processing, use splittable generators.

Complete Migration Guide: From Java 8/11 to Java 17

Moving to Java 17 is a challenging task to plan and execute properly to be compatible and use new features in Java 11 or Java 8. So here is how to make the migration as easy as possible without compromising the robustness and efficiency of your applications.

Phase 1: Compatibility check and environment setup

Maven:

  • Edit the `pom.xml` and put `maven.compiler.source` and `target` to `17`. 
  • Apply Maven Compiler Plugin 3.8.1+ and take into consideration Maven Toolchains when it comes to multi-version support.
   <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
        </plugins>
    </build>

Gradle: 

  • Select sourceCompatibility and targetCompatibility to be the JavaVersion.VERSION_17. 
  • In order to be fully compatible with Java17, upgrade Gradle Wrapper to 7.3+.
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

IDE Configuration: 

  • Install your IDE (e.g. IntelliJ, Eclipse) with which you are going to work with Java 17 SDK.
  • When trying pattern matching, enable the preview features. 
  • Modify the coding inspection rules in order to fit the new syntax.

Phase 2: Check Dependencies

Framework Compatibility:

  • The Spring Framework (v 5.3.21+) and Spring Boot (v 2.5.14+) have Java 17; Spring Boot (v 3.0+) requires Java 17.
  • The Hibernate 5.6+, Jackson 2.13+, Mockito 4.6+ are Java 17-compatible.

Examine APIs: 

To find deprecated APIs, run the jdeps tools(Security Manager, Applet API) (which were removed in Java 17)

jdeps --jdk-internals your-app.jar

Refresh Libraries: 

Check all the dependencies so that they are compatible with the help of some tools, such as the Maven Dependency Plugin or Dependency Insights in Gradle.

Phase 3: Migrate Code

Low-Risk Changes:

  • Make use of RandomGenerator rather than Random to be efficient.
RandomGenerator generator = RandomGenerator.getDefault();
int number = generator.nextInt(100);
  • Perform hexadecimal operations with the help of HexFormat.
  • Make new domain models by introducing sealed classes.

Medium-Risk Changes:

  • Use pattern matching on fixed old switch statements.
  • Replace class hierarchies to employ sealed classes to make them type safe.

High-Risk Changes:

  • Test foreign function API in offline components.
  • Modern security practices should take the place of the Security Manager.

Phase 4: Optimization of performance

Memory Management:

Java 17 has enhanced garbage collection algorithms to decrease pause times, as well as better allocation of short-lived objects.

Allow -XX: +UseG1GC to applications that have giant heaps.

Apply -XX: +UseStringDeduplication to applications that are intensive in the use of memory.

Observe the performance using -XX:+FlightRecorder and get more details.

Application-Specific Optimizations:

Web Applications: Take advantage of HTTP/2 to boost web services and enhance the performance of SSL/TLS, leveraging out-of-the-box cipher suites.

Data Processing: Employ new Monte Carlo random number generators and get better parallel streams.

Additional Tips:

Make sure to test extensively to detect the compatibility problems, particularly on deprecated APIs.

Find potential problems using such tools as jdeps or the Maven Dependency Plugin.

See the Spring Boot Migration Guide for specific advice related to the framework.

Why Java 17 is the Strategic Choice?

Java 17 is a developer delight as it brings both more performance and better developer features, and long-term support. This is why it is a must-adopt for modern Java applications.

Performance Boosts

  • Quicker application start up particularly in containerized applications such as Docker, which makes microservices more reactive.
  • Better memory utilization with deduplication and allocation patterns of the strings, especially text-intensive applications.
    • Faster JIT compilation and vectorization to tasks that are CPU-bound, such as this one of matrix multiply:
  public static double[] multiplyMatrix(double[] a, double[] b, int n) {
      double[] result = new double[n * n];
      for (int i = 0; i < n; i++) {
          for (int j = 0; j < n; j++) {
              for (int k = 0; k < n; k++) {
                  result[i * n + j] += a[i * n + k] * b[k * n + j];
              }
          }
      }
      return result;
  }

Developer Productivity

  • With sealed classes, one does not have to wait long to debug, as unexpected inheritance is avoided.
  • Pattern matching removes the risks of ClassCastException and ensures code quality.
  • Better IDE support (e.g., IntelliJ, Eclipse) that has better autocomplete and error messages on Java 17 features.

Security Enhancements

  • Context-specific deserialization filters guard against remote code execution vulnerabilities, which are important to web apps that receive user input.
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter("com.example.MyClass;!*");
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter);
  • Better random number generation of cryptographic operations and better validation of certificates on secure connections.

Long-Term Support

Java 17 is a safe option when it comes to building enterprise solutions, as it will be supported until at least September 2026.

Conclusion

Java 17 LTS, new Modules such as sealed classes and pattern matching, and performance are enough reasons to adopt it in the world of modern Java development.

Java 17 is not only a technical update, but it is also an effective business decision that minimizes costs, limits risks, and prepares your organization for long-term growth. With its immediate performance advantages and its long-term support, it is the obvious choice to use in enterprise Java applications.

esparkbiz

Harikrishna Kundariya, is a marketer, developer, IoT, Cloud & AWS savvy, co-founder, and Director of eSparkBiz, a Software Development Company. His 14+ years of experience enables him to provide digital solutions to new start-ups based on IoT and SaaS applications.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button