Какая версия Javac построил мою банку?

212

Как я могу сказать, какая версия компилятора Java использовалась для создания jar? У меня есть файл JAR, и он может быть встроен в любой из трех JDK. Нам нужно точно знать, какой именно, чтобы мы могли сертифицировать совместимость. Версия компилятора встроена где-то в файлы классов или jar?

Рон Ромеро
источник
8
Вы можете узнать главную версию, взглянув на файл манифеста. Вы можете узнать целевую версию, посмотрев сами файлы классов, однако JDK могут создавать файлы классов для более ранних версий java, используя опцию -target, поэтому просмотр первых байтов может быть неточным.
Питер Лори
15
В MANIFEST.MF вы можете найти что-то вродеCreated-By: 1.7.0_13 (Oracle Corporation)
hko19
1
Похоже, что Maven 3 делает это Created-By: Apache MavenиBuild-Jdk: 1.8.0_25
Нил Сюн
Пожалуйста, проверьте это: stackoverflow.com/questions/27065/…
Abheek Dutta

Ответы:

89

Вы не можете сказать из самого файла JAR, обязательно.

Загрузите шестнадцатеричный редактор и откройте один из файлов классов внутри JAR и посмотрите на смещения байтов с 4 по 7. Информация о версии встроена.

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

Примечание: как указано в комментарии ниже,

эти байты сообщают вам, для какой версии был скомпилирован класс, а не какая версия его скомпилировала.

Джонатон Фауст
источник
41
Чтобы быть педантичными, эти байты сообщают вам, для какой версии был скомпилирован класс, а не какая версия его скомпилировала. Java позволяет вам компилировать код так, чтобы он был совместим с более ранними версиями Java. Однако это относится только к байтовому коду и формату. Например, он с радостью скомпилирует код, который ссылается на библиотеки JDK 6, в формат JDK 5. JDK 5 загрузит класс, но не сможет его запустить, поскольку библиотека JDK 5 не имеет кода, на который ссылается JDK 6.
Will Hartung
6
Мы могли бы найти его в файле манифеста как Создано: 1.7.0_21-b11 (Oracle Corporation)
Кришна
8
Другой ответ рассказывает, как легко проверить через командную строку
mgarciaisaia
313

А jarэто просто контейнер. Это файловый архив а-ля tar. Хотя a jarможет содержать интересную информацию, содержащуюся в его иерархии META-INF , он не обязан указывать марку классов внутри своего содержимого. Для этого нужно изучитьclass файлы в нем.

Как упоминал Питер Лоури в комментарии к исходному вопросу, вы не можете точно знать, какая версия JDK создала данный classфайл, но вы можете узнать версию classфайла класса байт-кода, содержащуюся вjar .

Да, это отстой, но первым шагом является извлечение одного или нескольких классов из jar. Например:

$ jar xf log4j-1.2.15.jar

В Linux, Mac OS X или Windows с установленным Cygwin команда file (1) знает версию класса.

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

Или, альтернативно, использование javapиз JDK как @ jikes.thunderbolt удачно указывает:

$ javap -v ./org/apache/log4j/Appender.class | grep major
 major version: 45

И если вы переведены в Windowsсреду без fileилиgrep

> javap -v ./org/apache/log4j/Appender.class | findstr major
 major version: 45

FWIW, я согласен, что javapрасскажет намного больше о данномclass файле, чем первоначальный вопрос.

В любом случае, версия другого класса, например:

$ 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 = Java 6
  • 51 = Java 7
  • 52 = Java 8
  • 53 = Java 9
Дэвид Дж. Лишевски
источник
7
Моя версия fileне показали, но я был в состоянии проверить класс вручную с помощью этой команды: hexdump ~/bin/classes/P.class | head. Просто посмотрите на восьмой байт и преобразуйте в десятичный.
Джаретт Миллард
2
FYI 'файл', кажется, зависит от версии Java относительно того, будет ли он также отображать версию JDK. В CentOS / Oracle Linux и скомпилированном классе Java 6 я получаю «скомпилированные данные класса Java, версия 50.0 (Java 1.6)», но когда я запускаю его в классе, скомпилированном с Java 7, я просто получаю общую версию «скомпилированные данные класса Java, версия 51.0 "
Дэн Хейнс
1
JAR=something.jar ; unzip -p $JAR `unzip -l $JAR | grep '\.class$' | head -1` | file -
Рэндалл Уитмен
3
@JarettMillard my cygwin file file.class(5.22-1) изначально не показывал цель класса java: "file.class: [зодчество = 6909806] [зодчество = 6845039]". Однако file -k file.classсделал это: «file.class: [Architecture = 6909806] [Architecture = 6845039] скомпилированные данные класса Java, версия 50.0 (Java 1.6)». Похоже, fileполучил только первый матч в его дабах magicfiles без -k.
Дим
1
Использование «javap -p <class> | grep major» не является надежным. Если pom.xml имеет источник / цель 1.7, javap всегда выдаст вам «главную версию: 51» независимо от того, используете ли вы JDK 1.7, JDK 1.8 или JDK 1.9 для компиляции.
user2569618
54

