Java API, чтобы узнать версию JDK, для которой скомпилирован файл класса?

Существуют ли какие-либо API-интерфейсы Java, чтобы узнать версию JDK, для которой скомпилирован файл класса? Конечно, есть инструмент javap, чтобы узнать основную версию, как указано в здесь. Однако я хочу сделать это программно, чтобы я мог предупредить пользователя о необходимости скомпилировать его для соответствующего JDK.


person Prabhu R    schedule 18.08.2009    source источник
comment
Ответы, связанные с чтением байтов из файла, кажутся мне довольно нецивилизованными. Должен быть способ получить эту информацию с помощью Reflection, например Foobar.class.getTargetVersion ().   -  person John Henckel    schedule 01.10.2014


Ответы (9)


import java.io.*;

public class ClassVersionChecker {
    public static void main(String[] args) throws IOException {
        for (int i = 0; i < args.length; i++)
            checkClassVersion(args[i]);
    }

    private static void checkClassVersion(String filename)
        throws IOException
    {
        DataInputStream in = new DataInputStream
            (new FileInputStream(filename));

        int magic = in.readInt();
        if(magic != 0xcafebabe) {
            System.out.println(filename + " is not a valid class!");;
        }
        int minor = in.readUnsignedShort();
        int major = in.readUnsignedShort();
        System.out.println(filename + ": " + major + " . " + minor);
        in.close();
    }
}

Возможные значения:

major  minor Java platform version 
45       3           1.0
45       3           1.1
46       0           1.2
47       0           1.3
48       0           1.4
49       0           5
50       0           6
51       0           7
52       0           8
53       0           9
54       0           10
55       0           11
56       0           12
57       0           13
58       0           14
person RealHowTo    schedule 18.08.2009

Подход basszero может быть реализован через командную строку UNIX и команду «od (1)»:

% od -x HelloWorldJava.class |head -2
0000000 feca beba 0000 3100 dc00 0007 0102 2a00
0000020 6f63 2f6d 6e65 6564 6163 642f 6d65 2f6f

«feca beba» - это магическое число. «0000 3100» - это 0x31, что соответствует J2SE 5.0.

person rickumali    schedule 06.04.2011

В Linux вы можете использовать команду file в файле класса, что я считаю наиболее простым для моего случая использования. Например:

$ file Foo.class
Foo.class: compiled Java class data, version 50.0 (Java 1.6)
person Daniel Wille    schedule 09.08.2016

Это дает вам содержимое файла класса:

MysteryClass.class.getResourceAsStream("MysteryClass.class")

Затем посмотрите байты 5-8, чтобы получить младшую и основную версии. Соответствие между этими числами и выпусками JDK можно найти здесь.

person Michael Borgwardt    schedule 18.08.2009
comment
Требуется ли полное определение строкового аргумента? getResourceAsStream (org / apache / foobar / Whatever.class)? - person Thorbjørn Ravn Andersen; 19.11.2009
comment
Нет, поскольку вы используете сам класс как основу для получения ресурса. Вам нужно полное имя, если вы можете использовать методы GetResource для ClassLoader вместо класса. - person Michael Borgwardt; 22.11.2009

Apache BCEL предоставляет следующий API:

JavaClass c = Repository.lookupClass("com.x.MyClass")
c.getMinor();
c.getMajor();
person skaffman    schedule 18.08.2009

Просто прочтите файл класса напрямую. С версией ОЧЕНЬ легко разобраться. Ознакомьтесь с спецификацией и wiki, а затем попробуйте код. Обернуть этот код и сделать его более полезным / красивым остается в качестве упражнения. В качестве альтернативы вы можете использовать такую ​​библиотеку, как BCEL, ASM или JavaAssist

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ClassVersionTest
{
    private static final int JAVA_CLASS_MAGIC = 0xCAFEBABE;

    public static void main(String[] args)
    {
        try
        {
            DataInputStream dis = new DataInputStream(new FileInputStream("Test.class"));
            int magic = dis.readInt();
            if(magic == JAVA_CLASS_MAGIC)
            {
                int minorVersion = dis.readUnsignedShort();
                int majorVersion = dis.readUnsignedShort();

                /**
                 * majorVersion is ...
                 * J2SE 6.0 = 50 (0x32 hex),
                 * J2SE 5.0 = 49 (0x31 hex),
                 * JDK 1.4 = 48 (0x30 hex),
                 * JDK 1.3 = 47 (0x2F hex),
                 * JDK 1.2 = 46 (0x2E hex),
                 * JDK 1.1 = 45 (0x2D hex).
                 */

                System.out.println("ClassVersionTest.main() " + majorVersion + "." + minorVersion);
            }
            else
            {
                // not a class file
            }
        }
        catch (FileNotFoundException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
person basszero    schedule 18.08.2009

Как показали другие, это достаточно просто сделать, прочитав первые восемь байтов файла класса. Если вам нужна предварительно созданная двоичная библиотека, вы можете загрузить ее здесь.

person McDowell    schedule 18.08.2009

JavaP имеет API, но он специфичен для Sun JDK.

Он находится в tools.jar, в sun/tools/javap/Main.class.

person Robert Munteanu    schedule 18.08.2009

Я использую этот сценарий в пакетном файле в Windows, перенаправляю вывод и grep для основного числа, которое я проверяю:

for /R %%f in (*.class) do (
"C:\Program Files\Java\JDK8\bin\javap" -v "%%f" | findstr major )

Я запускаю его как таковой:

verify.bat > versionResults.txt
person Beezer    schedule 26.12.2020