什么版本的javac构建了我的jar?

什么版本的javac构建了我的jar?,java,jar,Java,Jar,我如何知道构建jar时使用了哪个版本的Java编译器?我有一个jar文件,它可以构建在三个JDK中的任何一个中。我们需要知道到底是哪一个,这样我们才能证明兼容性。编译器版本是否嵌入在类文件或jar的某个地方?您无法从jar文件本身中分辨出来 下载一个十六进制编辑器,打开JAR中的一个类文件,查看字节偏移量4到7。版本信息是内置的 注:如以下评论所述 这些字节告诉您编译类的目的是什么版本,而不是 是哪个版本编译的 您可以通过(或使用一个可以识别的名称)来识别Java二进制版本 据我所知,编译器本

我如何知道构建jar时使用了哪个版本的Java编译器?我有一个jar文件,它可以构建在三个JDK中的任何一个中。我们需要知道到底是哪一个,这样我们才能证明兼容性。编译器版本是否嵌入在类文件或jar的某个地方?

您无法从jar文件本身中分辨出来

下载一个十六进制编辑器,打开JAR中的一个类文件,查看字节偏移量4到7。版本信息是内置的

注:如以下评论所述

这些字节告诉您编译类的目的是什么版本,而不是 是哪个版本编译的


您可以通过(或使用一个可以识别的名称)来识别Java二进制版本


据我所知,编译器本身不插入任何识别签名。无论如何,我在文件中找不到这样的东西。

Java编译器(
javac
)不构建JAR,而是将Java文件转换为类文件。Jar工具(
Jar
)创建实际的Jar。如果未指定自定义清单,默认清单将指定用于创建jar的JDK版本。

每个类文件都有一个嵌入字节码级别的版本号,JVM使用该版本号查看是否喜欢该特定字节码块。Java1.4是48,Java1.5是49,Java6是50

存在许多可以在每个级别生成字节码的编译器,javac使用“-target”选项指示要生成的字节码级别,而java6javac至少可以生成1.4、1.5和6的字节码。我不相信编译器会插入任何能够识别编译器本身的东西,我认为这是您所要求的。此外,Eclipse编译器也越来越多地被使用,因为它是一个仅能与JRE一起运行的jar


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

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

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

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

$ jar xf log4j-1.2.15.jar
$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0
在已安装的Linux、Mac OS X或Windows上,该命令知道类的版本

$ 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
将比最初提出的问题更多地讲述一个给定的
文件

无论如何,不同的类版本,例如:

$ jar xf log4j-1.2.15.jar
$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0
类版本主编号对应于以下Java JDK版本:

  • 45.3=Java 1.1
  • 46=Java 1.2
  • 47=Java 1.3
  • 48=Java 1.4
  • 49=Java 5
  • 50=爪哇6
  • 51=Java 7
  • 52=爪哇8
  • 53=Java 9
  • 54=爪哇10
  • 55=爪哇11
  • 56=爪哇12
  • 57=爪哇13
  • 58=爪哇14
您可以在此处了解其他一些答案中提到的信息:

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代码,以检查每个依赖项的编译目的。

继@David J.Liszewski的回答之后,我在Ubuntu上运行了以下命令来提取jar文件的清单:

# Determine the manifest file name:
$ jar tf LuceneSearch.jar | grep -i manifest
META-INF/MANIFEST.MF

# Extract the file:
$ sudo jar xf LuceneSearch.jar META-INF/MANIFEST.MF

# Print the file's contents:
$ more META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b30 (Oracle Corporation)
Main-Class: org.wikimedia.lsearch.config.StartupManager

