Skip to main content Link Menu Expand (external link) Document Search Copy Copied

A Comprehensive Guide to Upgrading from Java 8 to Java 11

Coding Pirate

Introduction

Brief overview of Java 8 and Java 11

Java 8, released in March 2014, introduced significant changes to the Java language, such as lambda expressions, the Stream API, and the new Date and Time API. These features made Java 8 a popular choice for developers and helped to modernize the language.

Java 11, released in September 2018, is the first long-term support (LTS) release since Java 8. It brings additional language features, performance improvements, and several deprecated or removed features. As an LTS release, Java 11 will receive updates and support for an extended period, making it an ideal choice for organizations and developers.

Importance of upgrading to Java 11

Upgrading to Java 11 ensures that you stay up-to-date with the latest language features, performance improvements, and security patches. It also helps to future-proof your applications, as new Java releases will be based on Java 11 and its successors. Additionally, upgrading can help you avoid potential compatibility issues that may arise when using outdated dependencies and libraries.

Objective

This article aims to provide a comprehensive guide for developers and organizations looking to upgrade their Java 8 applications to Java 11. We will cover the key differences between Java 8 and Java 11, the steps to prepare for the upgrade, and the best practices to ensure a smooth transition.

Example of a Java 8 Lambda expression:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, (a, b) -> a.compareToIgnoreCase(b));

Example of a Java 11 Lambda expression using ‘var’ keyword:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, (var a, var b) -> a.compareToIgnoreCase(b));

Example of a Java 11 ‘var’ keyword usage in local-variable type inference:

var names = new ArrayList<String>(); // Type inferred as ArrayList<String>
names.add("Alice");
names.add("Bob");
names.add("Charlie");

Key differences between Java 8 and Java 11

Language features and syntax improvements

Local-variable type inference (var keyword):

Java 11 introduces the ‘var’ keyword, which allows you to declare local variables without explicitly specifying their type. The type is inferred by the compiler based on the value assigned to the variable.

Java 8 code:

String message = "Hello, Java 8!";
List<String> names = new ArrayList<>();

Java 11 code with ‘var’:

var message = "Hello, Java 11!";
var names = new ArrayList<String>();

API improvements:

Java 11 brings improvements to existing APIs and introduces new ones, such as the methods added to the String class:

  • String::repeat(int): Repeat the string a specified number of times.
  • String::isBlank(): Check if the string is empty or contains only whitespace.
  • String::lines(): Returns a stream of lines extracted from the string, separated by line terminators.

Java 11 example:

var text = "Welcome to Java 11!\n";
System.out.println(text.repeat(3).strip());

JEP 286: Pattern Matching for instanceof

Java 11 simplifies the common coding pattern of testing if an object is an instance of a specific type and then casting it to that type. The pattern matching for ‘instanceof’ allows you to declare and assign a new local variable in a single statement.

Java 8 code:

Object obj = "Hello!";
if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.toLowerCase());
}

Java 11 code with pattern matching:

Object obj = "Hello!";
if (obj instanceof String str) {
    System.out.println(str.toLowerCase());
}

JEP 323: Local-Variable Syntax for Lambda Parameters

Java 11 allows the use of the ‘var’ keyword in lambda expressions, making it more consistent with local-variable type inference in other parts of the language.

Java 8 code:

Function<String, Integer> stringLength = (String s) -> s.length();

Java 11 code with ‘var’:

Function<String, Integer> stringLength = (var s) -> s.length();

Performance improvements

  1. JEP 328: Flight Recorder Java Flight Recorder (JFR) is a powerful profiling and event collection framework, which is now open-sourced and integrated into Java 11. It allows developers to monitor the JVM and Java applications with minimal performance overhead.

  2. JEP 315: Improve Aarch64 Intrinsics Java 11 enhances the performance of Aarch64 processors by optimizing the implementation of various APIs and adding new intrinsics for specific operations.

  3. JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) Java 11 introduces the Z Garbage Collector (ZGC), an experimental low-latency, scalable garbage collector that reduces application pause times and improves overall performance.

