Quelle version de javac a construit mon pot?


212

Comment savoir quelle version du compilateur Java a été utilisée pour créer un bocal? J'ai un fichier jar, et il aurait pu être construit dans l'un des trois JDK. Nous devons savoir exactement lequel, afin de pouvoir certifier la compatibilité. La version du compilateur est-elle intégrée quelque part dans les fichiers de classe ou le bocal?


8
Vous pouvez dire le verson majeur en regardant le fichier manifeste. Vous pouvez indiquer la version cible en consultant les fichiers de classe eux-mêmes, mais les JDK peuvent produire des fichiers de classe pour les versions antérieures de java à l'aide de l'option -target, donc regarder les premiers octets peut ne pas être précis.
Peter Lawrey

15
Dans MANIFEST.MF, vous pouvez trouver quelque chose commeCreated-By: 1.7.0_13 (Oracle Corporation)
hko19

1
On dirait que Maven 3 fait ça Created-By: Apache MavenetBuild-Jdk: 1.8.0_25
Neal Xiong

Réponses:


89

Vous ne pouvez pas dire à partir du fichier JAR lui-même, nécessairement.

Téléchargez un éditeur hexadécimal et ouvrez l'un des fichiers de classe à l'intérieur du JAR et examinez les décalages d'octets 4 à 7. Les informations de version sont intégrées.

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

Remarque: Comme mentionné dans le commentaire ci-dessous,

ces octets vous indiquent pour quelle version la classe a été compilée, pas pour quelle version elle a été compilée.


41
Pour être pédant, ces octets vous indiquent pour quelle version la classe a été compilée, pas pour quelle version elle a été compilée. Java vous permet de compiler du code afin qu'il soit compatible avec les versions antérieures de Java. Cependant, cela ne s'applique qu'au code d'octet et au format. Il compilera avec plaisir du code qui référence les bibliothèques JDK 6 dans un format JDK 5, par exemple. Le JDK 5 chargera la classe, mais ne peut pas l'exécuter car la bibliothèque JDK 5 n'a pas le code référencé à partir du JDK 6.
Will Hartung

6
Nous pourrions le trouver dans le fichier manifeste comme créé par: 1.7.0_21-b11 (Oracle Corporation)
Krishna

8
L'autre réponse indique comment vérifier facilement via la ligne de commande
mgarciaisaia

313

A jarn'est qu'un conteneur. Il s'agit d'une archive de fichiers à la tar. Même si un jarpeut contenir des informations intéressantes dans sa hiérarchie META-INF , il n'a aucune obligation de spécifier le millésime des classes dans son contenu. Pour cela, il faut examiner les classfichiers qui s'y trouvent.

Comme Peter Lawrey l'a mentionné dans le commentaire de la question d'origine, vous ne pouvez pas nécessairement savoir quelle version JDK a construit un classfichier donné , mais vous pouvez trouver la version de la classe de code octet du classfichier contenu dans a jar.

Oui, c'est un peu nul, mais la première étape consiste à extraire une ou plusieurs classes du jar. Par exemple:

$ jar xf log4j-1.2.15.jar

Sous Linux, Mac OS X ou Windows avec Cygwin installé, la commande file (1) connaît la version de la classe.

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

Ou bien, utiliser à javappartir du JDK comme @ jikes.thunderbolt indique avec justesse:

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

Et si vous êtes relégué dans un Windowsenvironnement sans fileougrep

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

FWIW, je conviens que javapcela en dira beaucoup plus sur une donnéeclass fichier que la question initiale posée.

Quoi qu'il en soit, une version de classe différente, par exemple:

$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0

Le numéro majeur de version de classe correspond aux versions JDK Java suivantes:

  • 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
Ma version de filene montre pas, mais j'ai pu vérifier la classe manuellement avec cette commande: hexdump ~/bin/classes/P.class | head. Regardez simplement le huitième octet et convertissez-le en décimal.
Jarett Millard

2
Le fichier FYI semble dépendre de la version Java pour savoir s'il affichera également la version JDK. Sur CentOS / Oracle Linux et une classe compilée Java 6, j'obtiens "données de classe Java compilées, version 50.0 (Java 1.6)", mais lorsque je l'exécute sur une classe compilée avec Java 7, j'obtiens simplement la version générique "données de classe Java compilées, version 51.0 "
Dan Haynes

1
JAR=something.jar ; unzip -p $JAR `unzip -l $JAR | grep '\.class$' | head -1` | file -
Randall Whitman

3
@JarettMillard mon cygwin file file.class(5.22-1) n'a pas montré la cible de classe Java initialement: "file.class: [architecture = 6909806] [architecture = 6845039]". Cependant file -k file.class, cela: "file.class: [architecture = 6909806] [architecture = 6845039] données de classe Java compilées, version 50.0 (Java 1.6)". On dirait qu'il filen'a obtenu que le premier match dans sa base de données magicfiles sans -k.
dim

