X

Sundararajan's Weblog

  • Java
    July 28, 2006

Thou shall know the .class file version!

Guest Author

It is easy to have more than on JDK or JRE version(in particular JRE -- because of browser plugin downloads!) in the same machine. If you compile your Java source(s) with a JDK version and run the same with with an earlier JDK/JRE version, you could get an error that looks like this:


Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file

Now, how do I know what is the version of a .class file and what version of JDK I need to run the same?
The section 4.1 of VM spec. edition 2
explains the "header" structure of
every .class file:


ClassFile {

u4 magic;

u2 minor_version;

u2 major_version;

u2 constant_pool_count;

cp_info constant_pool[constant_pool_count-1];

u2 access_flags;

u2 this_class;

u2 super_class;

u2 interfaces_count;

u2 interfaces[interfaces_count];

u2 fields_count;

field_info fields[fields_count];

u2 methods_count;

method_info methods[methods_count];

u2 attributes_count;

attribute_info attributes[attributes_count];
}

The magic of a .class file is 0xCAFEBABE. The following class takes a class file as input and prints the major, minor version of
it and also prints minium JDK/JRE required to run the given class.

File: Version.java



import java.io.\*;
public class Version {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: java version <.class file>");
System.exit(1);
}
if (! new File(args[0]).exists()) {
System.err.println(args[0] + " does not exist!");
System.exit(2);
}
DataInputStream dis = new DataInputStream(
new FileInputStream(args[0]));
int magic = dis.readInt();
if (magic != 0xcafebabe) {
System.err.println(args[0] + " is not a .class file");
System.exit(3);
}
int minor = dis.readShort();
int major = dis.readShort();
System.out.println("class file version is " + major + "." + minor);
String version = null;
if (major < 48) {
version = "1.3.1";
} else if (major == 48) {
version = "1.4.2";
} else if (major == 49) {
version = "1.5";
} else if (major == 50) {
version = "6";
} else {
version = "7";
}
System.out.println("You need to use JDK " + version + " or above");
}
}

For example, after compiling the above class with JDK 1.5 and running it against itself prints the following:

java -cp . Version Version.class
class file version is 49.0
You need to use JDK 1.5 or above

Join the discussion

Comments ( 5 )
  • Vijay Tatkar Friday, July 28, 2006
    Isnt that "thou shalt" rather than "thou shall" :-)
  • A. Sundararajan Friday, July 28, 2006
    Another "Thou shall" .. er... "Thou shalt" :-) ??
  • Jesse Wilson Friday, July 28, 2006
    Great idea. Some enhancement ideas...
    - include support for scanning entire .jar files
    - future proof it should another version of Java come out after 7. For example, print the major version number.
  • Bharath R Friday, July 28, 2006
    Cool! Really useful.
  • Pete Monday, July 31, 2006

    But...but... Why create a specialised tool just for jar files? On Unix platforms at least, the "file" utility is designed for exactly this kind of thing.

    So for example by adding the following lines to the "magic numbers" file that "file" uses:-

    0       ulong           0xcafebabe      Java class file
    >6 ushort x major version %d.
    >6 ushort <48 Requires JRE 1.3.1 or later
    >6 ushort =48 Requires JRE 1.4.2 or later
    >6 ushort =49 Requires JRE 1.5 or later
    >6 ushort =50 Requires JRE 6 or later
    >6 ushort >50 Requires JRE 7 or later

    You can obtain the following output:-

    $ file Hello.class
    Hello.class: Java class file major version 49. Requires JRE 1.5 or later

    Caveat: Solaris doesn't have a way of specifying big-endian vs little-endian magic numbers so the example lines will only work on SPARC, not x86)


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.