C. Removed features and deprecated APIs

  1. Applets and Java Web Start Java 11 removes support for Applets and Java Web Start, which means that you’ll need to find alternative technologies for deploying and running your Java applications.

  2. Removal of deprecated APIs and methods Java 11 removes various deprecated APIs and methods, making it essential to update your code to use newer alternatives.

  3. JEP 320: Remove the Java EE and CORBA Modules Java 11 removes the Java EE and CORBA modules, which were deprecated in Java 9. Applications relying on these modules will

Preparing for the Upgrade

A. Analyzing and understanding the current Java 8 codebase

Before starting the upgrade process, it’s essential to analyze and understand your current Java 8 codebase. This analysis will help you identify dependencies, third-party libraries, and the impact of deprecated features and APIs.

  1. Identifying dependencies and third-party libraries

Make a list of all the dependencies and third-party libraries used in your project. Check if these libraries are compatible with Java 11. If they’re not, search for suitable alternatives or newer versions. For example, if you’re using a third-party library like Apache Commons Lang 3.4, you might need to update it to a newer version that supports Java 11.

  1. Assessing the impact of deprecated features and APIs

Java 11 has removed some deprecated features and APIs from previous versions. Review your codebase to identify the usage of such features and APIs. For instance, the sun.misc.Unsafe class has been deprecated since Java 9, and using it may cause issues in Java 11. To find such usages, you can use the jdeps tool that comes with the JDK:

$ jdeps --jdk-internals path/to/your/classes

B. Setting up a testing environment

  1. Choosing the right IDE and build tools

Ensure that you’re using an IDE (Integrated Development Environment) and build tools that support Java 11. IntelliJ IDEA, Eclipse, and NetBeans have all updated their versions to support Java 11. Similarly, make sure your build tools (e.g., Maven or Gradle) are updated to their latest versions.

  1. Ensuring version control and backup

Before starting the upgrade process, create a separate branch in your version control system (e.g., Git) to isolate the changes. Also, take a backup of your codebase to avoid any accidental loss of data.

C. Handling migration challenges

  1. Resolving compatibility issues

In some cases, you might encounter compatibility issues when upgrading to Java 11. For example, you might be using a library that relies on a removed module, like javax.xml.bind. In such cases, you need to find a suitable replacement, or in some instances, you can include the removed module as a separate dependency:

<!-- Maven -->
<dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
  <version>2.3.0</version>
</dependency>

// Gradle
implementation 'javax.xml.bind:jaxb-api:2.3.0'
  1. Addressing security concerns

With the upgrade, some security features and mechanisms may have changed. Make sure you understand and address these changes in your application. For instance, the default algorithm for the KeyPairGenerator class may have changed, and you might need to specify the desired algorithm explicitly:

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

Performing the upgrade

A. Updating the Java Development Kit (JDK) 1. Downloading and installing Java 11 JDK - Visit the official Oracle JDK downloads page (https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) or OpenJDK (https://jdk.java.net/11/) to download the appropriate Java 11 JDK for your platform. - Follow the installation instructions for your platform (Windows, macOS, or Linux).

2. Configuring the environment variables
    - Update the JAVA_HOME environment variable to point to the Java 11 JDK installation directory.
    - Update your system's PATH variable to include the Java 11 JDK's "bin" directory.

B. Updating the codebase 1. Refactoring the code to utilize Java 11 features - Use the ‘var’ keyword for local-variable type inference:

// Java 8
List<String> names = new ArrayList<>();
for (String name : names) { ... }

// Java 11
var names = new ArrayList<String>();
for (var name : names) { ... }
    - Use JEP 286 pattern matching for instanceof:
// Java 8
if (obj instanceof String) {
    String str = (String) obj;
    ...
}

// Java 11
if (obj instanceof String str) {
    ...
}
2. Removing deprecated features and APIs
    - Replace deprecated methods or classes with their recommended alternatives.
    - For example, replace the deprecated 'Thread.stop()' with a more appropriate method for stopping threads.

3. Resolving compatibility issues
    - If you use third-party libraries, make sure they are compatible with Java 11.
    - Update any library or framework to the latest version that supports Java 11.

C. Updating build tools and configurations 1. Maven, Gradle, or Ant build tools - Update your Maven or Gradle build scripts to use the Java 11 JDK:

Maven:

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

Gradle:

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}
2. Integrating Java 11 with IDEs
    - Update your IDE to the latest version that supports Java 11 (e.g., IntelliJ IDEA, Eclipse, or NetBeans).
    - Configure the IDE to use the Java 11 JDK for your project. This usually involves updating the project's JDK settings or preferences.

