我如何知道哪个版本的Java编译器被用来构建一个jar?我有一个jar文件,它可以构建在三个jdk中的任何一个中。我们需要确切知道是哪一个,这样我们才能证明兼容性。编译器版本是否嵌入到类文件或jar中?
当前回答
你可以使用十六进制编辑器从.class文件中找到Java编译器版本。
步骤1: 使用zip提取器从jar文件中提取.class文件
第二步:用十六进制编辑器打开。class文件。(我已经使用notepad++十六进制编辑器插件。这个插件读取文件为二进制并显示为十六进制) 如下图所示。
索引6和7给出了所使用的类文件格式的主要版本号。 https://en.wikipedia.org/wiki/Java_class_file
Java SE 11 = 55 (0x37十六进制)
Java SE 10 = 54 (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十六进制)。
其他回答
运行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在清单中没有任何关于所包含类的有用信息。
您可以通过检查前8个字节(或使用可以的应用程序)来判断Java二进制版本。
据我所知,编译器本身并没有插入任何标识签名。无论如何,我不能在文件VM规范类格式中发现这样的东西。
因为我需要分析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
我也写了自己的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
罐子只是一个容器。它是一个文件归档文件(ā 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
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap