See also the updated article Switching Java Versions (Update).

Sometimes you’ll need to support more than one java version at a time. Especially if you use the JDK, you might want to use the specific java version in order to avoid to use accidently a new java API, that is later on not available at the runtime version.

Problem

Assume, you are using the maven compiler plugin with setting a source and a target option. Please consider the note, that I quoted from there:

Note: Merely setting the target option does not guarantee that your code actually runs on a JRE with the specified version. The pitfall is unintended usage of APIs that only exist in later JREs which would make your code fail at runtime with a linkage error. To avoid this issue, you can either configure the compiler’s boot classpath to match the target JRE or use the Animal Sniffer Maven Plugin to verify your code doesn’t use unintended APIs.

Let’s consider a very simple example. Java 8 introduced the java.time API, that is not available in Java 6. The following sample program makes use of this API:

import java.time.OffsetDateTime;

public class JavaTime {
    public static void main(String[] args) {
        System.out.println(OffsetDateTime.now());
    }
}

You can compile it with a Java 8 compiler for Java 6:

~$ javac -source 1.6 -target 1.6 JavaTime.java 
warning: [options] bootstrap class path not set in conjunction with -source 1.6
1 warning

You silently ignore the warning - however, this is exactly the problem, we will see, when we run the program in different Java versions. With Java 8, everything is fine:

~$ java JavaTime
2015-09-25T10:48:58.458+02:00

However, with Java 6, you’ll get a NoClassDefFoundError as Java 6 doesn’t know about any java.time.* classes:

~$ java JavaTime
Exception in thread "main" java.lang.NoClassDefFoundError: java/time/OffsetDateTime
    at JavaTime.main(JavaTime.java:6)
Caused by: java.lang.ClassNotFoundException: java.time.OffsetDateTime
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    ... 1 more

So, while the compile bytecode is compatible with Java 6, it still doesn’t run under Java 6, as an API has been used that is not available in Java 6.

Solution

That’s one reason why you might want to switch between java version easily. I’ve created a couple of bash aliases in my ~/.bashrc file as follows:

alias java6="export JAVA_HOME=$HOME/programs/java6; export PATH=\"\$JAVA_HOME/bin:\${PATH/\$HOME\/programs\/java?\/bin:/}\""
alias java7="export JAVA_HOME=$HOME/programs/java7; export PATH=\"\$JAVA_HOME/bin:\${PATH/\$HOME\/programs\/java?\/bin:/}\""
alias java8="export JAVA_HOME=$HOME/programs/java8; export PATH=\"\$JAVA_HOME/bin:\${PATH/\$HOME\/programs\/java?\/bin:/}\""

It assumes, that you have installed your different java versions under $HOME/programs/java{6,7,8}. I usually extract the java archives under $HOME/programs/ and set a symlink to the specific version:

~$ cd $HOME/programs
~/programs$ ls -l java*
lrwxrwxrwx 1 andreas andreas 11 Mär  7  2015 java6 -> jdk1.6.0_45
lrwxrwxrwx 1 andreas andreas 11 Sep 25 11:23 java7 -> jdk1.7.0_80
lrwxrwxrwx 1 andreas andreas 11 Sep 25 11:18 java8 -> jdk1.8.0_60
lrwxrwxrwx 1 andreas andreas  8 Aug 31  2014 java9 -> jdk1.9.0

Now, if you have the aliases available, you can easily switch between java versions without polluting your PATH environment variable:

~$java6
~$java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
~$java7
~$java -version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)
~$java8
~$java -version
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
~$echo $PATH
/home/andreas/programs/java8/bin:/home/andreas/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
~$echo $JAVA_HOME
/home/andreas/programs/java8

As you can see in the end, the PATH variable only contains the java8 version. By the way, $JAVA_HOME is also set accordingly.