Công cụ đọc và hiển thị các phiên bản Java. Class


115

Có ai trong số các bạn biết về một công cụ sẽ tìm kiếm các tệp. Class và sau đó hiển thị các phiên bản đã biên dịch của chúng không?

Tôi biết bạn có thể xem chúng riêng lẻ trong trình soạn thảo hex nhưng tôi có rất nhiều tệp lớp để xem qua (một số thứ trong ứng dụng khổng lồ của tôi đang biên dịch sang Java6 vì một số lý do).


1
Nhân đôi phổ biến hơn stackoverflow.com/questions/1096148/ đã trả lời một số công cụ tiện dụng không được đề cập ở đây.
Vadzim

Câu trả lời:


142

Sử dụng công cụ javap đi kèm với JDK. Các -verbosetùy chọn sẽ in số phiên bản của tập tin lớp.

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

Để chỉ hiển thị phiên bản:

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

2
Phiên bản 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;
cháu trai

45

Thật dễ dàng để đọc chữ ký tệp lớp và nhận các giá trị này mà không cần API của bên thứ 3. Tất cả bạn cần làm là đọc 8 byte đầu tiên.

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;

Đối với phiên bản tệp lớp 51.0 (Java 7), các byte mở là:

CA FE BA BE 00 00 00 33

... Trong đó 0xCAFEBABE là các byte ma thuật, 0x0000 là phiên bản nhỏ và 0x0033 là phiên bản chính.

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

Thư mục đi bộ ( Tệp ) và lưu trữ ( JarFile ) tìm kiếm tệp lớp là không đáng kể.

Blog Joe Darc của Oracle liệt kê phiên bản lớp cho ánh xạ phiên bản JDK lên tới 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

Ngoài ra, hãy nhớ rằng assert chỉ chạy nếu được bật khi khởi chạy java để bạn có thể đọc các tệp rác nếu bạn không sử dụng IllegalArgumentException (ví dụ)
jontejj

21

Trên Unix giống như

tập tin /path/to/Thing. class

Sẽ cung cấp loại tập tin và phiên bản là tốt. Đây là những gì đầu ra trông như:

dữ liệu lớp Java được biên dịch, phiên bản 49.0


(được đơn giản hóa từ câu trả lời của WMR)
phunehehe

cách này đơn giản hơn các giải pháp khác
mmuller

9

Nếu bạn đang sử dụng hệ thống unix, bạn có thể thực hiện

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

(phiên bản tệp của tôi nói "dữ liệu lớp Java đã biên dịch, phiên bản 50.0" cho các lớp java6).


Trên macOS (ít nhất là 10.12.6), đầu ra thậm chí còn hữu ích hơn: file *.class sản xuất: ClassName.class: compiled Java class data, version 50.0 (Java 1.6)
Gary

5

Một phiên bản java khác kiểm tra

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

5

Trong nhật thực nếu bạn không có nguồn đính kèm. Nhớ dòng đầu tiên sau nút nguồn đính kèm.

// Được tổng hợp từ CDestinoLog.java ( phiên bản 1.5: 49.0, siêu bit )

nhập mô tả hình ảnh ở đây


2

Có lẽ điều này cũng giúp ai đó. Có vẻ như có nhiều cách dễ dàng hơn để có được phiên bản JAVA được sử dụng để biên dịch / xây dựng. Class. Cách này rất hữu ích để tự kiểm tra ứng dụng / lớp trên phiên bản JAVA.

Tôi đã đi qua thư viện JDK và tìm thấy hằng số hữu ích này: com.sun.deploy.config.BuiltInProperIES.CURRENT_VERSION . Tôi không biết từ khi nào nó ở trong JAVA JDK.

Thử đoạn mã này cho một số hằng số phiên bản tôi nhận được kết quả bên dưới:

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

đầu ra:

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

Trong lớp mã byte thực sự có hằng số được lưu trữ thực sự - xem phần được đánh dấu màu đỏ của Main.call - hằng số được lưu trữ trong. Class byodeode

Hằng số trong lớp được sử dụng để kiểm tra xem phiên bản JAVA đã hết hạn chưa (xem Cách kiểm tra Java đã hết hạn ) ...


1

Một giải pháp dựa trên java sử dụng số ma thuật phiên bản . Bên dưới nó được sử dụng bởi chính chương trình để phát hiện phiên bản mã byte của nó.

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

Lớp Java này quét nội dung của tất cả các nội dung WAR và JAR được tìm thấy trong danh sách các thư mục và in tóm tắt các phiên bản tệp của lớp java cho từng thành phần bao gồm từng JAR trong WARs:

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);
            }
        };
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.