Introduction

Let’s explore how to use multiple versions of Java in a Maven project By this, I mean seemingly compiling and running a project with different Java versions.

I will be using Java 17, 21 and 25 for this demo. Make sure you have them installed on your machine. You can use SDKMAN! to manage multiple Java versions easily. Additionally, ensure Maven is installed.

As always, there are multiple ways to achieve this. Here, I will demonstrate one approach using Maven Toolchains.

Configure Maven Toolchains

First, create a toolchains.xml file in your Maven configuration directory (~/.m2/ on Unix-based systems or %USERPROFILE%\.m2\ on Windows). This file will define the different Java versions you want to use.

<?xml version="1.0" encoding="UTF-8"?>
<toolchains>
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>17</version>
    </provides>
    <configuration>
      <jdkHome>/Users/vladflore/.sdkman/candidates/java/17.0.12-oracle</jdkHome>
    </configuration>
  </toolchain>

  <toolchain>
    <type>jdk</type>
    <provides>
      <version>21</version>
    </provides>
    <configuration>
      <jdkHome>/Users/vladflore/.sdkman/candidates/java/21.0.3-oracle</jdkHome>
    </configuration>
  </toolchain>

  <toolchain>
    <type>jdk</type>
    <provides>
      <version>25</version>
    </provides>
    <configuration>
      <jdkHome>/Users/vladflore/.sdkman/candidates/java/25.0.1-open</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

This configuration specifies three different JDK versions and their respective installation paths.

Set Up Maven Project

I will be using a new Maven project for this demonstration.

> mvn archetype:generate -DgroupId=tech.vladflore.app -DartifactId=multijava -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeGroupId=org.apache.maven.archetypes -DinteractiveMode=false && cd multijava

Add this code in the App.java file located in src/main/java/tech/vladflore/app/.

package tech.vladflore.app;

public class App {

    public static void main(String[] args) {
        System.out.println(Runtime.version());
    }
}

Now, let’s see how to compile this project with different Java versions using Maven Toolchains.

Update the pom.xml file by including the Maven Toolchains plugin.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-toolchains-plugin</artifactId>
  <version>3.2.0</version>
  <executions>
    <execution>
      <goals>
        <goal>toolchain</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <toolchains>
      <jdk>
        <version>${java.compiler.version}</version>
      </jdk>
    </toolchains>
  </configuration>
</plugin>

The java.compiler.version property will be set when running Maven commands.

Do not forget to add it to the properties section of your pom.xml.

<properties>
  <maven.compiler.release>${java.compiler.version}</maven.compiler.release>
</properties>

We use maven.compiler.release because it ensures both bytecode level and API compatibility.

Compiling with Different Java Versions

At this point, we can compile the project using different Java versions. We can check the Java version used during compilation by running javap on the compiled class file and inspecting the version.

> mvn clean compile -Djava.compiler.version=17
> cd target/classes
> javap -verbose tech.vladflore.app.App | grep major

This should output major version: 61, which corresponds to Java 17.

Let’s try with the other version as well.

> mvn clean compile -Djava.compiler.version=21
...
> cd target/classes
> javap -verbose tech.vladflore.app.App | grep major
major version: 65
> mvn clean compile -Djava.compiler.version=25
...
> cd target/classes
> javap -verbose tech.vladflore.app.App | grep major
major version: 69

So far so good! We have successfully compiled the project with different Java versions.

Running with Different Java Versions

If I were to run the project now, i.e. run the only Java class I have, I would get the same output regardless of the compilation version, as it would use the default Java version set in my environment.

> mvn clean compile -Djava.compiler.version=17 exec:java -Dexec.mainClass=tech.vladflore.app.App
> mvn clean compile -Djava.compiler.version=21 exec:java -Dexec.mainClass=tech.vladflore.app.App
> mvn clean compile -Djava.compiler.version=25 exec:java -Dexec.mainClass=tech.vladflore.app.App

They would all output the same Java version, i.e. 25.0.1+8-27, because that is the default Java version on my machine.

> java --version
openjdk 25.0.1 2025-10-21
...
> ls -l "$JAVA_HOME"
/Users/vladflore/.sdkman/candidates/java/current -> 25.0.1-open
> sdk current java
Using java version 25.0.1-open

How do we run the project with different Java versions then?

One way is to use the JAVA_HOME environment variable to point to the desired Java version before running the project.

We can do this either by exporting the JAVA_HOME variable in the shell or by letting sdkman handle it for us.

Another way is to use the exec-maven-plugin plugin.

Add this to your pom.xml:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>3.1.0</version>
  <configuration>
    <executable>${java.runtime.home}/bin/java</executable>
    <arguments>
      <argument>-cp</argument>
      <argument>${project.build.outputDirectory}</argument>
      <argument>${main.class}</argument>
    </arguments>
  </configuration>
</plugin>

The java.runtime.home and main.class properties will be set when running Maven commands.

> mvn clean compile -Djava.compiler.version=17 exec:exec -Djava.runtime.home=/Users/vladflore/.sdkman/candidates/java/17.0.12-oracle -Dmain.class=tech.vladflore.app.App

This compiles and runs the project with Java 17.

Let’s compile with 17 and run with 25.

> mvn clean compile -Djava.compiler.version=17 exec:exec -Djava.runtime.home=/Users/vladflore/.sdkman/candidates/java/25.0.1-open -Dmain.class=tech.vladflore.app.App

Let’s compile with 25 and run with 17.

> mvn clean compile -Djava.compiler.version=25 exec:exec -Djava.runtime.home=/Users/vladflore/.sdkman/candidates/java/17.0.12-oracle -Dmain.class=tech.vladflore.app.App
...
💥💥💥

We get the following error:

Error: LinkageError occurred while loading main class tech.vladflore.app.App
java.lang.UnsupportedClassVersionError: tech/vladflore/app/App has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 61.0
[ERROR] Command execution failed.

which is expected since we are trying to run a class compiled with a newer Java version on an older Java runtime.

Conclusion

In this post, we explored how to manage multiple Java versions in a Maven project using Maven Toolchains for compilation and the exec-maven-plugin for running the application with different Java runtimes. This approach allows developers to test and run their applications across various Java versions seamlessly.