Вот способ Java найти эту информацию.

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

Например:
> javap -v Application | findstr major   major version: 51

jikes.thunderbolt
источник
3
из всех приведенных ответов ваш ответ является наиболее кратким и не требует написания 10 строк кода для получения информации о версии. +1 за это
Тирумалай Партасаратхи
2
Для чего нужен <class>параметр?
Дим
1
@Dims Файл .class, который вы пытаетесь найти версию. В его примере у него есть Application.classфайл, и он скомпилирован для Java 7 ( major version: 51).
inanutshellus
1
Эти команды предоставляют целевую версию JVM, а не javacверсию, скомпилировавшую файлы .class, о чем и просили.
маркиз Лорн
15

Нет необходимости распаковывать JAR (если одно из имен классов известно или ищется, например, с помощью 7zip), поэтому в Windows будет достаточно следующего:

javap -cp log4j-core-2.5.jar -verbose org.apache.logging.log4j.core.Logger | findstr major
НАРЭ
источник
1
Это работает и в Linux (за исключением того, что вы, конечно, используете grep вместо findstr)
Майкл Раш
1
Эти команды обеспечивают версию целевой виртуальной машины Java, а не javacверсия , которая составлена .class - файлы, что и было запрошено.
маркиз Лорн
15

Компилятор Java ( javac) не создает jar-файлы, он переводит файлы Java в файлы классов. Инструмент Jar ( jar) создает настоящие банки. Если пользовательский манифест не указан, по умолчанию будет указано, какая версия JDK использовалась для создания jar-файла.

Jackrabbit
источник
3
Хотя это правда, это не дает ответа на вопрос.
zb226
2
@ zb226 AFAIK версия компилятора используется для компиляции классов не может быть получена из файлов классов, только версия , что классы были составлены для . Информация, которую я предоставил, предоставляет единственную (стандартную) информацию о том, какая версия создала артефакт. Если это не то, что спрашивают, вопрос следует перефразировать.
Jackrabbit
Хорошо, я смотрел на это с прагматической точки зрения, но теперь я вижу вашу точку зрения, пониженное голосование удалено :)
zb226
9

Поскольку мне нужно было анализировать толстые банки, меня интересовала версия каждого отдельного класса в файле jar. Поэтому я выбрал подход Джо Ливерседжа https://stackoverflow.com/a/27877215/1497139 и соединил его с таблицей версий номера класса Дэвида Дж. Лизевского ( 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
...

Баш сценарий ярв

#!/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 
Вольфганг Фаль
источник
1
Отличный сценарий! Я был в состоянии проверить Java-версии банок.
ARK
Большое спасибо за то, что поделились сценарием! Просто намек с моей стороны. head -1может использоваться вместе со сценарием: обычно достаточно увидеть версию первого класса в файле jar.
Сергей Брунов
7

Вы можете найти версию компилятора Java из файлов .class, используя Hex Editor.

Шаг 1: Извлеките файлы .class из jar-файла, используя zip-экстрактор

Шаг 2: откройте файл .class с помощью шестнадцатеричного редактора. (Я использовал плагин hexpad редактора notepad ++. Этот плагин читает файл как двоичный файл и показывает его в шестнадцатеричном формате). введите описание изображения здесь

Индексы 6 и 7 дают основной номер версии используемого файла класса. https://en.wikipedia.org/wiki/Java_class_file

Java SE 11 = 55 (0x37 hex)

Java SE 10 = 54 (0x36 hex)

Java SE 9 = 53 (0x35 hex)

Java SE 8 = 52 (0x34 hex),

Java SE 7 = 51 (0x33 hex),

Java SE 6.0 = 50 (0x32 hex),

Java SE 5.0 = 49 (0x31 hex),

JDK 1,4 = 48 (гекс 0x30),

JDK 1,3 = 47 (гекс 0x2F),

JDK 1,2 = 46 (гекс 0x2E),

JDK 1.1 = 45 (0x2D hex).

каит беяз
источник
4

Вы можете узнать двоичную версию Java, проверив первые 8 байтов (или используя приложение, которое может).

Насколько мне известно, сам компилятор не вставляет никакой идентификационной подписи. Так или иначе, я не могу обнаружить такую ​​вещь в формате класса спецификации файловой VM .

Макдауэлл
источник
Эти команды обеспечивают версию целевой виртуальной машины Java, а не javacверсия , которая составлена .class - файлы, что и было запрошено.
маркиз Лорн
4

Код отправленный Оуэн может сказать вам информацию , указанную в ряде других ответов здесь:

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, чтобы проверить, для чего была скомпилирована каждая из моих зависимостей.

