Développons en Java 2.30 | |
Copyright (C) 1999-2022 Jean-Michel DOUDOUX | (date de publication : 15/06/2022) |
|
Niveau : | Fondamental |
Ce chapitre présente quelques concepts de base utilisés en Java relatifs à la compilation et l'exécution d'applications, notamment, les notions de classpath, de packages et d'archives de déploiement jar.
Ce chapitre contient plusieurs sections :
La plate-forme Java utilise quelques notions de base lors de sa mise en oeuvre, notamment :
Un programme Java est composé d'un ou plus généralement plusieurs fichiers source. N'importe quel éditeur de texte peut être utilisé pour éditer un fichier source Java.
Ces fichiers source possèdent l'extension .java. Ils peuvent contenir une ou plusieurs classes ou interfaces mais il ne peut y avoir qu'une seule classe ou interface déclarée publique par fichier. Le nom de ce fichier source doit obligatoirement correspondre à la casse près au nom de cette entité publique suivi de l'extension .java
Il est nécessaire de compiler le source pour le transformer en J-code ou bytecode Java qui sera lui exécuté par la machine virtuelle. Pour être compilé, le programme doit être enregistré au format de caractères Unicode : une conversion automatique est faite par le JDK si nécessaire.
Un compilateur Java, par exemple l'outil javac fourni avec le JDK est utilisé pour compiler chaque fichier source en fichier de classe possédant l'extension .class. Cette compilation génère pour chaque fichier source un ou plusieurs fichiers .class qui contiennent du bytecode.
Exemple : |
public class MaClasse {
public static void main(String[] args) {
System.out.println("Bonjour");
}
} |
Résultat : |
C:\TEMP>javac MaClasse.java
C:\TEMP>dir MaClas*
Volume in drive C has no label.
Volume Serial Number is 1E06-2R43
Directory of C:\TEMP
31/07/2007 13:34 417 MaClasse.class
31/07/2007 13:34 117 MaClasse.java |
Le compilateur génère autant de fichiers .class que de classes et interfaces définies dans chaque fichier source.
Exemple : |
public class MaClasse {
public static void main(String[] args) {
System.out.println("Bonjour");
}
}
class MonAutreClasse {
public static void afficher(String message) {
System.out.println(message);
}
} |
Résultat : |
C:\TEMP>dir *.class
Volume in drive C has no label.
Volume Serial Number is 1E06-2R43
Directory of C:\TEMP
31/07/2007 13:40 417 MaClasse.class
31/07/2007 13:40 388 MonAutreClasse.class |
Pour exécuter une application, la classe servant de point d'entrée doit obligatoirement contenir une méthode ayant la signature public static void main(String[] args). Il est alors possible de fournir cette classe à la JVM qui va charger le ou les fichiers .class utiles à l'application et exécuter le code.
Exemple : |
C:\TEMP>java MaClasse
Bonjour |
Pour les classes anonymes, le compilateur génère un nom de fichier constitué du nom de la classe englobante suffixé par $ et un numéro séquentiel.
Exemple : |
import javax.swing.JFrame;
import java.awt.event.*;
public class MonApplication {
public static void main(String[] args) {
MaFenetre f = new MaFenetre();
f.afficher();
}
}
class MaFenetre {
JFrame mainFrame = null;
public MaFenetre() {
mainFrame = new JFrame();
mainFrame.setTitle("Mon application");
mainFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
});
mainFrame.setSize(320, 240);
}
public void afficher() {
mainFrame.setVisible(true);
}
} |
Résultat : |
C:\TEMP>javac MonApplication.java
C:\TEMP>dir *.class
Volume in drive C has no label.
Volume Serial Number is 1E06-2R43
Directory of C:\TEMP
31/07/2007 13:50 494 MaFenetre$1.class
31/07/2007 13:50 687 MaFenetre.class
31/07/2007 13:50 334 MonApplication.class |
Une classe anonyme peut elle-même définir une classe : dans ce cas le nom du fichier de classe sera celui de la classe anonyme suffixé par le caractère $ et le nom de la classe
Exemple : |
import javax.swing.JFrame;
import java.awt.event.*;
public class MonApplication {
public static void main(String[] args) {
MaFenetre f = new MaFenetre();
f.afficher();
}
}
class MaFenetre {
JFrame mainFrame = null;
public MaFenetre() {
mainFrame = new JFrame();
mainFrame.setTitle("Mon application");
mainFrame.addWindowListener(new WindowAdapter() {
class MonAutreClasse {
public void afficher(String message) {
System.out.println(message);
}
}
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
});
mainFrame.setSize(320, 240);
}
public void afficher() {
mainFrame.setVisible(true);
}
} |
Résultat : |
C:\TEMP>javac MonApplication.java
C:\TEMP>dir *.class
Volume in drive C has no label.
Volume Serial Number is 1E06-2R43
Directory of C:\TEMP
31/07/2007 13:53 549 MaFenetre$1$MonAutreClasse.class
31/07/2007 13:53 555 MaFenetre$1.class
31/07/2007 13:53 687 MaFenetre.class
31/07/2007 13:53 334 MonApplication.class |
Les fichiers sources peuvent être organisés en packages. Les packages définissent une hiérarchie de noms, chaque nom étant séparé par le caractère point. Le nom d'un package est lié à une arborescence de sous-répertoires correspondant à ce nom.
Ceci permet de structurer les sources d'une application car une application peut rapidement contenir plusieurs centaines voire milliers de fichiers source. Les packages permettent aussi d'assurer l'unicité d'une classe grâce à son nom pleinement qualifié (nom du package suivi du caractère «.» suivi du nom de la classe).
L'API Java est organisée en packages répartis en trois grands ensembles :
Les principaux packages standards de Java 6 sont :
java.applet |
Création d'applets |
java.awt |
Création d'interfaces graphiques avec AWT |
java.io |
Accès aux flux entrants et sortants |
java.lang |
Classes et interfaces fondamentales |
java.math |
Opérations mathématiques |
java.net |
Accès aux réseaux |
java.nio |
API NIO |
java.rmi |
API RMI (invocation de méthodes distantes) |
java.security |
Mise en oeuvre de fonctionnalités concernant la sécurité |
java.sql |
API JDBC (accès aux bases de données) |
java.util |
Utilitaires (collections, internationalisation, logging, expressions régulières,...). |
Les principaux packages d'extensions de Java 6 sont :
javax.crypto |
Cryptographie |
javax.jws |
Services web |
javax.management |
API JMX |
javax.naming |
API JNDI (Accès aux annuaires) |
javax.rmi |
RMI-IIOP |
javax.script |
API Scripting |
javax.security |
Authentification et habilitations |
javax.swing |
API Swing pour le développement d'interfaces graphiques |
javax.tools |
Api pour l'accès à certains outils comme le compilateur par exemple |
javax.xml.bind |
API JAXB pour la mapping objet/XML |
javax.xml.soap |
Création de messages SOAP |
javax.xml.stream |
API StAX (traitement de documents XML) |
javax.xml.transform |
Transformation de documents XML |
javax.xml.validation |
Validation de documents XML |
javax.xml.ws |
API JAX-WS (service web) |
Les principaux packages tiers de Java 6 sont :
org.omg.CORBA |
Mise en oeuvre de CORBA |
org.w3c.dom |
Traitement de documents XML avec DOM |
org.xml.sax |
Traitement de documents XML avec SAX |
Le package est précisé dans le fichier source grâce à l'instruction package. Le fichier doit donc, dans ce cas, être stocké dans une arborescence de répertoires qui correspond au nom du package.
Exemple : |
package fr.jmdoudoux.dej;
public class MaCLasseTest {
public static void main() {
System.out.println("Bonjour");
}
} |
Si les sources de l'application sont dans le répertoire C:\Documents and Settings\jm\workspace\Tests, alors le fichier MaCLasseTest.java doit être dans le répertoire C:\Documents and Settings\jm\workspace\Tests\com\jmdoudoux\test.
Si aucun package n'est précisé, alors c'est le package par défaut (correspondant au répertoire courant) qui est utilisé. Ce n'est pas une bonne pratique d'utiliser le package par défaut sauf pour des tests.
Dans le code source, pour éviter d'avoir à utiliser les noms pleinement qualifiés des classes, il est possible d'utiliser l'instruction import suivi d'un nom de package suivi d'un caractère «.» et du nom d'une classe ou du caractère «*»
Exemple : |
import javax.swing.JFrame;
import java.awt.event.*; |
Remarque : par défaut le package java.lang est toujours importé par le compilateur.
Il est possible de créer une enveloppe qui va contenir tous les fichiers d'une application Java ou une portion de cette application dans un fichier .jar (Java archive). Ceci inclus : l'arborescence des packages, les fichiers .class, les fichiers de ressources (images, configuration, ...), ... Un fichier .jar est physiquement une archive de type Zip qui contient tous ces éléments.
L'outil jar fourni avec le jdk permet de manipuler les fichiers jar.
Exemple : |
C:\TEMP>jar cvf MonApplication.jar *.class
manifest ajoutÚ
ajout : MaFenetre$1$MonAutreClasse.class (entrÚe = 549) (sortie = 361) (34% comp
ressÚs)
ajout : MaFenetre$1.class (entrÚe = 555) (sortie = 368) (33% compressÚs)
ajout : MaFenetre.class (entrÚe = 687) (sortie = 467) (32% compressÚs)
ajout : MonApplication.class (entrÚe = 334) (sortie = 251) (24% compressÚs) |
Le fichier .jar peut alors être diffusé et exécuté s'il contient au moins une classe avec une méthode main().
Exemple : déplacement du jar pour être sûre qu'il n'utilise pas de classe du répertoire et exécution |
C:\TEMP>copy MonApplication.jar ..
1 file(s) copied.
C:\TEMP>cd ..
C:\>java -cp MonApplication.jar MonApplication |
Remarque : un fichier .jar peut contenir plusieurs packages.
Le fichier jar peut inclure un fichier manifest qui permet de préciser des informations d'exécution sur le fichier jar (classe principale à exécuter, classpath, ...) : ceci permet d'exécuter directement l'application en double-cliquant sur le fichier .jar.
A l'exécution, la JVM et les outils du JDK recherchent les classes requises dans :
Important : il n'est pas recommandé d'ajouter des classes ou des bibliothèques dans les sous-répertoires du JDK.
La notion de classpath est importante car elle est toujours utilisée quel que soit l'emploi qui est fait de Java (ligne de commandes, IDE, script Ant, ...). Le classpath est sûrement la notion de base qui pose le plus de problèmes aux développeurs inexpérimentés en Java mais sa compréhension est absolument nécessaire.
Le classpath permet de préciser au compilateur et à la JVM où ils peuvent trouver les classes dont ils ont besoin pour la compilation et l'exécution d'une application. C'est un ensemble de chemins vers des répertoires ou des fichiers .jar dans lequel l'environnement d'exécution Java recherche les classes (celles de l'application mais aussi celles de tiers) et éventuellement des fichiers de ressources utiles à l'exécution de l'application. Ces classes ne concernent pas celles fournies par l'environnement d'exécution incluses dans le fichier rt.jar qui est implicitement utilisé par l'environnement.
Le classpath est constitué de chemins vers des répertoires et/ou des archives sous la forme de fichiers .jar ou .zip. Chaque élément du classpath peut donc être :
Les éléments du classpath qui ne sont pas des répertoires ou des fichiers .jar ou .zip sont ignorés.
Ces chemins peuvent être absolus ou relatifs. Chaque chemin est séparé par un caractère spécifique au système d'exploitation utilisé : point-virgule sous Windows et deux-points sous Unix par exemple.
Exemple sous Windows : |
.;C:\java\tests\bin;C:\java\lib\log4j-1.2.11.jar;"C:\Program Files\tests\tests.jar" |
Dans cet exemple, le classpath est composé de quatre entités :
Remarque : sous Windows, il est possible d'utiliser le caractère / ou \ comme séparateur d'arborescence de répertoires.
Par défaut, si aucun classpath n'est défini, le classpath est composé uniquement du répertoire courant. Une redéfinition du classpath (avec l'option -classpath ou -cp ou la variable d'environnement système CLASSPATH) inhibe cette valeur par défaut.
La recherche d'une classe se fait dans l'ordre des différents chemins du classpath : cet ordre est donc important surtout si une bibliothèque est précisée dans deux chemins. Dans ce cas, c'est le premier trouvé dans l'ordre précisé qui sera utilisé, ce qui peut être à l'origine de problèmes.
Le classpath peut être défini à plusieurs niveaux :
Exemple sous Windows
Il faut utiliser la commande set pour définir la variable d'environnement CLASSPATH. Le séparateur entre chaque élément du classpath est le caractère point-virgule. Il ne faut pas mettre d'espace de part et d'autre du signe égal.
Exemple : |
set CLASSPATH=C:\java\classes;C:\java\lib;C:\java\lib\mysql.jar;. |
Sous Windows 9x : il est possible d'ajouter une ligne définissant la variable d'environnement dans le fichier autoexec.bat :
Exemple : |
set CLASSPATH=.;c:\java\lib\mysql.jar;%CLASSPATH% |
Sous Windows NT/2000/XP : il faut lancer l'application démarrer/paramètre/panneau de configuration/système, ouvrir l'onglet "avancé" et cliquer sur le bouton "Variables d'environnement". Il faut ajouter ou modifier la variable CLASSPATH avec comme valeur les différents éléments du classpath séparés chacun par un caractère point virgule.
Exemple sous Unix (interpréteur bash) :
Exemple : |
CLASSPATH=.:./lib/log4j-1.2.11.jar
export CLASSPATH; |
L'utilisation de la variable système CLASSPATH est pratique car elle évite d'avoir à définir le classpath pour compiler ou exécuter mais c'est une mauvaise pratique car cela peut engendrer des problèmes :
Si la JVM ou le compilateur n'arrive pas à trouver une classe dans le classpath, une exception de type java.lang.ClassNotFoundException à la compilation ou java.lang.NoClassDefFoundError à l'exécution est levée.
Exemple : |
package fr.jmdoudoux.dej;
public class MaCLasseTest {
public static void main() {
System.out.println("Bonjour");
}
} |
Le fichier MaCLassTest.class issu de la compilation est stocké dans le répertoire C:\Documents and Settings\jm\workspace\Tests\com\jmdoudoux\test
En débutant en Java, il est fréquent de se placer dans le répertoire qui contient le fichier .class et de lancer la JVM.
Exemple : |
C:\Documents and Settings\jmd\workspace\Tests\com\jmdoudoux\test>java MaCLasseTe
st
Exception in thread "main" java.lang.NoClassDefFoundError: MaCLasseTest (wrong n
ame: com/jmdoudoux/test/MaCLasseTest)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$000(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source) |
Cela ne fonctionne pas car la JVM cherche à partir du répertoire courant (défini dans le classpath par défaut) une classe qui soit définie dans le package par défaut (aucun nom de package précisé). Hors dans l'exemple, la classe est définie dans le package fr.jmdoudoux.dej.
Une autre erreur assez fréquente est de se déplacer dans le répertoire qui contient le premier répertoire du package
Exemple : |
C:\Documents and Settings\jm\workspace\Tests\fr\jmdoudoux\test>cd ../../..
C:\Documents and Settings\jm\workspace\Tests>java MaCLasseTest
Exception in thread "main" java.lang.NoClassDefFoundError: MaCLasseTest |
Dans ce cas, cela ne fonctionne pas car le nom de la classe n'est pas pleinement qualifié
Exemple : |
C:\Documents and Settings\jmd\workspace\Tests>java fr.jmdoudoux.dej.MaCL
asseTest
Bonjour |
En précisant le nom pleinement qualifié de la classe, l'application est exécutée.
Si le classpath est redéfini, il ne faut pas oublier d'ajouter le répertoire courant au besoin en utilisant le caractère point. Cette pratique n'est cependant pas recommandée.
Exemple : |
C:\Documents and Settings\jmd\workspace\Tests>java -cp test.jar fr.jmdoudouxde
j.MaCLasseTest
Exception in thread "main" java.lang.NoClassDefFoundError: com/jmdoudoux/test/Ma
CLasseTest
C:\Documents and Settings\jmd\workspace\Tests>java -cp test.jar;. fr.jmdoudoux
test.MaCLasseTest
Bonjour |
Les IDE fournissent tous des facilités pour gérer le classpath. Cependant en débutant, il est préférable d'utiliser les outils en ligne de commande pour bien comprendre le fonctionnement du classpath.
Dans cette section, une application est contenue dans le répertoire c:\java\tests. Elle est composée de la classe fr.jmdoudoux.dej.MaClasse.java.
Exemple : |
package fr.jmdoudoux.dej;
public class MaClasse {
public static void main(
String[] args) {
System.out.println("Bonjour");
}
} |
La structure des répertoires et fichiers de l'application est la suivante :
Pour compiler la classe MaClasse, il faut utiliser la commande :
Exemple : |
C:\java\tests>javac com/jmdoudoux/test/MaClasse.java |
Le fichier MaClasse.class est créé
Pour exécuter la classe, il faut utiliser la commande
Exemple : |
C:\java\tests>java fr.jmdoudoux.dej.MaClasse
Bonjour |
Remarque : il est inutile de spécifier le classpath puisque celui-ci n'est composé que du répertoire courant qui correspond au classpath par défaut.
Il est cependant possible de le préciser explicitement
Exemple : |
C:\java\tests>java -cp . fr.jmdoudoux.dej.MaClasse
Bonjour
C:\java\tests>java -cp c:/java/tests fr.jmdoudoux.dej.MaClasse
Bonjour |
Il est possible de définir le classpath en utilisant la variable d'environnement système CLASSPATH.
Exemple : le fichier run.bat |
@echo off
set CLASSPATH=c:/java/tests
javac com/jmdoudoux/test/MaClasse.java
java fr.jmdoudoux.dej.MaClasse |
Ce script redéfinit la variable CLASSPATH, exécute le compilateur javac et l'interpréteur java pour exécuter la classe. Ces deux commandes utilisent la variable CLASSPATH.
Exemple : |
C:\java\tests>run.bat
Bonjour |
L'exemple de cette section va utiliser la bibliothèque log4j.
Exemple : |
package fr.jmdoudoux.dej;
import org.apache.log4j.*;
public class MaClasse {
static Logger logger = Logger.getLogger(MaClasse.class);
public static void main(
String[] args) {
PropertyConfigurator.configure("log4j.properties");
logger.info("Bonjour");
}
}
|
Le fichier jar de log4j est stocké dans le sous-répertoire lib. Le fichier de configuration log4.properties est dans le répertoire principal de l'application puisqu'il est inclus dans le classpath
Il est nécessaire de préciser dans le classpath le répertoire tests et le fichier jar de log4j.
Exemple : |
C:\java\tests>javac -cp c:/java/tests;c:/java/tests/lib/log4j-1.2.11.jar fr/jmd
oudoux/dej/MaClasse.java
C:\java\tests>java -cp c:/java/tests;c:/java/tests/lib/log4j-1.2.11.jar fr.jmdo
udoux.dej.MaClasse
[main] INFO fr.jmdoudoux.dej.MaClasse - Bonjour |
Il est aussi possible d'utiliser la variable d'environnement système classpath.
Il est possible de préciser les bibliothèques requises dans le fichier manifest du fichier jar.
La propriété JAR-class-path va étendre le classpath mais uniquement pour les classes chargées à partir du jar. Les classes incluses dans le JAR-class-path sont chargées comme si elles étaient incluses dans le jar.
Exemple : le fichier manifest.mf |
Main-Class: fr.jmdoudoux.dej.MaClasse
Class-Path: lib/log4j-1.2.11.jar |
La clé Class-Path permet de définir le classpath utilisé lors de l'exécution.
Remarques importantes : Il faut obligatoirement que le fichier manifest se termine par une ligne vide. Pour préciser plusieurs entités dans le classpath, il faut les séparer par un caractère espace.
La structure des répertoires et des fichiers est la suivante :
Pour créer l'archive jar, il faut utiliser l'outil jar en précisant les options de création, le nom du fichier .jar, le fichier manifest et les entités à inclure dans le fichier jar.
Exemple : |
C:\java\tests>jar cfm tests.jar manifest.mf com log4j.properties |
Le fichier jar est créé
L'archive jar ne contient pas le sous-répertoire lib, donc il n'inclut pas la bibliothèque requise.
Pour exécuter l'application, il suffit d'utiliser l'interpréteur java avec l'option -jar
Exemple : |
C:\java\tests>java -jar tests.jar
[main] INFO fr.jmdoudoux.dej.MaClasse - Bonjour |
Attention : les entités précisées dans le classpath du fichier manifest doivent exister pour permettre l'exécution de l'application.
Exemple : |
C:\java\tests>rename lib libx
C:\java\tests>java -jar tests.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logg
er
at fr.jmdoudoux.dej.MaClasse.<clinit>(MaClasse.java:6)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 1 more |
Il suffit de créer une page HTML pouvant être très simple :
Exemple : |
<HTML>
<TITLE> test applet Java </TITLE>
<BODY>
<APPLET code="NomFichier.class" width="270" height="200">
</APPLET>
</BODY>
</HTML> |
Il faut ensuite visualiser la page créée dans l'appletviewer ou dans un navigateur 32 bits compatible avec la version de Java dans laquelle l'applet est écrite.
|