Java .class sürümlerini okumak ve görüntülemek için araç


115

.Class dosyalarını arayacak ve daha sonra derlenmiş sürümlerini görüntüleyecek bir araç biliyor musunuz?

Onlara bir onaltılık düzenleyicide tek tek bakabileceğinizi biliyorum, ancak bakmam gereken çok sayıda sınıf dosyası var (dev uygulamamdaki bir şey nedense Java6'da derleniyor).


1
Daha popüler yinelenen stackoverflow.com/questions/1096148/… , burada bahsedilmeyen bazı kullanışlı araçlara cevap vermektedir.
Vadzim

Yanıtlar:


142

JDK ile birlikte gelen javap aracını kullanın . -verboseOpsiyon sınıf dosyasının sürüm numarasını yazdırır.

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

Yalnızca sürümü göstermek için:

WINDOWS> javap -verbose MyClass | find "version"
LINUX  > javap -verbose MyClass | grep version

2
Sürüm major.minor = JDK / JavaSE; 45.3 = JDK 1.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;
nephewtom

45

Sınıf dosyası imzasını okumak ve bu değerleri 3. parti API olmadan almak yeterince kolaydır . Tek yapmanız gereken ilk 8 baytı okumaktır.

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;

Sınıf dosyası sürüm 51.0 (Java 7) için açılış baytları şunlardır:

CA FE BA BE 00 00 00 33

... burada 0xCAFEBABE sihirli baytlar, 0x0000 ikincil sürüm ve 0x0033 ana sürümdür.

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);
    }
  }
}

Sınıf dosyalarını aramak için dizinleri ( Dosya ) ve arşivleri ( JarFile ) aramak önemsizdir.

Oracle'ın Joe Darcy blogu , Java 7'ye kadar JDK sürüm eşlemelerinin sınıf sürümünü listeler :

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

Ayrıca assert'in yalnızca java başlatılırken etkinleştirildiğinde çalıştırıldığını unutmayın, böylece
IllegalArgumentException

21

Unix benzeri

dosya /yol/to/Thing.class

Dosya türünü ve sürümünü de verecektir. Çıktı şu şekilde görünüyor:

derlenmiş Java sınıfı verileri, sürüm 49.0


(
WMR'nin

bu diğer çözümlerden çok daha basit
mmuller

9

Bir unix sistemindeyseniz, sadece bir

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

(Benim dosya sürümüm, java6 sınıfları için "derlenmiş Java sınıfı verileri, sürüm 50.0" diyor).


MacOS'ta (en azından 10.12.6), çıktı daha da yararlıdır: şunu file *.class üretir: ClassName.class: compiled Java class data, version 50.0 (Java 1.6)
Gary

5

Yine başka bir java sürümü kontrolü

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

5

Tutulmada, ekli kaynaklarınız yoksa. Kaynak ekle düğmesinden sonraki ilk satıra dikkat edin.

// CDestinoLog.java'dan derlenmiştir ( sürüm 1.5: 49.0, süper bit )

görüntü açıklamasını buraya girin


2

Belki bu da birine yardımcı olur. .Class'ı derlemek / oluşturmak için kullanılan JAVA sürümünü almanın daha kolay yolu var gibi görünüyor. Bu yol, JAVA sürümünde uygulama / sınıf kendi kendine kontrolü için kullanışlıdır.

JDK kitaplığını inceledim ve şu kullanışlı sabiti buldum: com.sun.deploy.config.BuiltInProperties.CURRENT_VERSION . JAVA JDK'da ne zamandan beri bilmiyorum.

Bu kod parçasını birkaç sürüm sabiti için denediğimde aşağıdaki sonucu alıyorum:

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() );

çıktı:

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

Sınıf bayt kodunda gerçekten depolanmış sabit vardır - Main.call'ın kırmızı işaretli kısmına bakın - .class bayt kodunda depolanan sabit

Sabit, JAVA sürümünün güncel olup olmadığını kontrol etmek için kullanılan sınıftadır (bkz.Java, güncelliğini nasıl kontrol eder ) ...


1

Sürüm sihirli numaralarını kullanan java tabanlı bir çözüm . Aşağıda, programın kendisi tarafından bayt kodu sürümünü tespit etmek için kullanılır.

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));
    }
}

0

Bu Java sınıfı, dizinler listesi altında bulunan tüm WAR içeriklerinin ve JAR'ların içeriğini tarar ve WARs içindeki her JAR dahil olmak üzere her bileşen için java sınıfı dosya sürümlerinin özetini yazdırır:

public class ShowClassVersions {
    private static final byte[] CLASS_MAGIC = new byte[] {(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe};
    private final byte[] bytes = new byte[8];
    private TreeMap<String,ArrayList<String>> vers = new TreeMap<>();

    private void scan(Path f) throws IOException {
        if (Files.isDirectory(f)) {
            Pattern pattern = Pattern.compile("\\.[wj]ar$"); // or |\\.class
            try(var stream = Files.find(f, Integer.MAX_VALUE, (p,a) -> a.isRegularFile() && pattern.matcher(p.toString()).find())) {
                stream.forEach(this::scanFile);
            }
            return;
        }
        scanFile(f);
    }
    private void scanFile(Path f) {
        String fn = f.getFileName().toString();
        try {
            if (fn.endsWith(".jar"))
                scanArchive(f);
            else if (fn.endsWith(".war"))
                scanArchive(f);
            else if (fn.endsWith(".class"))
                record(f, versionOfClass(f));
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    private void scanArchive(Path p) throws IOException {
        try(InputStream in = Files.newInputStream(p))  {
            scanArchive(p.toAbsolutePath().toString(), in);
        }
    }
    private String scanArchive(String desc, InputStream in) throws IOException {
        String version = null;
        ZipInputStream zip = new ZipInputStream(in);
        ZipEntry entry = null;
        while ((entry = zip.getNextEntry()) != null) {
            String name = entry.getName();
            if (version == null && name.endsWith(".class"))  {
                version = versionOfClass(zip);
            }
            else if (name.endsWith(".jar"))  {
                scanArchive(desc+" ==>> "+name, zip);
            }
        }
        if (version != null)
            record(desc, version);
        return version;
    }

    private String versionOfClass(Path p) throws IOException {
        String version = null;
        try(InputStream in = Files.newInputStream(p)) {
            version = versionOfClass(in);
        }
        return version;
    }

    private String versionOfClass(InputStream in) throws IOException {
        String version = null;
        int c = in.read(bytes);
        if (c == bytes.length && Arrays.mismatch(bytes, CLASS_MAGIC) == CLASS_MAGIC.length) {
            int minorVersion = (bytes[4] << 8) + (bytes[4] << 0);
            int majorVersion = (bytes[6] << 8) + (bytes[7] << 0);
            version = ""+majorVersion + "." + minorVersion;
        }
        return version;
    }
    private void record(String p, String v) {
        vers.computeIfAbsent(String.valueOf(v), k -> new ArrayList<String>()).add(p);
    }
    private void record(Path p, String v) {
        record(p.toAbsolutePath().toString(), v);
    }
    public static void main(String[] args) throws IOException {
        ShowClassVersions v = new ShowClassVersions();
        var files = Arrays.stream(args).map(Path::of).collect(Collectors.toList());
        for (var f : files) {
            v.scan(f);
        }
        for (var ver : v.vers.keySet()) {
            System.out.println("Version: "+ver);
            for (var p : v.vers.get(ver)) {
                System.out.println("   "+p);
            }
        };
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.