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


当前回答

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

因为我不想手动检查每个类,所以我写了一个java程序来做到这一点:

https://github.com/Nthalk/WhatJDK

./whatjdk some.war
some.war:WEB-INF/lib/xml-apis-1.4.01.jar contains classes compatible with Java1.1
some.war contains classes compatible with Java1.6

虽然这并没有说明类是用什么编译的,但它决定了哪些JDK能够加载这些类,这可能是您想要开始的。

其他回答

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中的所有类,以确定内容的特征。

您不一定能从JAR文件本身判断出来。

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

http://en.wikipedia.org/wiki/Java_class_file

注:如以下评论所述,

这些字节告诉你类是为哪个版本编译的,而不是 哪个版本编译了它。

因为我需要分析fat jar,所以我对jar文件中每个单独类的版本感兴趣。因此我采取了乔·利弗塞奇的方法 https://stackoverflow.com/a/27877215/1497139,并将其与David J. Liszewski的https://stackoverflow.com/a/3313839/1497139类号版本表结合起来,创建了一个bash脚本jarv,以显示jar文件中所有类文件的版本。

使用

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
...

Bash脚本jarv

#!/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