1
L'utilisation de "javap -p <classe> | grep major" n'est pas fiable. Si un pom.xml a une source / cible de 1,7, javap vous donnera toujours la "version principale: 51", que vous utilisiez JDK 1.7, JDK 1.8 ou JDK 1.9 pour compiler.
user2569618

54

Voici la façon dont Java trouve ces informations.

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

Par exemple:
> javap -v Application | findstr major   major version: 51


3
de toutes les réponses données, votre réponse est la plus concise et n'implique pas d'écrire 10 lignes de code pour connaître les informations de version. +1 pour cela
Thirumalai Parthasarathi

2
À quoi sert le <class>paramètre?
Dims

1
@Dims Le fichier ".class" dont vous essayez de trouver la version. Dans son exemple, il a un Application.classfichier et il s'est avéré être compilé pour Java 7 ( major version: 51).
inanutshellus

1
Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne

15

Il n'est pas nécessaire de décompresser le fichier JAR (si l'un des noms de classe est connu ou recherché, par exemple en utilisant 7zip), donc sous Windows, les éléments suivants seraient suffisants:

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

1
Cela fonctionne aussi sur Linux (sauf que vous utiliseriez grep au lieu de findstr bien sûr)
Michael Rusch

1
Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne

15

Le compilateur Java ( javac) ne construit pas de fichiers JAR, il traduit les fichiers Java en fichiers de classe. L'outil Jar ( jar) crée les pots réels. Si aucun manifeste personnalisé n'a été spécifié, le manifeste par défaut spécifiera quelle version du JDK a été utilisée pour créer le bocal.


3
Tout en étant vrai, cela ne fournit pas de réponse à la question.
zb226

2
@ zb226 la version du AFAIK du compilateur utilisé pour compiler les classes ne peut être obtenu à partir des fichiers de classe, seule la version que les classes ont été compilées pour . Les informations que j'ai fournies fournissent les seules informations (standard) disponibles sur la version qui a construit l'artefact. Si ce n'est pas ce qui est demandé, la question devrait être reformulée.
jackrabbit

D'accord, je regardais cela d'un point de vue pragmatique, mais je vois votre point maintenant, le downvote a été supprimé :)
zb226

9

Comme j'avais besoin d'analyser des pots de graisse, j'étais intéressé par la version de chaque classe individuelle dans un fichier de pot. Par conséquent, j'ai adopté l'approche Joe Liversedge https://stackoverflow.com/a/27877215/1497139 et l' ai combinée avec la table de version du numéro de classe https://stackoverflow.com/a/3313839/1497139 de David J. Liszewski pour créer un script bash jarv pour afficher les versions de tous les fichiers de classe dans un fichier jar.

usage

usage: ./jarv jarfile
 -h|--help: show this usage

Exemple

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

1
Grand script! J'ai pu vérifier les versions java des pots.
ARK

Merci beaucoup d'avoir partagé le script! Juste un indice de mon côté. head -1peut être utilisé en conjonction avec le script: généralement, il suffit de voir la version de la première classe dans le fichier jar.
Sergey Brunov

7

vous pouvez trouver la version du compilateur Java à partir de fichiers .class à l'aide d'un éditeur hexadécimal.

Étape 1: Extraire les fichiers .class du fichier jar à l'aide d'un extracteur zip

étape 2: ouvrir le fichier .class avec un éditeur hexadécimal. (J'ai utilisé le plug-in de l'éditeur hexadécimal notepad ++. Ce plugin lit le fichier en binaire et l'affiche en hexadécimal) Vous pouvez voir ci-dessous. entrez la description de l'image ici

Les index 6 et 7 indiquent le numéro de version principal du format de fichier de classe utilisé. 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 hex),

JDK 1,3 = 47 (hex2F2),

JDK 1.2 = 46 (0x2E hex),

JDK 1.1 = 45 (hex2D 0xD).



4

Le code affiché par Owen peut vous indiquer les informations mentionnées par un certain nombre d'autres réponses ici:

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

Voir aussi ceci et ce site. J'ai fini par modifier le code Mind Products rapidement pour vérifier à quoi chacune de mes dépendances a été compilée.


Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne

3

Les développeurs et administrateurs exécutant Bash peuvent trouver ces fonctions pratiques utiles:

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

Vous pouvez les coller pour une utilisation unique ou les ajouter à ~/.bash_aliasesou ~/.bashrc. Les résultats ressemblent à:

$ jar_jdk_version poi-ooxml-3.5-FINAL.jar
49

et

$ print_jar_jdk_version poi-ooxml-3.5-FINAL.jar
poi-ooxml-3.5-FINAL.jar contains classes compiled with JDK version 1.5.

