If a Java class file is compiled with a higher supported version than is currently being run, you will get the ‘bad class file’, ‘class file has the wrong version XX.0, should be XX.0’ error message.
For example, if the .class file was compiled as a Java 1.8 class file on a Jenkins continuous integration node, but the JRE on the desktop where you want to run it only has Java 1.7, then you will get this message when you attempt to run it.
The good news is that this is a relatively simple issue to address.
Quick Resolution
The easiest way to resolve this issue is to simply move up the higher Java version expected. The error message indicates ‘should be XX.0’, and the table below shows which JVM to use.
But there are also times when for security reasons or enterprise standards, that you cannot upgrade your JVM just to accommodate a single class or library. In these cases, you can look for older versions of the library or if the project is open-source, you can investigate recompiling at a lower level as long as newer features and packages are not utilized.
Background
Here is a table of the JVM versions mapped to the corresponding major version:
JVM | Major Version (decimal) |
Java 1.4 | 48 |
Java 5 | 49 |
Java 6 | 50 |
Java 7 | 51 |
Java 8 | 52 |
Java 9 | 53 |
Java 10 | 54 |
Java 11 | 55 |
Java 12 | 56 |
Java 13 | 57 |
Java 14 | 58 |
This major version number is stored in the header of the .class file, at byte 7.
Determining major version
If you have a JDK (Java Development Kit) installed on your machine, then you can use ‘javap’ to look into the class file. Note that a JRE (runtime) will not have this utility available.
Linux:
$ javap -verbose MyClass | grep "major"
Windows:
> javap -verbose MyClass | findstr "major"
If you don’t have access to javap, you can also using a console based utility or GUI hex editor to look at the value of the byte at position 7 in the file. Using Linux, this is easily done using the ‘od’ dump standard utility.
$ od --format=d1 MyClass.class -j 7 -N 1
0000007 51
0000011
Compiling to lower versions
Even if you are using a newer JDK, you can still compile code compatible with older JVM specifications by using the ‘-target’ parameter. If you target a lower JVM, it is important that no features/packages from the newer specification are used.
For example, even if the ‘javac’ being used below is from a 1.8 JVM, it can still create 1.7 compatible class files if you use the parameters below.
$ javac -classpath . MyClass.java -source 1.7 -target 1.7
Compiling to lower versions using Maven
If a project uses Maven, then the way to specify the JVM compatibility is in the pom.xml ‘maven-compiler-plugin’ section.
<project> ... <build> ... <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins>
Alternatively, you can use the ‘maven.compiler.target’ in the <properties> element.
REFERENCES
https://stackoverflow.com/questions/1096148/how-to-check-the-jdk-version-used-to-compile-a-class-file (javap and JVM to major version map)
https://superuser.com/questions/706101/how-to-print-first-10-bytes-in-hexadecimal-of-a-file
https://en.wikipedia.org/wiki/Java_class_file#General_layout
http://www.draconianoverlord.com/2014/04/01/jdk-compatibility.html (even using target, Java 1.8 only classes can sneak in)
https://www.mkyong.com/maven/how-to-tell-maven-to-use-java-8/