JasonPlutext
источник
Эти команды обеспечивают версию целевой виртуальной машины Java, а не javacверсия , которая составлена .class - файлы, что и было запрошено.
маркиз Лорн
3

Разработчики и администраторы, использующие 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.

РЕДАКТИРОВАТЬ Как указывает крольчатник , вы не можете на 100% полагаться на манифест, чтобы сказать вам что-нибудь полезное. Если это так, то вы можете извлечь его в вашей любимой оболочке UNIX с помощью 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 не имеет ничего полезного в манифесте о содержащихся классах.

Джо Ливерседж
источник
Эти команды обеспечивают версию целевой виртуальной машины Java, а не javacверсия , которая составлена .class - файлы, что и было запрошено.
маркиз Лорн
3

Один лайнер (Linux)

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

Это печатает содержимое MANIFEST.MFфайла на стандартный вывод (надеюсь, он есть в вашем jar-файле :)

В зависимости от того, что собрало ваш пакет, вы найдете версию Created-Byили Build-Jdkключ JDK .

Нил Сюн
источник
3
Обратите внимание, что эти поля не обязаны присутствовать MANIFEST.MF, равно как и нет никаких обязательств, чтобы значения были правильными (Да, я однажды встречал, .jarгде значение было фиктивным).
zb226
Эти команды обеспечивают версию целевой виртуальной машины Java, а не javacверсия , которая составлена .class - файлы, что и было запрошено.
маркиз Лорн
2

В каждом файле класса есть номер версии, встроенный для уровня байтового кода, который JVM использует, чтобы увидеть, нравится ли ему этот конкретный фрагмент байтового кода или нет. Это 48 для Java 1.4, 49 для Java 1.5 и 50 для Java 6.

Существует много компиляторов, которые могут генерировать байт-код на каждом уровне, javac использует опцию «-target», чтобы указать, какой уровень байт-кода генерировать, а Java 6 javac может генерировать байт-код как минимум для 1.4, 1.5 и 6. Я не Полагайте, что компилятор вставляет все, что может идентифицировать сам компилятор, что, как я думаю, вы просите. Кроме того, все чаще используется компилятор Eclipse, так как это единственный jar, который может работать только с JRE.

В файле jar обычно много классов, и каждый из них является независимым, поэтому вам нужно изучить все классы в jar, чтобы быть уверенным в характеристиках содержимого.

Турбьерн Равн Андерсен
источник
1

В ответ на ответ @David J. Liszewski я выполнил следующие команды для извлечения манифеста jar-файла в Ubuntu:

# 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
Алан
источник
2
Или, как однострочник:unzip -p LiceneSearch.jar META-INF/MANIFEST.MF
Огр Псалом33
Эти команды предоставляют целевую версию JVM, а не javacверсию, скомпилировавшую файлы .class, о чем и просили.
маркиз Лорн
1

В большинстве случаев вы можете просматривать целые jar-файлы или war-файлы, которые содержат много jar-файлов в дополнение к самим себе.

Поскольку я не хотел проверять каждый класс вручную, я написал 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 смогут ЗАГРУЗИТЬ классы, и это, вероятно, то, с чего вы хотели начать.

Nthalk
источник
1

Чтобы расширить ответы Джонатона Фауста и МакДауэлла : Если вы работаете в системе * nix, вы можете использовать od(одну из самых ранних программ Unix 1, которая должна быть доступна практически везде) для запроса .classфайла на двоичном уровне:

od -An -j7 -N1 -t dC SomeClassFile.class

Это выведет знакомые целочисленные значения, например, 50для Java 5, 51для Java 6и так далее.

1 Цитата из https://en.wikipedia.org/wiki/Od_(Unix)

zb226
источник
Эти команды предоставляют целевую версию JVM, а не javacверсию, скомпилировавшую файлы .class, о чем и просили.
маркиз Лорн
1

Я также написал свой собственный скрипт bash для вывода версии Java, требуемой для всех jar-файлов, передаваемых в командной строке ... У меня немного грубовато, но у меня работает ;-)

пример использования

$ 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
Брэд Паркс
источник
1

Вы можете легко сделать это в командной строке, используя следующий процесс:

Если вам известно имя класса в 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
Shweta
источник
0

Вы проверяете в файле манифеста примера jar:

Версия манифеста: 1.0 Создано: 1.6.0 (IBM Corporation)

amoljdv06
источник
-1

В Windows сделайте следующее:

  1. Разархивируйте или распакуйте файл JAR с помощью команды WinZip / Java JAR.
  2. Перетащите один из файлов классов в ваш проект Eclipse Java.
  3. Откройте файл класса.

Теперь Eclipse покажет точную мажорную и минорную версию.

Kittu
источник
-1

Я строю небольшой скрипт bash (на github), основанный на предложении Davids, используя fileкоманду

beardedN5rd
источник