Determine major Java version across all Java releases

Viewed 963

When determining the major Java version on Java 8 and before it was common to use the system property java.specification.version, drop 1., and parse the second digit:

  • on Java 8 this would yield "1.8" ~> "8" ~> 8
  • on Java 9 the same calls leads to NumberFormatException because the system property is "9"

What is a future-proof way to determine the major Java version? The goal is to get an int that can be if-ed or switch-ed over to decide which code path to take (e.g. in a library to activate a feature).

4 Answers

Java 9 introduced the Runtime.Version class, which should hopefully be supported for some time to come. Pairing it with the old approach I got:

public static int getMajorVersion() {
    try {
        // use Java 9+ version API via reflection, so it can be compiled for older versions
        Method runtime_version = Runtime.class.getMethod("version");
        Object version = runtime_version.invoke(null);
        Method version_major = runtime_version.getReturnType().getMethod("major");
        return (int) version_major.invoke(version);
    // do not catch `ReflectiveOperationException` because it does not exist in Java <7
    } catch (Exception ex) {
        // before Java 9 system property 'java.specification.version'
        // is of the form '1.major', so return the int after '1.'
        String versionString = System.getProperty("java.specification.version");
        return Integer.parseInt(versionString.substring(2));
    }
}

(I release this code under CC-0: You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.)

It works on my machine (haha), but I'm not sure whether this is the best solution, because I don't know whether either the new API or the system property have any corner cases that I'm not aware of.

See also Stephen's answer for why reducing the version to a single digit might not be a good idea in the first place.

Obviously, nothing can be guaranteed future-proof. We cannot predict the future with certainty (!)

However, the following would work for previous versions of Java and versions that conform with JEP 233:

  1. If version string starts with "1.[0-4]" use as is.
  2. If version string starts with "1.[5-8]" remove the "1."
  3. Otherwise, use the number before the first "."

But there is also the issue of which version of the numbering you use. For example, "Java 5" and "Java 1.5" mean the same thing. Which you use depends on who you are trying to satisfy with your naming scheme.

A good (but not definitive) reference on what the "official" Java version names is:


Note that your original scheme breaks for early Java versions like "1.2.1" and "1.3.1" where the final number is significant. And you don't want to start labeling Java 1.0 as "Java 0". Finally, Java 1.0 and Java 1.1 are very different and should not be confused. (Java 1.0 doesn't have nested / inner classes, for a start.)


I would only do this for the purpose of making a "manager friendly" name. I would not reduce the version to a single number for "decision making purposes". You are liable to find that differences between minor versions are significant.

Of course, it depends on the goal of the operation. If you want to report it to the user in a Java marketing compatible way, you have to face the fact that the marketing itself never used a consistent labeling and may retroactively relabel old versions.

If the check is only intended to ensure the presence of certain features or bug fixes, just assigning an ascending number to every release would be sufficient. Then, you may assign 0 to Java 1.0, which is significantly different from Java 1.1 (assign 1 to it) and get a consistent numbering up to nine using

public static int getMajorVersion() {
    String version = System.getProperty("java.class.version");
    int p = version.indexOf('.');
    if(p>0) version = version.substring(0, p);
    return Integer.parseInt(version)-44;
}

The good thing about the class file version is that it is bound to a more formal definition, as it has to fit into the two fields of a class file, so it can’t be subject to scheme changes nor retroactive redefinition. Also, there is no room for prose like “beta”, “final”, or “please interpret differently” in these two version numbers. The only thing, the code above protects against, is the potential omission of the .0 minor number which has not been used since Java 1.1, as the major class file version has been incremented for every release.

Of course, there is no guaranty that the number will be incremented again in each of the next releases, however, this is not an issue for compatibility checks, as it will always have at least the number of the previous release, being interpreted as “compatible with the previous release”. To start using newer features, you have to touch the source code anyway. In that case, you may add the Runtime.version() based operation for these future releases…

But note that you get this for free when compiling with -target or --release, as the required minimum version is written into the class file anyway and older JVM versions will refuse to execute your code. When you want to optionally support features of a version newer than the minimum release, you have to access them dynamically anyway, so in this case, you can simply make a reflective attempt to use the feature, going to the fallback code if failed, and don’t need to do an additional version number check. That’s exactly what you are doing when trying to implement getMajorVersion() doing a reflective Runtime.version().major(), without a preceding version based check for the presence of that feature.

Alternatively and probably to comply with the 1.5 and above versions only, a simpler way still could be to make use of :

private static int getMajorVersion() {
    String systemVersionProperty = System.getProperty("java.specification.version");
    return systemVersionProperty.contains(".") ? Integer.parseInt(systemVersionProperty.substring(2)) :
            Integer.parseInt(systemVersionProperty);
}
Related