Testing and validation

After upgrading your application to Java 11, it is crucial to thoroughly test and validate the changes. This section will guide you through the process of ensuring that your upgraded application works as expected and performs better than before.

A. Unit and integration testing

Once you’ve updated your codebase, it’s essential to run all your existing unit and integration tests to ensure that your application still behaves correctly. This will help you detect any new test failures and issues caused by the upgrade.

  1. Ensuring all tests pass with Java 11

    Run your unit and integration tests using your preferred testing framework, such as JUnit or TestNG. Make sure that all tests pass successfully. If you encounter any test failures, investigate and address the root cause.

    For example, to run JUnit tests in a Maven project, execute the following command:

     mvn test
    
  2. Addressing new test failures and issues

    If you encounter test failures or unexpected behavior, analyze the issues and update your code accordingly. For instance, if a test failure is due to a removed API, replace it with the appropriate alternative.

    Example of replacing the removed Thread.destroy() method:

     // Old code (Java 8)
     myThread.destroy();
    
     // New code (Java 11)
     myThread.interrupt();
    

B. Performance testing and benchmarking

After verifying the correctness of your application, it’s important to measure its performance and compare it against the previous version. This will help you validate the benefits of upgrading to Java 11 and identify areas for further optimization.

  1. Comparing Java 8 and Java 11 performance

    Use benchmarking tools like JMH (Java Microbenchmark Harness) to compare the performance of your application before and after the upgrade. Monitor key metrics like response times, throughput, and memory usage.

  2. Identifying areas for further optimization

    Analyze the benchmark results to identify any bottlenecks or areas where Java 11’s new features can improve performance. For example, you can optimize your code by taking advantage of the new ZGC garbage collector.

    To enable ZGC in Java 11, add the following JVM options:

     -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
    

C. Security and vulnerability testing

Upgrading to Java 11 can help you address potential security vulnerabilities in your application. It is important to ensure that your application is secure after the upgrade by conducting security testing and vulnerability scans.

  1. Addressing new security concerns

    Perform security tests using tools like OWASP ZAP or Find Security Bugs to identify potential vulnerabilities in your application. Address any new security concerns that arise due to the upgrade.

  2. Ensuring the application is secure after the upgrade

    After fixing any identified security issues, re-run the security tests to ensure that your application is secure. Continuously monitor and address any security vulnerabilities to maintain a secure application.

    ### VI. Deploying and monitoring the upgraded application

After successfully upgrading and thoroughly testing your application, it’s time to deploy and monitor it to ensure it’s performing well in a production environment. In this section, we will discuss deployment strategies, best practices, and monitoring tools to help you maintain your upgraded Java 11 application.

A. Deployment strategies and best practices

  1. Continuous Integration and Continuous Deployment (CI/CD): Implement a CI/CD pipeline to automate the build, test, and deployment process. Tools like Jenkins, GitLab CI/CD, and Travis CI can facilitate this process. Make sure to include your updated Java 11 application in the pipeline.