EDIT Comme le souligne jackrabbit , vous ne pouvez pas vous fier à 100% au manifeste pour vous dire quoi que ce soit d'utile. Si c'est le cas, vous pouvez le retirer dans votre shell UNIX préféré avec 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

Ce .jar n'a rien d'utile dans le manifeste sur les classes contenues.


Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne

3

Un liner (Linux)

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

Ceci imprime le contenu de MANIFEST.MF fichier sur stdout (j'espère qu'il y en a un dans votre fichier jar :)

En fonction de ce qui a construit votre package, vous trouverez la version JDK dans Created-Byou la Build-Jdkclé.


3
Notez qu'il n'y a aucune obligation pour ces champs d'être présents dans MANIFEST.MF, ainsi que pour les valeurs d'être correctes (Oui, j'ai déjà rencontré un .jaroù la valeur était fausse).
zb226

Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne

2

Chaque fichier de classe a un numéro de version incorporé pour le niveau de code octet que la JVM utilise pour voir si elle aime ou non ce bloc de code octet particulier. Il s'agit de 48 pour Java 1.4, 49 pour Java 1.5 et 50 pour Java 6.

De nombreux compilateurs existent qui peuvent générer du code d'octets à chaque niveau, javac utilise l'option "-target" pour indiquer le niveau de code d'octets à générer, et le javac Java 6 peut générer du code d'octets pour au moins 1.4, 1.5 et 6. Je ne le fais pas crois que le compilateur insère tout ce qui peut identifier le compilateur lui-même, ce que je pense que vous demandez. De plus, le compilateur Eclipse est de plus en plus utilisé, car il s'agit d'un seul pot qui ne peut fonctionner qu'avec le JRE.

Dans un fichier jar, il y a généralement de nombreuses classes, et chacune d'entre elles est indépendante, vous devez donc étudier toutes les classes du jar pour être certain des caractéristiques du contenu.


1

Suite à la réponse de @David J. Liszewski, j'ai exécuté les commandes suivantes pour extraire le manifeste du fichier jar sur 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
Ou, en une ligne:unzip -p LiceneSearch.jar META-INF/MANIFEST.MF
Ogre Psalm33

Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne du

1

La plupart du temps, vous pouvez consulter des fichiers jar entiers ou des fichiers war contenant en plus d'eux-mêmes de nombreux fichiers jar.

Parce que je ne voulais pas vérifier manuellement chaque classe, j'ai écrit un programme java pour le faire:

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

Bien que cela ne dise pas avec quoi la classe a été compilée, cela détermine quels JDK pourront CHARGER les classes, ce qui est probablement ce que vous vouliez commencer.


1

Pour développer les réponses de Jonathon Faust et McDowell : Si vous êtes sur un système basé sur * nix, vous pouvez utiliser od(l'un des premiers programmes Unix 1 qui devrait être disponible pratiquement partout) pour interroger le .classfichier au niveau binaire:

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

Cela produira les valeurs entières familières, par exemple 50pour Java 5, 51pour Java 6et ainsi de suite.

1 Citation de https://en.wikipedia.org/wiki/Od_(Unix)


Ces commandes fournissent la version JVM cible, pas la javacversion qui a compilé les fichiers .class, ce qui était demandé.
Marquis de Lorne du

1

J'ai également écrit mon propre script bash pour vider la version Java requise par tous les pots passés en ligne de commande ... Le mien est un peu rude, mais fonctionne pour moi ;-)

exemple d'utilisation

$ 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

Vous pouvez facilement le faire sur la ligne de commande en utilisant le processus suivant:

Si vous connaissez l'un des noms de classe dans jar, vous pouvez utiliser la commande suivante:

javap -cp jarname.jar -verbose packagename.classname | findstr major

exemple :

    C:\pathwherejarlocated> javap -cp jackson-databind-2.8.6.jar -verbose com.fasterxml.jackson.databind.JsonMappingException | findstr major

Production :

    major version: 51

Référence rapide :

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: si vous ne connaissez aucun nom de classe, vous pouvez facilement le faire en utilisant l'un des décompilateurs jar ou en utilisant simplement la commande suivante pour extraire le fichier jar:

jar xf myFile.jar

0

Vous archivez le fichier manifeste de l'exemple de jar:

Version du manifeste: 1.0 Créé par: 1.6.0 (IBM Corporation)


-1

Sous Windows, procédez comme suit:

  1. Décompressez ou extrayez le fichier JAR à l'aide de la commande WinZip / Java JAR.
  2. Faites glisser et déposez l'un des fichiers de classe dans votre projet Java Eclipse.
  3. Ouvrez le fichier de classe.

Maintenant, Eclipse affichera la version exacte majeure et mineure.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.