Инструмент для чтения и отображения версий Java .class

Кто-нибудь из вас знает инструмент, который будет искать файлы .class и затем отображать их скомпилированные версии?

Я знаю, что вы можете просматривать их по отдельности в шестнадцатеричном редакторе, но у меня есть много файлов классов, которые нужно просмотреть (что-то в моем гигантском приложении по какой-то причине компилируется на Java6).


person SCdF    schedule 25.08.2008    source источник
comment
Более популярный дубликат stackoverflow.com/questions/1096148/, имеет в ответ некоторые удобные инструменты, не упомянутые здесь.   -  person Vadzim    schedule 04.06.2014


Ответы (8)


Используйте инструмент javap, поставляемый с JDK. . Параметр -verbose напечатает номер версии файла класса.

> javap -verbose MyClass
Compiled from "MyClass.java"
public class MyClass
  SourceFile: "MyClass.java"
  minor version: 0
  major version: 46
...

Чтобы показать только версию:

WINDOWS> javap -verbose MyClass | find "version"
LINUX  > javap -verbose MyClass | grep version
person staffan    schedule 26.08.2008
comment
Версия major.minor = JDK / JavaSE; 45,3 = JDK1.1; 46,0 = JDK1.2; 47,0 = JDK1.3; 48,0 = JDK1.4; 49.0 = JavaSE5 (1.5); 51.0 = JavaSE7 (1.7); 50.0 = JavaSE6 (1.6); 52.0 = JavaSE8 (1.8); 53.0 = JavaSE9; 54.0 = JavaSE10; 55.0 = JavaSE11; 56.0 = JavaSE12; 57.0 = JavaSE13; 58.0 = JavaSE14; - person nephewtom; 24.07.2019

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

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;

Для файла класса версии 51.0 (Java 7) открывающими байтами являются:

CA FE BA BE 00 00 00 33

... где 0xCAFEBABE - это магические байты, 0x0000 - дополнительная версия, а 0x0033 - основная версия.

import java.io.*;

public class Demo {
  public static void main(String[] args) throws IOException {
    ClassLoader loader = Demo.class.getClassLoader();
    try (InputStream in = loader.getResourceAsStream("Demo.class");
        DataInputStream data = new DataInputStream(in)) {
      if (0xCAFEBABE != data.readInt()) {
        throw new IOException("invalid header");
      }
      int minor = data.readUnsignedShort();
      int major = data.readUnsignedShort();
      System.out.println(major + "." + minor);
    }
  }
}

Путевые каталоги (File) и архивы (JarFile) поиск файлов классов тривиален .

В блоге Джо Дарси Oracle перечислены сопоставления версии класса с версией JDK до Java 7:

Target   Major.minor Hex
1.1      45.3        0x2D
1.2      46.0        0x2E
1.3      47.0        0x2F
1.4      48.0        0x30
5 (1.5)  49.0        0x31
6 (1.6)  50.0        0x32
7 (1.7)  51.0        0x33
8 (1.8)  52.0        0x34
9        53.0        0x35
person McDowell    schedule 25.08.2008
comment
Также помните, что assert запускается только в том случае, если он включен при запуске java, поэтому вы можете читать ненужные файлы, если вы не используете IllegalArgumentException (например) - person jontejj; 04.07.2013

На Unix-подобном

file /path/to/Thing.class

Также будет указан тип и версия файла. Вот как выглядит результат:

скомпилированные данные класса Java, версия 49.0

person phunehehe    schedule 07.07.2011
comment
(упрощено из ответа WMR) - person phunehehe; 07.07.2011
comment
это намного проще, чем другие решения - person mmuller; 12.07.2017

Если вы работаете в системе unix, вы можете просто сделать

find /target-folder -name \*.class | xargs file | grep "version 50\.0"

(в моей версии файла написано «скомпилированные данные класса Java, версия 50.0» для классов java6).

person WMR    schedule 26.08.2008
comment
В macOS (по крайней мере, 10.12.6) вывод еще более полезен: file *.class производит: ClassName.class: compiled Java class data, version 50.0 (Java 1.6) - person Gary; 19.08.2017

Очередная проверка версии Java

od -t d -j 7 -N 1 ApplicationContextProvider.class | head -1 | awk '{print "Java", $2 - 44}'
person igo    schedule 08.08.2014

В eclipse, если у вас нет прикрепленных источников. Обратите внимание на первую строку после кнопки прикрепления источника.

// Скомпилировано из CDestinoLog.java (версия 1.5: 49.0, супер бит)

введите описание изображения здесь

person PbxMan    schedule 29.01.2016

Может быть, это тоже кому-нибудь поможет. Похоже, есть более простой способ использовать версию JAVA для компиляции / сборки .class. Этот способ полезен для самопроверки приложения / класса в версии JAVA.

Я просмотрел библиотеку JDK и нашел эту полезную константу: com.sun.deploy.config.BuiltInProperties.CURRENT_VERSION. Не знаю, с каких пор это в JAVA JDK.

Пробуя этот фрагмент кода для нескольких констант версии, я получаю результат ниже:

src:

System.out.println("JAVA DEV       ver.: " + com.sun.deploy.config.BuiltInProperties.CURRENT_VERSION);
System.out.println("JAVA RUN     v. X.Y: " + System.getProperty("java.specification.version") );
System.out.println("JAVA RUN v. W.X.Y.Z: " + com.sun.deploy.config.Config.getJavaVersion() ); //_javaVersionProperty
System.out.println("JAVA RUN  full ver.: " + System.getProperty("java.runtime.version")  + " (may return unknown)" );
System.out.println("JAVA RUN       type: " + com.sun.deploy.config.Config.getJavaRuntimeNameProperty() );

вывод:

JAVA DEV       ver.: 1.8.0_77
JAVA RUN     v. X.Y: 1.8
JAVA RUN v. W.X.Y.Z: 1.8.0_91
JAVA RUN  full ver.: 1.8.0_91-b14 (may return unknown)
JAVA RUN       type: Java(TM) SE Runtime Environment

В байт-коде класса действительно хранится константа - см. Выделенную красным часть Main.call - константа, хранящаяся в. байт-код класса

Константа в классе используется для проверки того, устарела ли версия JAVA (см. Как Java проверяет, что это устарело) ...

person Radoslav Kastiel    schedule 16.05.2016

Решение на основе Java, использующее версию магических чисел. Ниже он используется самой программой для определения версии своего байт-кода.

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
public class Main {
    public static void main(String[] args) throws DecoderException, IOException {
        Class clazz = Main.class;
        Map<String,String> versionMapping = new HashMap();
        versionMapping.put("002D","1.1");
        versionMapping.put("002E","1.2");
        versionMapping.put("002F","1.3");
        versionMapping.put("0030","1.4");
        versionMapping.put("0031","5.0");
        versionMapping.put("0032","6.0");
        versionMapping.put("0033","7");
        versionMapping.put("0034","8");
        versionMapping.put("0035","9");
        versionMapping.put("0036","10");
        versionMapping.put("0037","11");
        versionMapping.put("0038","12");
        versionMapping.put("0039","13");
        versionMapping.put("003A","14");

        InputStream stream = clazz.getClassLoader()
            .getResourceAsStream(clazz.getName().replace(".", "/") + ".class");
        byte[] classBytes = IOUtils.toByteArray(stream);
        String versionInHexString = 
            Hex.encodeHexString(new byte[]{classBytes[6],classBytes[7]});
        System.out.println("bytecode version: "+versionMapping.get(versionInHexString));
    }
}
person Marinos An    schedule 31.10.2019