# Example of a .gitlab-ci.yml configuration for a Java 11 Maven project

image: maven:3.8.2-openjdk-11

build:
  script:
    - mvn compile
  artifacts:
    paths:
      - target/

test:
  script:
    - mvn test

deploy:
  script:
    - mvn deploy
  1. Docker and containerization: Use Docker to containerize your Java 11 application, ensuring consistent behavior across different environments. Create a Dockerfile with the appropriate configuration for your application.
# Example of a Dockerfile for a Java 11 application

FROM openjdk:11-jdk

COPY ./target/my-app-1.0-SNAPSHOT.jar /usr/src/myapp/

WORKDIR /usr/src/myapp

CMD ["java", "-jar", "my-app-1.0-SNAPSHOT.jar"]
  1. Cloud deployment: Deploy your application to cloud platforms like AWS, Google Cloud, or Azure. These platforms provide managed services for Java applications and can automatically handle scaling, updates, and monitoring.

B. Monitoring application performance and stability

  1. Java Flight Recorder (JFR): Java 11 introduced Java Flight Recorder (JFR), a powerful tool for collecting diagnostic and profiling data about a running Java application. Use JFR to monitor your application’s performance and identify potential bottlenecks or issues.
# Start a Java Flight Recorder recording for a running Java application

$ jcmd <PID> JFR.start filename=my_recording.jfr

# Stop the recording and analyze the data with JDK Mission Control

$ jcmd <PID> JFR.stop
  1. Logging and monitoring tools: Integrate logging and monitoring tools like Logstash, Elasticsearch, and Kibana (ELK Stack) or Prometheus and Grafana to monitor your application’s performance and gather insights on its behavior in production.

  2. Alerting and notifications: Set up alerting and notifications to be informed about any critical issues, performance degradations, or unexpected behavior in your application. Tools like PagerDuty or Opsgenie can help you manage these alerts effectively.

C. Addressing post-upgrade issues and bugs

Even after thorough testing, some issues might only manifest in production. It’s crucial to establish a process for tracking, resolving, and deploying fixes for these issues quickly.

  1. Issue tracking: Use issue tracking tools like Jira, GitHub Issues, or GitLab Issues to track and manage post-upgrade bugs and issues.

  2. Root cause analysis: Perform root cause analysis to identify the origin of issues and implement appropriate fixes. Be sure to document your findings for future reference.

  3. Rolling back: In case of critical issues that cannot be resolved immediately, have a rollback plan to revert to the previous Java 8 version while you address the problem.

By following these deployment and monitoring best practices, you can ensure the long-term success of your upgraded Java 11 application.

VII. Conclusion

In this blog post, we have covered the essential steps to upgrade your Java application from Java 8 to Java 11. We have highlighted the key differences between the two versions, discussed the preparation process, and provided guidance on performing the upgrade, testing, and deployment. With these steps, you can now take advantage of the performance improvements, new language features, and enhanced security that Java 11 has to offer.

Here are some code examples to showcase a few of the language features introduced in Java 11:

1. Local-variable type inference (var keyword):

Before (Java 8):

List<String> names = new ArrayList<>();

After (Java 11):

var names = new ArrayList<String>();

2. JEP 286: Pattern Matching for instanceof

Before (Java 8):

if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.toLowerCase());
}

After (Java 11):

if (obj instanceof String str) {
    System.out.println(str.toLowerCase());
}

3. JEP 323: Local-Variable Syntax for Lambda Parameters

Before (Java 8):

BinaryOperator<Integer> sum = (Integer x, Integer y) -> x + y;

After (Java 11):

BinaryOperator<Integer> sum = (var x, var y) -> x + y;

We encourage you to further explore the features and improvements introduced in Java 11, as it will greatly benefit your projects in terms of performance, maintainability, and security. Remember that upgrading to newer Java versions is essential to keep your applications up-to-date and ensure their long-term success.

Happy coding!