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

时间:2010-07-22 21:10:39

标签: java jar

如何判断使用哪个版本的Java编译器构建jar?我有一个jar文件,它可以在三个JDK中的任何一个中构建。我们需要确切地知道哪一个,所以我们可以证明兼容性。编译器版本是嵌入在类文件或jar中的某个地方吗?

21 个答案:

答案 0 :(得分:293)

jar仅仅是一个容器。它是一个文件存档ālatar。虽然jar可能包含其 META-INF 层次结构中包含的有趣信息,但它没有义务在其内容中指定类的复古。为此,必须检查其中的class文件。

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

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

jar

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

jar

或者,使用JDK中的$ jar xf log4j-1.2.15.jar @ jikes.thunderbolt恰当地指出:

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

如果您在没有javap$ javap -v ./org/apache/log4j/Appender.class | grep major major version: 45

的情况下被降级到Windows个环境
file

FWIW,我会同意,grep将比原始问题更多地说明给定的> javap -v ./org/apache/log4j/Appender.class | findstr major major version: 45 文件。

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

javap

类版本主编号对应于以下Java JDK版本:

  • 45.3 = Java 1.1
  • 46 = Java 1.2
  • 47 = Java 1.3
  • 48 = Java 1.4
  • 49 = Java 5
  • 50 = Java 6
  • 51 = Java 7
  • 52 = Java 8
  • 53 = Java 9

答案 1 :(得分:84)

你无法从JAR文件中判断出来。

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

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

注意:如下面的评论所述,

  

这些字节告诉你该类已被编译为FOR的版本,而不是   什么版本编译它。

答案 2 :(得分:48)

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

Windows:javap -v <class> | findstr major
Unix:javap -v <class> | grep major

例如:
> javap -v Application | findstr major   major version: 51

答案 3 :(得分:12)

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

答案 4 :(得分:11)

没有必要解压缩JAR(如果其中一个类名已知或被查找,例如使用7zip),那么在Windows上就足够了:

javap -cp log4j-core-2.5.jar -verbose org.apache.logging.log4j.core.Logger | findstr major

答案 5 :(得分:6)

您可以使用Hex编辑器从.class文件中找到Java编译器版本。

步骤1: 使用zip提取器从jar文件中提取.class文件

步骤2:使用十六进制编辑器打开.class文件。(我使用过notepad ++十六进制编辑器插件。此插件将文件读取为二进制文件,并以十六进制显示) 您可以在下面看到。 enter image description here

索引6和7给出了所使用的类文件格式的主要版本号。 https://en.wikipedia.org/wiki/Java_class_file

Java SE 11 = 55(十六进制0x37)

Java SE 10 = 54(16x 0x36)

Java SE 9 = 53(十六进制0x35)

Java SE 8 = 52(十六进制0x34),

Java SE 7 = 51(十六进制0x33),

Java SE 6.0 = 50(十六进制0x32),

Java SE 5.0 = 49(十六进制0x31)

JDK 1.4 = 48(0x30十六进制),

JDK 1.3 = 47(十六进制0x2F),

JDK 1.2 = 46(十六进制0x2E),

JDK 1.1 = 45(十六进制0x2D)。

答案 6 :(得分:6)

由于我需要分析胖罐子,因此我对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 

答案 7 :(得分:4)

您可以通过inspecting the first 8 bytes(或使用app告诉Java二进制版本。

据我所知,编译器本身并不插入任何识别签名。无论如何,我无法在文件VM spec class format中发现这样的事情。

答案 8 :(得分:4)

code posted by 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
    }
}

另请参阅thisthis网站。我最终快速修改了Mind产品代码,以检查我的每个依赖项的编译内容。

答案 9 :(得分:3)

一个班轮(Linux)

unzip -p mylib.jar META-INF/MANIFEST.MF

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

根据您的软件包的构建,您将在Created-ByBuild-Jdk密钥中找到JDK版本。

答案 10 :(得分:2)

运行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

$ 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%依靠清单告诉你任何有用的东西。如果是,那么您可以使用unzip

在您喜欢的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在包含的类的清单中没有任何用处。

答案 11 :(得分:2)

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

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

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

答案 12 :(得分:1)

关注@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

答案 13 :(得分:1)

很多时候,您可能会查看整个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能够加载类的内容,这可能就是你想要开始的内容。

答案 14 :(得分:1)

扩展Jonathon Faust'sMcDowell's答案:如果您使用的是基于* nix的系统,则可以使用[[ 0. 7. 14. 21. 28. 35. 42.] [ 1. 8. 15. 22. 29. 36. 43.] [ 2. 9. 16. 23. 30. 37. 44.] [ 3. 10. 17. 24. 31. 38. 45.] [ 4. 11. 18. 25. 32. 39. 46.] [ 5. 12. 19. 26. 33. 40. 47.] [ 6. 13. 20. 27. 34. 41. 0.]] (最早的Unix程序之一 1 < / sup>几乎应该可以在任何地方使用)在二进制级别查询od文件:

.class

这将输出熟悉的整数值,例如od -An -j7 -N1 -t dC SomeClassFile.class 50Java 551,依此类推。

1 引自https://en.wikipedia.org/wiki/Od_(Unix)

答案 15 :(得分:0)

您可以使用以下过程在命令行上轻松完成此操作:

如果您知道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文件   :

jar xf myFile.jar

答案 16 :(得分:0)

我还编写了自己的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

答案 17 :(得分:0)

IntelliJ 用户

  • 安装插件存档浏览器
  • 在 Intellij 中打开 Jar。您只需从文件浏览器中拖放 jar 文件,即可将 jar 添加到任何项目中。

  • 浏览 jar 文件以找到一个类文件,IntelliJ 的反编译器应显示用于创建 jar 的 Java 版本。

IntelliJ shows java version

答案 18 :(得分:0)

你检查jar的清单文件 例如:

清单 - 版本:1.0 创建者:1.6.0(IBM Corporation)

答案 19 :(得分:-1)

我使用file命令基于Davids建议构建了一个小bash脚本(on github)

答案 20 :(得分:-1)

在Windows上执行以下操作:

  1. 使用WinZip / Java JAR命令解压缩或解压缩JAR文件。
  2. 将其中一个类文件拖放到Eclipse Java项目中。
  3. 打开课程文件。
  4. 现在Eclipse将显示确切的主要和次要版本。