在Windows上执行以下操作:

  • 使用WinZip/javajar命令解压或解压缩JAR文件
  • 将其中一个类文件拖放到EclipseJava项目中
  • 打开类文件

  • 现在Eclipse将显示准确的主要版本和次要版本。

    以下是Java查找此信息的方法

    Windows:
    javap-v|findstr-major

    Unix:
    javap-v|grep-major

    例如:
    >javap-v应用程序| findstr专业
    主要版本:51
    

    运行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\u别名
    ~/.bashrc
    。结果如下所示:

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

    编辑 正如所指出的,您不能100%依赖清单来告诉您任何有用的信息。如果是,那么您可以在您最喜欢的UNIX shell中通过
    解压将其拉出:

    $ 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在清单中没有任何关于所包含类的有用信息。

    我使用
    文件
    命令一行程序(Linux)基于Davids的建议构建了一个小bash脚本

    解压-p mylib.jar META-INF/MANIFEST.MF

    这会将
    MANIFEST.MF
    文件的内容打印到stdout(希望您的jar文件中有一个:)


    根据构建包的内容,您将在由
    Build JDK
    键创建的
    中找到JDK版本。

    很多时候,您可能会看到整个jar文件,或者除了自身之外还包含许多jar文件的war文件

    因为我不想手工检查每个类,所以我编写了一个java程序:

    虽然这并没有说明类是用什么编译的,但它决定了JDK能够加载哪些类,这可能是您想要开始的
    javap -cp log4j-core-2.5.jar -verbose org.apache.logging.log4j.core.Logger | findstr major
    
    od -An -j7 -N1 -t dC SomeClassFile.class
    
    usage: ./jarv jarfile
     -h|--help: show this usage
    
    jarv $Home/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar
    
    java 1.4 org.apache.log4j.Appender
    java 1.4 org.apache.log4j.AppenderSkeleton
    java 1.4 org.apache.log4j.AsyncAppender$DiscardSummary
    java 1.4 org.apache.log4j.AsyncAppender$Dispatcher
    ...
    
    #!/bin/bash
    # WF 2018-07-12
    # find out the class versions with in jar file
    # see https://stackoverflow.com/questions/3313532/what-version-of-javac-built-my-jar
    
    # uncomment do debug
    # set -x
    
    #ansi colors
    #http://www.csc.uvic.ca/~sae/seng265/fall04/tips/s265s047-tips/bash-using-colors.html
    blue='\033[0;34m'  
    red='\033[0;31m'  
    green='\033[0;32m' # '\e[1;32m' is too bright for white bg.
    endColor='\033[0m'
    
    #
    # a colored message 
    #   params:
    #     1: l_color - the color of the message
    #     2: l_msg - the message to display
    #
    color_msg() {
      local l_color="$1"
      local l_msg="$2"
      echo -e "${l_color}$l_msg${endColor}"
    }
    
    #
    # error
    #
    #   show an error message and exit
    #
    #   params:
    #     1: l_msg - the message to display
    error() {
      local l_msg="$1"
      # use ansi red for error
      color_msg $red "Error: $l_msg" 1>&2
      exit 1
    }
    
    #
    # show the usage
    #
    usage() {
      echo "usage: $0 jarfile"
      # -h|--help|usage|show this usage
      echo " -h|--help: show this usage"
      exit 1 
    }
    
    #
    # showclassversions
    #
    showclassversions() {
      local l_jar="$1"
      jar -tf "$l_jar" | grep '.class' | while read classname
      do
        class=$(echo $classname | sed -e 's/\.class$//')
        class_version=$(javap -classpath "$l_jar" -verbose $class | grep 'major version' | cut -f2 -d ":" | cut -c2-)
        class_pretty=$(echo $class | sed -e 's#/#.#g')
        case $class_version in
          45.3) java_version="java 1.1";;
          46) java_version="java 1.2";;
          47) java_version="java 1.3";;
          48) java_version="java 1.4";;
          49) java_version="java5";;
          50) java_version="java6";;
          51) java_version="java7";;
          52) java_version="java8";;
          53) java_version="java9";;
          54) java_version="java10";;
          *) java_version="x${class_version}x";;
        esac
        echo $java_version $class_pretty
      done
    }
    
    # check the number of parameters
    if [ $# -lt 1 ]
    then
      usage
    fi
    
    # start of script
    # check arguments
    while test $# -gt 0
    do
      case $1 in
        # -h|--help|usage|show this usage
        -h|--help) 
          usage
          exit 1
          ;;
        *)
         showclassversions "$1"
      esac
      shift
    done 
    
    $ jar_dump_version_of_jvm_required.sh *.jar
    JVM VERSION REQUIRED: 46.0, /private/tmp/jars/WEB-INF/lib/json-simple-1.1.jar
    JVM VERSION REQUIRED: 49.0, /private/tmp/jars/WEB-INF/lib/json-smart-1.1.1.jar
    JVM VERSION REQUIRED: 50.0, /private/tmp/jars/WEB-INF/lib/jsontoken-1.0.jar
    JVM VERSION REQUIRED: 50.0, /private/tmp/jars/WEB-INF/lib/jsr166y-1.7.0.jar
    
    #!/bin/bash
    
    DIR=$(PWD)
    function show_help()
    {
      ME=$(basename $0)
      IT=$(cat <<EOF
    
      Dumps the version of the JVM required to run the classes in a jar file
    
      usage: $ME JAR_FILE
    
      e.g. 
    
      $ME myFile.jar    ->  VERSION: 50.0     myFile.jar
    
      Java versions are:
      54 = Java 10
      53 = Java 9
      52 = Java 8
      51 = Java 7
      50 = Java 6
      49 = Java 5
      48 = Java 1.4
      47 = Java 1.3
      46 = Java 1.2
      45.3 = Java 1.1
    
    EOF
      )
      echo "$IT"
      exit
    }
    
    if [ "$1" == "help" ]
    then
      show_help
    fi
    if [ -z "$1" ]
    then
      show_help
    fi
    
    function unzipJarToTmp()
    {
      JAR=$1
      CLASS_FILE=$(jar -tf "$JAR" | grep \.class$ | grep -v '\$' | head -n1 | awk '{print $NF}')
      OUT_FILE="$CLASS_FILE"
      #echo "J=$JAR C=$CLASS_FILE O=$OUT_FILE"
      jar xf "$JAR" "$CLASS_FILE"
    
      MAJOR=$(javap -v "$OUT_FILE" 2>&1 | grep major | awk -F' ' '{print $3'})
      MINOR=$(javap -v "$OUT_FILE" 2>&1 | grep minor | awk -F' ' '{print $3'})
      if [ -z "$MAJOR" ]
      then
        echo "JVM VERSION REQUIRED: NA as no classes in $JAR"
      else
        echo "JVM VERSION REQUIRED: $MAJOR.$MINOR, $JAR"
      fi
    }
    
    # loop over cmd line args
    for JAR in "$@"
    do
      cd "$DIR"
      JAR_UID=$(basename "$JAR" | sed s/.jar//g)
      TMPDIR=/tmp/jar_dump/$JAR_UID/
      mkdir -p "$TMPDIR"
      JAR_ABS_PATH=$(realpath $JAR)
    
      cd "$TMPDIR"
    
      #echo "$JAR_ABS_PATH"
      unzipJarToTmp "$JAR_ABS_PATH"
      #sleep 2
    done
    
    javap -cp jarname.jar -verbose packagename.classname | findstr major
    
        C:\pathwherejarlocated> javap -cp jackson-databind-2.8.6.jar -verbose com.fasterxml.jackson.databind.JsonMappingException | findstr major
    
        major version: 51
    
    JDK 1.0 — major version 45 
    DK 1.1 — major version 45 
    JDK 1.2 — major version 46 
    JDK 1.3 — major version 47 
    JDK 1.4 — major version 48 
    JDK 1.5 — major version 49 
    JDK 1.6 — major version 50 
    JDK 1.7 — major version 51 
    JDK 1.8 — major version 52 
    JDK 1.9 — major version 53 
    
    jar xf myFile.jar