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


当前回答

我也写了自己的bash脚本来转储所有在命令行传递的jar所需的Java版本…我的有点粗糙,但适合我;-)

示例使用

$ 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

jar_dump_version_of_jvm_required.sh

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

其他回答

你可以在命令行上使用以下过程轻松完成:

如果你知道jar中的任何一个类名,你可以使用以下命令:

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 

PS:如果你不知道任何类名,你可以很容易地做到这一点 使用任何jar反编译器或简单地使用以下命令来提取jar文件 : myFile.jar

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

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

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

注:如以下评论所述,

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

我也写了自己的bash脚本来转储所有在命令行传递的jar所需的Java版本…我的有点粗糙,但适合我;-)

示例使用

$ 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

jar_dump_version_of_jvm_required.sh

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

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代码,以检查我的每个依赖项是为了什么而编译的。

每个类文件都嵌入了字节代码级别的版本号,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中的所有类,以确定内容的特征。