我如何知道哪个版本的Java编译器被用来构建一个jar?我有一个jar文件,它可以构建在三个jdk中的任何一个中。我们需要确切知道是哪一个,这样我们才能证明兼容性。编译器版本是否嵌入到类文件或jar中?


当前回答

我基于david的建议使用file命令构建了一个小bash脚本(在github上)

其他回答

Owen发布的代码可以告诉你一些其他答案中提到的信息:

public void simpleExample ()
{
    FileInputStream fis = new FileInputStream ("mytest.class");
    parseJavaClassFile ( fis );
}
protected void parseJavaClassFile ( InputStream classByteStream ) throws Exception
{
    DataInputStream dataInputStream = new DataInputStream ( classByteStream );
    magicNumber = dataInputStream.readInt();
    if ( magicNumber == 0xCAFEBABE )
    {
        int minorVer = dataInputStream.readUnsignedShort();
        int majorVer = dataInputStream.readUnsignedShort();
        // do something here with major & minor numbers
    }
}

也可以看看这个和这个网站。最后,我迅速修改了Mind Products代码,以检查我的每个依赖项是为了什么而编译的。

Java编译器(javac)不构建jar,它将Java文件转换为类文件。Jar工具(Jar)创建实际的Jar。如果没有指定自定义清单,默认清单将指定使用哪个版本的JDK来创建jar。

每个类文件都嵌入了字节代码级别的版本号,JVM使用该版本号来查看它是否喜欢特定的字节代码块。Java 1.4是48,Java 1.5是49,Java 6是50。

有许多编译器可以在每个级别生成字节代码,javac使用“-target”选项来指示要生成哪个字节代码级别,Java 6 javac可以生成至少1.4、1.5和6级的字节代码。我不相信编译器插入任何可以识别编译器本身,这是我认为你要求的。Eclipse编译器也越来越多地被使用,因为它是一个单独的jar,只能与JRE一起运行。

在jar文件中通常有许多类,并且每个类都是独立的,因此您需要研究jar中的所有类,以确定内容的特征。

罐子只是一个容器。它是一个文件归档文件(ā la tar或zip)。虽然jar可能在其META-INF层次结构中包含有趣的信息,但它没有义务指定其内容中的类的年份。为此,必须检查其中的类文件。

正如Peter Lawrey在对原始问题的评论中提到的,您不一定知道哪个JDK版本构建了给定的类文件,但您可以找到jar中包含的类文件的字节码类版本。

是的,这有点糟糕,但第一步是从罐子中提取一个或多个类。例如:

$ jar xf log4j-1.2.15.jar

在安装了Cygwin的Linux、Mac OS X或Windows上,file(1)命令知道类版本。

$ file ./org/apache/log4j/Appender.class
./org/apache/log4j/Appender.class: compiled Java class data, version 45.3

或者,使用JDK中的javap作为@jikes。Thunderbolt恰当地指出:

$ javap -v ./org/apache/log4j/Appender.class | grep major
 major version: 45

对于没有文件或grep的Windows环境

> javap -v ./org/apache/log4j/Appender.class | findstr major
 major version: 45

FWIW,我同意javap会告诉一个给定的类文件比最初的问题更多的信息。

总之,一个不同的类版本,例如:

$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0

下面的列表显示了类版本的主版本号和引入类主版本的JDK版本。

注意:类版本不一定标识用于编译类的JDK -它只标识可能生成该类的最早版本。

例如,类主版本52可以由Java 7之后的任何JDK生成。

45.3 = Java 1.1 46 = Java 1.2 47 = Java 1.3 48 = java1.4 49 = Java 5 50 = Java 6 51 = Java 7 52 = Java 8 53 = Java 9 54 = Java 10 55 = Java 11 56 = Java 12 57 = Java 13 58 = Java 14 59 = Java 15 60 = Java 16 61 = Java 17 62 = Java 18 63 = Java 19

运行Bash的开发人员和管理员可能会发现这些方便的函数很有帮助:

jar_jdk_version() {
  [[ -n "$1" && -x "`command -v javap`" ]] && javap -classpath "$1" -verbose $(jar -tf "$1" | grep '.class' | head -n1 | sed -e 's/\.class$//') | grep 'major version' | sed -e 's/[^0-9]\{1,\}//'
}

print_jar_jdk_version() {
  local version
  version=$(jar_jdk_version "$1")
  case $version in 49) version=1.5;; 50) version=1.6;; 51) version=1.7;; 52) version=1.8;; esac
  [[ -n "$version" ]] && echo "`basename "$1"` contains classes compiled with JDK version $version."
}

您可以将它们粘贴进来一次性使用,或者将它们添加到~/中。Bash_aliases或~/.bashrc。结果如下所示:

$ jar_jdk_version poi-ooxml-3.5-FINAL.jar
49

and

$ print_jar_jdk_version poi-ooxml-3.5-FINAL.jar
poi-ooxml-3.5-FINAL.jar contains classes compiled with JDK version 1.5.

编辑 正如jackrabbit指出的那样,你不能100%依赖舱单告诉你任何有用的东西。如果是,那么你可以在你最喜欢的UNIX shell中使用unzip将它拉出来:

$ unzip -pa poi-ooxml-3.5-FINAL.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 11.3-b02 (Sun Microsystems Inc.)
Built-By: yegor
Specification-Title: Apache POI
Specification-Version: 3.5-FINAL-20090928
Specification-Vendor: Apache
Implementation-Title: Apache POI
Implementation-Version: 3.5-FINAL-20090928
Implementation-Vendor: Apache

这个.jar在清单中没有任何关于所包含类的有用信息。