0% ont trouvé ce document utile (0 vote)
55 vues121 pages

Java Av Cour

Le document traite des principes d'entrée/sortie (E/S) en Java, en se concentrant sur la manipulation des flux de données, qu'ils proviennent de fichiers, de mémoire ou de pipes. Il décrit les classes du package java.io pour gérer les flux, les types de flux (binaire et texte), ainsi que les filtres pour optimiser la lecture et l'écriture. Enfin, il présente des méthodes pour manipuler des fichiers, comme la création, la suppression et la récupération d'informations sur les fichiers.

Transféré par

aziz1238mahdi
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
55 vues121 pages

Java Av Cour

Le document traite des principes d'entrée/sortie (E/S) en Java, en se concentrant sur la manipulation des flux de données, qu'ils proviennent de fichiers, de mémoire ou de pipes. Il décrit les classes du package java.io pour gérer les flux, les types de flux (binaire et texte), ainsi que les filtres pour optimiser la lecture et l'écriture. Enfin, il présente des méthodes pour manipuler des fichiers, comme la création, la suppression et la récupération d'informations sur les fichiers.

Transféré par

aziz1238mahdi
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Introduction et principe de E /S

Les applications ont très souvent besoin d’accéder à des ressources externes, qui
fournissent ou reçoivent des données de l’application (donc transite ou transfert de
données), comme :
– fichiers, mémoire, les pipes.

Le transfert de données, entre des applications ou entre application et des ressources


externes, est appelé flux (stream en anglais) de données.
– Un flux est en quelque sorte un canal dans lequel de l'information transite,
depuis la source vers la destination.

Manipuler un flux, pour tout type de flux, c’est :


Ouverture du flux,
Ecriture ou lecture des données dans le flux,
Fermeture du flux.
L’objectif de ce cours est de savoir comment faire transiter des données depuis une
source vers une destination
– ou simplement comment une application peut recevoir des données d’une
source (autre application, réseau, fichier ou autres)
– Ou comment une application peut envoyer des données à une destination (autre
application, réseau, fichier ou autres)
M.AFILAL 2

Introduction et principe de E /S
Introduction et principe de E /S

Les fichiers:
Java fournit des classes qui représentent les flux et permettent leur manipulation, l’ensemble
– se sont des fichiers localisés sur disque dure de ses classes constitue le package java.io (la bibliothèque des E/S).

La mémoire: – Certains types de flux agissent sur la façon dont sont traitées les données qui transitent
par leur intermédiaire :
– se sont des variables représentant: E/S bufférisées, traduction de données (d’un codage à autre par exemple), …
des caractères,
des tableaux de caractères, – Pour réaliser la gestion souhaitée pour les E / S, on peut combiner différents types de
flux.
des bytes ou tableaux de bytes,
des String – Généralement,
des StringBuffer
Si le flux provient d’une source extérieure (clavier, fichier...) vers le programme: on
parle alors de flux en lecture.
Les pipes:
– se sont des canaux qui permettent de : Si le flux vient du programme et écrit dans un fichier, à l’écran ou autre... : on parle
communiquer des informations entre deux programmes (ou deux alors de flux en écriture.
«threads») qui s’exécutent concurremment (notion de synchronisation)
le package java.io prend en charge de nombreux type de sources de données et d’opérations
sur ces données.
transiter des données à travers le réseau
M.AFILAL 3 M.AFILAL 4
Introduction et principe de E /S Introduction et principe de E /S

L’information lue ou écrite est représentée par un flux de données, deux types d’objets sont
utilisés dans ce but : Application

– Un flux physique,
il prend en charge le type de la source de données.
– Il sait, par exemple, comment lire le contenu d’un fichier.
Filtre de lecture Filtre d’écriture
– Un filtre,
il prend en charge la manière de gérer le flux:
Transmet le flux Retransmet le flux
– la manière de lire ou d’écrire les données.
Par exemple, pour optimiser la lecture.
Flux physique Flux physique
Remarque:
– Un filtre ne sait pas accéder à une source physique (fichier, mémoire, pipe), Lecture du flux Écriture du flux
il s’appuie sur un objet de type flux physique.
Source de données Destination de
données

M.AFILAL 5 M.AFILAL 6

Introduction et principe des E / S Introduction et principe de E /S

La bibliothèque des E\S propose quatre hiérarchies ou catégories de classes (des classes de
flux) qui permettent où qui dérivent des postulas suivants :

Des accès en mode binaire (lecture ou écriture octet par octet : 8 bits)

Des accès en mode texte (ou mode caractère, un caractère unicode: 16 bits en java).

Remarque: Les flux de lecture et des flux d’écriture sont différenciés (les classes de
lecture sont différents de celles de d’écriture).

Des classes complémentaires permettent de :

Récupérer dynamiquement des informations (droit d’accès, etc. …) sur un fichier.

Gérer « lecture/Ecriture» les fichiers en accès direct ou indirecte aux


données(lecture/Ecriture: séquentielle ou directe).

Prendre en charge les erreurs de lecture / écriture.

M.AFILAL 7 M.AFILAL 8
Introduction et principe de E /S Règles pour les flux des E /S

InputStream :
Pour accéder à une source de données, il faut déterminer :
– Classe qui gère le flux d’entrée « accès séquentiel en mode binaire en lecture (mode
octet) ». Le type de gestion :
– accès directe, lecture séquentielle ou écriture séquentielle,
OutputStream :
– Classe qui gère le flux de sortie « accès séquentiel en mode binaire en écriture (mode
octet) ». Le type d’accès :
– mode binaire ou mode caractère.
Reader :
– Classe qui permet un accès en lecture « accès séquentiel en lecture en mode caractère ». Le type de la source de données :
– fichier, mémoire, pipe:
Writer :
– Classe qui permet un accès en écriture « accès séquentiel en écriture en mode caractère ». disque dure, console, écran, etc.….,

RondomAccessFile : La manière de gérer le flux :


– Classe qui permet un accès directe (lecture/écriture) aux données dans un fichier. – choisir le filtre adéquat (peut être aucun).
Elle permet de lire ou d'écrire tous les types Java de base, les lignes, les chaînes de
caractères ascii ou unicode, etc ...

M.AFILAL 9 M.AFILAL 10

Manipulations des fichiers indépendamment des données


Comment manipuler des fichiers indépendamment des données qu'ils contiennent, tels que :
– le renommage, la suppression,
– connaître les droits d'un fichier en terme de sécurité, …..

Pour cela, on utilise la classe File du package java.io,


– elle permet de manipuler un fichier sur le disque dur.

Quelques méthodes de la classe File.


– public File (String pathname)
Crée un nouvel objet File, correspondant au chemin (path) indiqué. Par exemple,
avec
– File f = new File("C\\FichierJava"); « remarquez les 2 antislash ou 1 slash »
– f désignera le répertoire "C\\FichierJava ". Le chemin peut correspondre à un
fichier ou à un répertoire.

– public File (File parent, String child)


Crée un nouvel objet File, correspondant au chemin (path) composé en ajoutant
child à parent. Ainsi, en exécutant le code :
– File homedir = new File("/home/java"); // 1 slash
– File f = new File(homedir, "MonProg.java");
– f désignera le fichier "/home/java/MonProg.java".

– long lastModified ( ): retourne la date de la dernière modification.


M.AFILAL 11 M.AFILAL 12

– boolean setReadOnly ( ) : place un fichier en lecture seule.


Quelques méthodes de la classe File Exemple: classe GestionFile

import java.io.File;
public class GestionFile {

– public GestionFile() { }
– // Méthode permettant l'affichage des caractéristiques d'un fichier s’il existe.

– void getFile(String nomFile) {

File f = new File(nomFile);

System.out.println(f.getAbsolutePath() + " :: " + f.getName());

if (f.exists()) {
– System.out.println(f.getName() + " : " + (f.canRead() ? "r" : "-")
+(f.canWrite() ? "w" : "-") + " :: " + f.length());
– f.delete(); // attention
}

else {
– System.out.println("fichier non existant ");
}
M.AFILAL 13 – } M.AFILAL 14

Exemple: classe GestionFile


// Méthode permettant de lister l'ensemble de fichier ou directory qui se trouve dans
nomDossier si c'est un dossier si non affichage du fichier nomDossier.
– void typeDossier(String nomDossier) {
try {
– File f = new File(nomDossier);
– String[ ] files = f.list();
– for (int i = 0; i < files.length; i++) {
if (new File(nomDossier + "\\" + files[i]).isDirectory()) {
System.out.println("Rep : " + nomDossier + "\\" + files[i]);
typeDossier(nomDossier + "\\" + files[i]);
}
else {
System.out.println("Fil : " + nomDossier + "\\" + files[i]);
getFile(nomDossier + "\\" + files[i]);
}
– }
}catch (NullPointerException e) {
– System.out.println("le type du dossier entrée est un fichier ou répertoire
introuvable");
}finally {
– getFile(nomDossier);
}
– } 15 16
M.AFILAL M.AFILAL
}
L’accès séquentiel en mode binaire(ou mode octet) Accès séquentiel binaire en lecture
Utiliser cet accès pour des données binaires séquentielles (image, chansons,…..). Les flux physiques en mode binaire en lecture possède:
– La classe abstraite InputStream:
– Seuls les caractères codés selon l’ISO Latin1 (ISO-8859-1) sont pris en compte «sont qui est le modèle de base pour toutes les classes flux physiques pour une lecture
visibles» : dans ce cas alors séquentielle binaire.
– Les caractères écrits dans le flux seront encodés avec l’ISO Latin-1
– Les caractères lus dans la source doivent avoir été encodés avec l’ISO Latin-1, – On ne peut pas utiliser directement la classe InputStream car:
elles sont lisibles par exemple dans un fichier elle ne propose que des méthodes élémentaires de récupération de données,
elle n’implante pas la méthode read( ) qui doit être définie par toutes les classes
– Pour accéder à la source de données, il faut déterminer : dérivées.
– L’accès en lecture ou écriture
– Le type de la source de données : fichier, mémoire, etc.…. – La fonction read( ): retourne un int.
– La manière de gérer le flux : choisir le filtre adéquat (peut être aucun) À chaque appel de cette méthode,
– on se déplace d’un octet sur le flux,
Remarque: – retourne la valeur -1 si on atteint la fin du flux.
– ISO Latin1: Code ASCII étendu à 8 bits comprenant tous les caractères accentués de la Elle permet de lire un byte ou un tableau de byte
langue française.
– La fonction read( byte[ ] buffer): retourne un int
– Les flux d’octets (binaire) permettent de lire et écrire une suite d’octets (sans aucune Elle permet de lire un tableau d’octets, de taille du tableau buffer, depuis le flux.
conversion « encodage système », rapide mais non compréhensible par l’utilisateur) Elle retourne le nombre d’octets lus.
Elle lit autant d’octets qu’elle peut en stocker dans le tableau et autant qu’elle peut
en lire sur le flux.
– Les flux de caractères permettent de lire et d’écrire une suite de caractères (avec Elle retourne -1 si la lecture à échoué (fin du flux).
conversions éventuelles « utilisation M.AFILAL
d’un nouveau encodage », pas très rapide mais 17 M.AFILAL 18
compréhensible par l’utilisateur).

Accès séquentiel binaire en lecture Les flux physiques

Principe d’utilisation des flux en mode binaire (lecture / écriture) :

– Pour la lecture :
Création de l’objet d’un type héritant d’InputStream
Invocation de la méthode read
Fermeture par la méthode close « si pas d’utilisation de try with resource »

– Pour l’écriture :
Création de l’objet d’un type héritant d’OutputStream
Invocation de la méthode write
Fermeture par la méthode close « si pas d’utilisation de try with resource »

M.AFILAL 19 M.AFILAL 20
Les flux physiques
ByteArrayInputStream :
Les filtres pour accès séquentiel binaire en lecture
– Permet de lire une zone mémoire (un tableau d’octets).

FileInputStream :
– Permet la lecture des données contenues dans un fichier byte par byte.

ObjectInputStream :
– Elle possède différentes méthodes pour lire tout type de données (primitifs ou objets).

PipedInputStream :
– Lecture de bytes dans un canal de communication entre deux applications:
une application va lire ce qui est écrit par l’autre application.
– Utilisée dans la communications via les réseaux et les gestions des processus légers (les
threads).
Permet de faire communiquer deux thread (processus légers)
Tout ce qui est écrit dans un tube par un thread est lu dans l’ordre par l’autre thread

SequenceInputStream :
– Représente la concaténation de plusieurs flux de lecture en un seul.
– Lorsqu’on atteint la fin d’un flux, la lecture continue sur le flux suivant.

StringBufferInputStream :
– Lecture de bytes depuis un string (la source est traitée comme un string, une chaîne de
caractères).
M.AFILAL 21 M.AFILAL 22
– Cette classe et l’ensemble de ses méthodes sont dépréciées car convertissent mal les
caractères en octets (bytes).

Les filtres: pour accès séquentiel binaire en lecture Exemple_1


public void copie1(String source) throws IOException {// sans filtre
Un filtre, en lecture binaire : – FileInputStream is = null;
– s’appuie sur un objet de type InputStream qui lui transmet le flux. – try{
Le filtre peut alors le transformer ou effectuer toute autre opération et retransmet à is = new FileInputStream(source);
son tour le flux. int c;
int nbOctet = is.available();
la classe BufferedInputStream : long saut = is.skip(2);
– Permet d’utiliser un buffer pour accélérer la lecture, System.out.println("nombre d’otets lu est égale à: " + nbOctet + " saut = " + saut);
– // la fonction read retourne l'octet lu et lève l’exception IOException
la classe DataInputStream : while ( (c = is.read( )) != -1) {
– Permet de lire des bytes d’un flux binaire et les transformer en données Java de type – System.out.println("l’octet lu et le caractère correspondant sont: " + c + " " +
primitif. (char) c);
– Permet de lire, de manière indépendante de la plate forme, }
Un flux contenant des données ayant un type primitif Java (int, float, double …), – }catch(NullPointerException e){ System.out.println("Fichier introuvable.");
Un flux représentant des chaînes de caractères au format Unicode, – }finally{ if (is != null) is.close(); }
} // à tester: voir classe LectureEcritureBinaireFichier_New
PushbackInputStream :
– Cette classe ajoute la possibilité de revenir en arrière dans un flux. Remarque
– available( ):
Remarque: Méthode qui retourne une estimation du nombre d'octets qu'il est encore possible de
– On ne doit plus utiliser readLine() de DataInputStream (ceci recevant un message de lire dans le flux
dépréciation au moment de la compilation), mais on utilise à la place un BufferedReader. – skip( long ):
saute autant d'octets dans le flux que la valeur fournie en paramètre. Elle renvoie le
M.AFILAL 23 nombre d'octets sautés, , ici = 2,M.AFILAL
donc, on commence la lecture à partir du troisième24
octet.
Remarque: Empilement de flux filtrés Remarque: Empilement de flux filtrés
En Java, chaque type de flux est destiné à réaliser une tâche.
– Lorsque le programmeur souhaite un flux qui ait un comportement plus complexe
Lecture bufférisée d’un nombre depuis un fichier
"empile", à la façon des poupées russes, plusieurs flux ayant des comportements
plus élémentaires.
– On parle de flux filtrés. – DataInputStream din = new DataInputStream (new BufferedInputStream (new
FileInputStream ("monfichier")));
Concrètement, il s'agit de passer, dans le constructeur d'un flux, un autre flux déjà
existant pour combiner leurs caractéristiques.
– double d = din.readDouble ( );
Exemple:
– Une combinaison des deux classes: Lecture de nombre dans un fichier au format zip

FileInputStream (elle ne sait lire que des octets se trouvant dans un fichier) et – ZipInputStream zin = new ZipInputStream ( new FileInputStream ("monfichier.zip"));

DataInputStream (elle ne sait pas lire depuis un fichier mais elle sait lire les types – DataInputStream din = new DataInputStream (zin);
primitifs de java)
– double b = din.readDouble ( );
– permet de combiner leurs caractéristiques :
Remarque
FileInputStream fic = new FileInputStream ("fichier"); – ZipInputStream est un filtre qui est une sous classe de FilterInputStream
DataInputStream din = new DataInputStream (fic);
double d = din.readDouble
M.AFILAL( ); 25 M.AFILAL 26

Exemple _2 Exemple _3
public void lectureParBloc(int dimBloc) { public void lectureParBlocBuffered(int dimBloc) {
– FileInputStream fis = null; – byte[ ] buffer = new byte[dimBloc];
– byte[ ] buffer = new byte[dimBloc]; – BufferedInputStream fis = null;
– int i = 0; // compteur de nombre byte lus depuis le flux – int i = 0; // affichage de nombre total de byte lu dans le flux
– try { – try {
File fichier = new File("E:/Documents and Settings/Administrateur/Bureau/Dossier1mm.mp3"); – // accès au fichier via un Buffer donc lecture plus rapide
fis = new FileInputStream(fichier);
fis = new BufferedInputStream(new FileInputStream("C:/Documents and
Calendar calDeb = Calendar.getInstance(); Settings/Administrateur/Bureau/dossier1/chanson.mp3"));
// création d'un objet de type Calendar avec des valeurs de date et heures système Calendar calDeb = Calendar.getInstance();
avant début de la lecture
for (int j = 0; j < 3000; j++) { for (int j = 0; j < 3000; j++) {
// création d'un objet de type string avec le tableau de byte buffer – String s = new String(buffer);
– String s = new String(buffer); – System.out.println(s);
// affichage de la chaîne de caractère lu dans le tableau buffer – i += fis.read(buffer);
– System.out.println("chaîne de caractère lu dans l'itération " + j + " est: " + s); }
– i += fis.read(buffer); Calendar calFin = Calendar.getInstance();
} System.out.println( i );
Calendar calFin = Calendar.getInstance(); // temps après fin de la lecture System.out.println("temps de lecture en ms = " + (calFin.getTimeInMillis() -
System.out.println( i ); calDeb.getTimeInMillis()));
System.out.println("temps de lecture en ms = " + (calFin.getTimeInMillis() - – }catch (FileNotFoundException e) { System.out.println("Fichier non trouvé.");
calDeb.getTimeInMillis( ) ) ); – }catch (IOException e) { System.out.println("Impossible de lire");
– }catch (FileNotFoundException e) { System.out.println("Fichier non trouvé1");
– } finally {
– }catch (IOException e) { System.out.println("Impossible de lire");
if ( fis != null ) {
– } finally {
if (fis != null) { – try { fis.close(); // fermeture du flux }
– try { fis.close(); // fermeture du flux } – catch (IOException e) { System.out.println("problème dans la fermeture du flux"); }
– catch (IOException e) { System.err.println("problème dans la fermeture du flux"); } }
} M.AFILAL 27 – } M.AFILAL 28

– } } // voir classe LectureEcritureBinaireFichier_New avec le bloc « try-with-resource »


}
Exemple _4 Exemple _4
public void lectureBufferedTypee(String nomFichier) {
– DataInputStream dis = null; Remarque
– try { – readUTF: lit une chaîne de type UTF
File fich = new File(nomFichier);
FileInputStream fis = new FileInputStream(fich); – En général, les caractères Unicode peuvent être encodés avec plusieurs encodages de la
BufferedInputStream bis = new BufferedInputStream(fis); norme UTF (Unicode Transformation Format) avec UTF32, UTF16 et UTF8
– //classe permettant de lire tous les types de base de Java
dis = new DataInputStream(bis);
int a = dis.readInt( ); – Java travaille en interne en stockant les données de types caractères ou chaîne de
short s = dis.readShort( ); caractères en Unicode en utilisant l'encodage UTF-16.
boolean b = dis.readBoolean( );
String st = dis.readUTF( );
System.out.println("affichage types primitifs:" + a + " " + s + " " + b + " " + st);

– }catch (NullPointerException e) {
System.err.println("Fichier non trouvé");
– }catch (FileNotFoundException e) {
System.err.println("Fichier non trouvé");
– }catch (IOException e) {
System.err.println("Impossible de lire4");
– }finally{
if (dis != null) dis.close( );
– } M.AFILAL 29 M.AFILAL 30
}

L’instruction "try-with-resource" L’instruction "try-with-resource"


Remarque: Remarque:
– L’instruction "try-with-resource" de Java 7 permet de gérer automatiquement la – S’il ya plusieurs ressources alors elles sont séparées par un ;.
fermeture des ressources (fichiers , flux, readers, writers, sockets, connexions …). – Si, on ne veut pas faire la déclaration d’une ressource dans l’instruction try, alors on
Ceci permet d’éviter d’utiliser le bloc "finally" et surtout d’éviter les bugs liés aux peut définir une variable et de l'initialiser avec l'instance existante.
oublis d’appels de la méthode "close()" ! public void Teste( ) {
public static void main(String args[ ]) { – String filepath = "src/demo/trywithresource/mytext.txt";
– String filepath = "src/demo/trywithresource/mytext.txt"; – BufferedReader reader = new BufferedReader(new FileReader(filepath));
– try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) – try (BufferedReader closeableReader = reader ) {
{ String line = closeableReader .readLine();
String line = reader.readLine(); while (line != null) {
while (line != null) { System.out.println(line); line = reader.readLine( );} System.out.println(line); line = closeableReader .readLine( );}
– } – }
– catch (IOException e) { System.err.println("Erreur : " + e.getMessage()); } – catch (IOException e) { System.err.println("Erreur : " + e.getMessage()); }
} – // …. Autre code qui dépend de reader …… dans finally par exemple
– Une ressource est un objet qui doit être fermé lorsque l'on a plus besoin de lui. }
– le mot clé try peut être utilisé pour déclarer une ou plusieurs ressources. Dans l'exemple ci-dessus, comme la variable définie et celle existante pointent sur la même
– le "try-with-resource" fonctionne avec les classes implémentant l’interface référence, les deux variables peuvent être utilisées indifféremment. L'instruction try-with-
"java.lang.AutoCloseable". Cette interface a été définie pour indiquer qu'une ressource resource se charge de fermer automatiquement le flux.
peut être fermée automatiquement.
– L'interface java.io.Closeable hérite de l'interface AutoCloseable : ainsi toutes les classes Attention, seules les ressources déclarées dans l'instruction try seront fermées
qui implémentent l'interface ClosableM.AFILAL
peuvent être utilisées comme ressource dans une 31 M.AFILAL
automatiquement. Si une ressource est explicitement instanciée dans le bloc try, la gestion de32
instruction try-with-resource. la fermeture et de l'exception qu'elle peut lever doit être gérée par le développeur.
Accès séquentiel binaire en écriture Accès séquentiel binaire en écriture
write(int b):
– prenant en paramètre un octet (bien que contenu dans un int) à écrire sur le flux de sortie.
Les flux physiques avec en mode binaire en écriture, utilise comme classe de base
La classe abstraite OutputStream: La classe fournit également d’autres méthodes :
– C’est le modèle de base pour toutes les classes flux physiques pour écriture
binaire séquentielle. – void write(byte[ ] b) :
– C’est la super classe de toutes celles représentant un flux de sortie d’octets cette méthode permet d’écrire plus d’un octet à la fois. En effet, les octets stockés
dans le tableau passé en paramètre seront tous écrits en un seul appel de cette
méthode.
Elle n’implante pas la méthode write( int)
– Cette méthode doit être définie par toutes les classes dérivées. – void write(byte[ ] b, int off, int len) :
– Cette méthode permet d’écrire un byte ou un tableau de byte cette méthode permet d’écrire sur le flux l’intégralité du tableau, on commence à
écrire les octets à partir de la position off (offset) du tableau. Le paramètre len
– Prenant en paramètre un octet (bien qu’il est contenu dans un int) à écrire sur correspond au nombre d’octets à écrire.
le flux de sortie.
– close( ) :
Cette méthode permet de fermer le flux. Cette méthode doit toujours être appelée
quand on a fini d’écrire sur le flux « si pas de gestion automatique de fermeture de
flux ».

– flush( ) :
Cette méthode permet de vider le buffer et force l’écriture des données dans le flux.
(Demande d'écrire dans le flux ce qu'il y a dans le buffer).
M.AFILAL 33 M.AFILAL 34

Accès séquentiel binaire en écriture Accès séquentiel binaire en écriture

FileOutputStream :

– Permet l’écriture de données dans un fichier dont le nom est donné dans le constructeur.

– Il est possible de spécifier dans le constructeur si le contenu doit s’ajouter à la suite du


contenu existant ou écraser les valeurs précédentes (si le fichier existait déjà).
Par défaut, le contenu sera remplacé.

ByteArrayOutputStream :

– Implémente un flux de sortie dans lequel les données sont écrites dans un tableau de
byte. Le buffer augmente au fur et à mesure que des données sont écrites.

M.AFILAL 35 M.AFILAL 36
Les filtres pour accès séquentiel binaire en écriture Les filtres pour accès séquentiel binaire en écriture

Un filtre pour les accès en écriture s’appuie sur un objet de type OutputStream qui récupère le
contenu du flux :
Le filtre traite les données du flux puis les retransmets à l’objet flux associé.

– La classe BufferedOutputStream:
Permet d’utiliser un buffer pour accélérer l’écriture.

– La classe DataOutputStream:

Fonctionne comme DataInputStream mais en écriture.


La classe PrintStream: – Permet de convertir des valeurs de type primitif en flux binaire
– Permet d’écrire des caractères, des types primitifs, des textes et aussi des objets sérialisés
(la sérialisation est un mécanisme de stockage des objets) Fournit des méthodes pour écrire des données sous formes de types primitifs Java,
– Permet d’écrire dans le flux la conversion des types primitifs en chaîne de caractères. dans un flux de sortie, de manière portable.
– Donc, elle permet d'écrire tous les types de base de Java.
– Java utilise cette classe pour les affichages sur écrans (System.out est de type
PrintStream). Ainsi, elles pourront être récupérées par la suite via un flux d’entrée:
DataInputStream.
– Elle permet également de contrôler le vidage du tampon (flush).
– La classe DataOutputStream et la classe DataInputStream doivent être utilisées en
– Les méthodes de ce flux ne lèvent aucune exception en cas d’erreurs (le programmeur association pour lire un flux écrit par l’autre.
doit gérer les exceptions lui même pour vérifier si l’opération d’écriture s’est bien
déroulée ou pas). M.AFILAL 37 M.AFILAL 38

Exemple : lecture et écriture par bloc Exemple _1: lecture et écriture byte par byte

public void copie2(String source, String dest) throws IOException {

– FileInputStream is = new FileInputStream(source);

– FileOutputStream os = new FileOutputStream(dest);

– int c;

– while ( (c = is.read()) != -1) {// lecture et écriture octet par octet

System.out.println("le byte lu et le caractère correspondant sont: " + c + " " +


(char) c);

os.write( (byte) c);


– }

– os.close();

– is.close();
}
Méthode permettant de copier, avec format lecture binaire, le contenu d'un fichier et le mettre,
avec format d'écriture binaire, dans un autre fichier.
M.AFILAL 39 M.AFILAL 40
Exemple _2 Exemple _3
Méthode permettant d'écrire, sous format binaire, un texte à la fin d'un fichier, jusqu'a entrée , Méthode permettant d'écrire dans un fichier en utilisant la classe PrintStream avec association
via le clavier, d’un string de taille nulle. Elle retourne une exception de type IOException d'un buffer, ce qui permet une conversion des types primitif en format string.

public void copieEcriture1(String dest) throws IOException {//* public void ecritureAvecPrintStream(String nomFichier) {
– FileOutputStream os = null; – File fich = new File(nomFichier);
– try { – int a = 10;
os = new FileOutputStream(dest, true); – char s = 'y';
while (true) { – boolean b = true;
– String str = (new DataInputStream(System.in)).readLine(); – try {
– if(str.equals("")) break; FileOutputStream fos = new FileOutputStream(fich, true);
– os.write(str.getBytes( )); // écriture de bloc de byte BufferedOutputStream bos = new BufferedOutputStream(fos);
} PrintStream dos = new PrintStream(bos);
– }catch (NullPointerException e) { dos.println(a);
System.out.println("Fichier introuvable."); dos.println(s);
– } dos.println(b);
– finally { dos.println("le code est simple avec printStream");
if (os != null) os.close( );
dos.close( );
– }
– }catch (FileNotFoundException e) {
}
System.out.println("Fichier non trouvé");
– }catch (IOException e) {
Remarque:
System.out.println("Impossible d’écrire dans le fichier");
– System.in est un objet de type inputStream, permet de lire les entrées clavier sous format
binaire – }
M.AFILAL 41 } M.AFILAL 42

– La méthode readLine retourne un string

*Exemple _4 Sérialisation / Désérialisation


public void ecritureParBlocBufferedTypee(String nomFichier) {
Définition:
– File fich = new File(nomFichier);
– DataOutputStream dos = null; – La sérialisation consiste à pouvoir prendre un objet en mémoire et à en sauvegarder l'état
– int a = 10;
sur un flux de données (vers un fichier, par exemple).
– short s = 3;
– boolean b = true; Pour qu'un objet puisse être sérialisé, il est nécessaire que sa classe implémente l'interface
– try { java.io.Serializable.
FileOutputStream fos = new FileOutputStream(fich, true); – Cette interface ne contient aucune déclaration de méthode ;
BufferedOutputStream bos = new BufferedOutputStream(fos); lorsqu'une classe implémente l'interface Serializable, elle « autorise » la sérialisation
dos = new DataOutputStream(bos); de ses instances.
System.out.println("nombre de bytes écrits, au début, dans le flux est " + dos.size());
dos.writeInt(a); Remarque et objectif
dos.writeShort(s); – Ce concept permettra de reconstruire, ultérieurement, l'objet en mémoire à l'identique de
ce qu'il pouvait être initialement.
dos.writeBoolean(b);
– La sérialisation peut donc être considérée comme une forme de persistance de données.
dos.writeUTF("le code est simple");
System.out.println("nombre de bytes écrits dans le flux est: " + dos.size());
– Il est possible avec un même flux de sérialiser plusieurs objets les uns à la suite des
dos.flush(); // vide le tampon autres. Ainsi, plusieurs objets peuvent être sauvegardés. Dans ce cas, il faut
– }catch (FileNotFoundException e) { faire attention de relire les objets dans leur ordre d'écriture.
System.err.println("Fichier non trouvé");
– }catch (IOException e) { appeler plusieurs fois les méthodes de ObjectInputStream.
System.err.println("Impossible d’écrire dans le fichier"); – La règle est la suivant : la désérialisation des objets doit être dans leur ordre
– }finally{ d'écriture (de sérialisation)
if (dos != null) {
– try {dos.close(); // fermeture du flux }
le premier objet sérialisé est le premier objet désérialisé, le second écrit
– catch (IOException e) {System.err.println("problème dans la fermeture du flux"); }
est le second lu, etc...
}
M.AFILAL 43 44
– } – Si la classe utilisée a changé entre le M.AFILAL
moment où elle a été sérialisée et le moment où elle
} // voir class LectureEcritureBinaireFichier_New puis utiliser la fonction lectureBufferedTypee pour le est désérialisée, une exception est levée : java.io.InvalidClassException
teste
Exemple Exemple
import java.io.Serializable;
public class PersonneSerialisee implements Serializable { public void ecritureObjects(String nomFichier) {
– static private final long serialVersionUID = 6L; – File fich = new File(nomFichier);
– public String nom; – ObjectOutputStream dos = null;
– public String prenom;
– try {
– public double age;
– // création d'une personne
PersonneSerialisee p = new PersonneSerialisee("Dupont", "Albert", 88);
– public PersonneSerialisee(String nom, String prenom, double age) {
System.out.println("creation de : " + p);
this.nom = nom;
dos = new ObjectOutputStream(new FileOutputStream(fich));
this.prenom = prenom;
– // sérialisation : écriture de l'objet dans le flux de sortie
this.age = age;
dos.writeObject(p);
– }
System.out.println(p + " a été sérialisé");
– public String toString() {
dos.flush(); //flush vide le buffer et force l'écriture dans le fichier binaire spécifié ;
return nom + " " + prenom + " " + age + " ans";
– } – }catch (FileNotFoundException e) {
} System.err.println("Fichier non trouvé");
Remarque – }catch (IOException e) {
System.err.println("Impossible d'ecrire");
– le serialVersionUID permet d'affecter un numéro de version à la classe. e.printStackTrace();
Ce numéro doit être changé, par exemple, lorsqu'un champs non-transient est ajouté – } finally {
ou supprimé de la classe. if (dos != null) {
– Théoriquement, c'est le développeur qui doit créer ce champs. Toutefois, si ce champs est – try { dos.close(); // fermeture du flux }
– catch (IOException e) { System.out.println("problème dans la fermeture du flux"); }
absent, le compilateur générera un numéro automatique. }
– Le champs serialVersionUID est utilisé lors de la désérialization afin de s'assurer que les – }
versions des classes Java soient concordantes.
M.AFILAL Si ce n'est pas le cas, une 45 } // voir class LectureEcritureBinaireFichier_New M.AFILAL 46
InvalidClassException sera levée.

Exemple pour la désérialisation


Remarques
public void lectureObjects(String nomFichier) {
– try {
Remarque ObjectInputStream dis = new ObjectInputStream(new FileInputStream(new
– Il peut cependant être problématique que toutes les valeurs des variables soient File(nomFichier)));
sauvegardées car cela peut poser des problèmes de sécurité.
PersonneSerialisee p = null;
Java fournit pour cette raison le modificateur transient.
– // désérialisation : lecture de l'objet depuis le flux d'entrée
– Les variables déclarées avec le modificateur transient seront stockées (type et nom) mais
pas leur valeur. p = (PersonneSerialisee) dis.readObject();
– Une fois désérialisées, elles auront les valeurs par défaut :
0 pour les nombres, if (p != null) {
false pour un boolean, – System.out.println(p + " a été désérialisé");
}
null pour les objets. (ceci peut engendrer des problèmes avec un l’utilisation d’un – }catch (NullPointerException e) { System.out.println("Objet Fichier nul");
objet nul) – }catch (FileNotFoundException e) { System.out.println("Fichier non trouvé");
– transient devant un attribut, entraine qu’il ne sera pas sauvegardé par writeObject() , ni lu – }catch (ClassNotFoundException e) { e.printStackTrace();
par readObject( ), la valeur de l'attribut sera donc initialisée à null – }catch (IOException e) {
System.out.println("Impossible de lire");
e.printStackTrace();
Remarque – } finally {
– Toutes les classes dérivées d’une class sérialisable sont aussi sérialisables. if (dis != null) {
– try { dis.close(); // fermeture du flux }
– Lorsqu’une classe est désérialisée par Java, les champs de la super-classe qui ne serait – catch (IOException e) { System.out.println("problème dans la fermeture du flux"); }
pas sérialisable seront initialisés dans le constructeur vide sans argument. }
M.AFILAL 47
– } M.AFILAL 48
} // voir class LectureEcritureBinaireFichier_New
Sérialisation et l’héritage Sérialisation personnalisée
Il est possible de personnaliser la sérialisation d'un objet:
– dans ce cas, la classe doit implémenter l'interface Externalizable qui hérite de l'interface
Deux cas de figure pour le comportement de la sérialisation face à l'héritage. Serializable.

– Premier cas de figure: Cette interface définit deux méthodes publiques : readExternal() et writeExternal().
lorsqu'une classe hérite d'une classe sérialisable, elle se comporte comme si elle – Utiliser ces méthodes pour contrôler la façon d’enregistrer ou de lire des objets à stocker
avait implémenté elle-même l'interface Serializable. via un flux de données.
Les attributs de la classe mère sont sérialisés selon l'implémentation de celle-ci. private void writeExternal (java.io.ObjectOutputStream out) throws IOException;
private void readExternal (java.io.ObjectInputStream in) throws IOException,
– Deuxième cas de figure: ClassNotFoundException;
une classe implémente l'interface Serializable et hérite d'une classe non sérialisable.
– Il y a ici deux points fondamentaux à savoir : Par défaut
les attributs hérités ne sont pas sérialisés. – la sérialisation d'un objet qui implémente cette interface ne prend en compte aucun
il est nécessaire que la classe étendue (la classe mère) possède un attribut de l'objet (pas de sérialisation par défaut).
constructeur par défaut accessible ; dans le cas contraire, une
InvalidClassException est levée à l'exécution. Remarque :
– le mot clé transient est donc inutile avec une classe qui implémente l'interface
Externalisable
– Une classe implémentant l'interface Externalizeable doit posséder un constructeur par
défaut public:
M.AFILAL 49 qui sera utilisé dans la déserialisation afin d’initialiser les champs sans valeur des
M.AFILAL 50
objets stockés (surtout si cette classe hérite d’une classe non sérialisable)
Exemple

Sérialisation personnalisée Remarques importantes pour la sérialisation


public class CryptageString implements Externalizable {
– static private final long serialVersionUID = 13L; Remarque pour l’exception StreamCorruptedException
– private int val; private String str; – Si on appel une première fois la méthode ecritureObjet pour enregistrer un objet dans un
– public CryptageString( ) { str = ""; val = 0;} fichier nomFichier, puis on ferme le flux utilisé.
– public CryptageString(String str, int val) { this.str = str; this. val = val; }
Puis, si on enregistre encore un autre objet, en faisant appel à la méthode
– /******************* Sérialisation des données. ******************************/
ecritureObjects en fermant le flux.
– public void writeExternal(ObjectOutputStrem out) throws IOException { – Le fichier nomFichier sera modifier
– // récupération du tableau de byte représentant str – et si on veut le relire encore une fois on aura le déclanchement d’une
byte[ ] tab = str.getBytes("UTF-8"); exception: Une exception de type StreamCorruptedException peut être levée
– // cryptage de str = césar avec val
– La même exception sera levée si le fichier a été corrompu par exemple en le
for(int i = 0; i < tab.length ; i ++) { tab[i] += val; };
modifiant avec un éditeur.
out.writeLong(val * val); // écriture du carré de val
out.writeObject(tab); // écriture du tableau de byte – La sérialisation des objets devrait se faire une seule fois avant la fermeture du flux utilisé
– } pour cette tâche.

– /******************** Désérialisation des données. ****************************/


Remarque pour la désérialisation:
– public void readExternal(ObjectInputStrem in) throws IOException, ClassNotFoundException{
– // lecture du carré de integer – la désérialisation de l'objet doit se faire avec la classe qui a été utilisée pour la
val = (int) Math.sqrt(in.readLong()); sérialisation.
– // lecture du tableau de byte – Il est possible avec un même flux d'écrire plusieurs objets les uns à la suite des autres.
byte[ ] tab = (byte[ ]) in.readObject(); Ainsi plusieurs objets peuvent être sauvegardés.
for(int i = 0; i < tab.length; i ++) { tab[i] -= val; }
Dans ce cas, il faut faire attention de relire les objets dans leur ordre d'écriture.
str = new String(tab, "UTF-8");
– } M.AFILAL 51 M.AFILAL 52

}
Remarques importantes pour la sérialisation Liste des différents flux primaires, classés par catégorie
Remarque dans le cas de champ statique
Lors de la sérialisation, on aura seulement la valeur nulle qui sera affectée au champ statique.
– Pour remédier à cela, on utilisera, par exemple, le code suivant qui permet la sérialisation
pour le champ statique : champStatique ou transient .
class newSerialisation implements Serializable {
– private static final long serialVersionUID = 4L;
– private static int champStatique = 0;
// Autres champs: non-transient , non-statique champs.
– /*Ceci impose que l’écriture et la lecture du champ statique doivent être
traitées à part*/
– private void writeObject(ObjectOutputStream oos) throws IOException {
// appel des mécanismes de sérialisation automatique par défaut (via le
mot Serializable définissant la classe)
– oos.defaultWriteObject( );
– oos.writeObject(new Integer(champStatique));
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, bytes
IOException {
– ois.defaultReadObject();
– champStatique = (Integer)M.AFILAL
ois.readObject(); 53 M.AFILAL 54
}
}

Accès séquentiel en mode texte

La question qu'on pourrait se poser est: pourquoi des flux spéciaux pour les caractères alors
que ce sont des données binaires ?

– La réponse est que Java, contrairement à beaucoup d'autres langages, utilise Unicode.

Ainsi, les caractères sont codés sur 16 bits et non sur 8.

Ces flux servent donc à gérer les jeux de caractères (conversion possible d'un jeu à
l'autre: choix d’encodage approprié).

Les classes de flux gérant ce type d’accès offrent deux améliorations :


un meilleur support de l’encodage des caractères lus (utilisation de Unicode:
possibilité de lire plusieurs types de caractères (arabe, français,…).
des meilleurs performances.
– on peut fixer un autre encodage, pour la gestion des caractères, qui peut être
différents de celui du système ,

Ce type d’accès est capable de lire ou écrire tout caractère Unicode.

Il faut privilégier ces classes pour être compatible avec les alphabets internationaux.
M.AFILAL 55 M.AFILAL 56
Accès séquentiel en mode texte Accès séquentiel en mode caractère en lecture

Les classes de flux gérant ce type d’accès sont les sous-classes des classes Reader et Writer. La classe Reader fournit:
– Attention: – Des méthodes permettant la lecture de caractères.
Par défaut les objets, des classes Writer ou Reader, utilisent l’encodage privilégié par – Ses méthodes read( ) sont similaires à celle avec les flux de binaires.
le système (réellement ses classes utilisent l’encodage par défaut de la JVM qui est – La seule méthode abstraite qu’elle implémente est la méthode :
dépendant du système). int read(char[ ] cbuff, int off, int len).
– Cela signifie que l’écriture dans un fichier, par exemple, peut aboutir à la perte – Elle permet de lire un caractère ou un tableau de caractères
des informations, due à un encodage incapable de prendre en compte certain Elle stocke les len caractères lus dans le tableau de char à partir de la
caractère. position off.
– Remarque:
les classes InputStream / OutputStream manipule des octets (byte en Java) donc Autre méthodes de lecture
dépendant de la plateforme
– int read( ) :
Lit le prochain caractère sur le flux et le retourne sous forme d’int.
Les concepts et méthodes utilisés pour les flux binaires ont été adaptés aux flux de caractères.
Il y a donc de fortes similitudes avec ce qui a été vu dans le cours de flux binaire. Pour l’utiliser réellement sous forme de caractère (char), il sera nécessaire de faire
une conversion explicite (cast).
– int read(char[] cbuff) :
Pour accéder à une source de données, il faut déterminer :
Elle stocke les caractères lus dans le tableau de char.
L’accès en lecture ou en écriture
Le type de la source de données : fichier, mémoire, etc.….
Cette classe possède également les méthodes
La manière de gérer le flux : choisir le filtre adéquat (peut être aucun).
– close( ), mark(int readlimit), markSupported( ), reset( ), skip(long n).
– Elle possède aussi une méthode boolean ready( ) qui teste s’il est possible de lire sans
bloquer le flux.
M.AFILAL 57 M.AFILAL 58

Accès séquentiel caractère en lecture


Accès séquentiel caractère en lecture
FilterReader :
– classe abstraite permet de lire des flux de données filtrées, il faut utiliser ses sous classes.

PushBackReader:
– permet de revenir en arrière dans un flux,
Elle est dotée d’une méthode unRead() qui permet de remettre du texte dans le flot
d’entrée, comme s’il n’avait pas déjà été lu. « pas ou jamais utilisée »
PipedReader :
– classes utilisée pour les communications via les réseaux et les gestions des processus.

InputStreamReader :
– Cette classe va permettre la communication entre les flux d’entrée d’octets et les flux d’entrée
de caractères.
– En effet,
il va lire les octets pour ensuite les transformer en caractères en utilisant un jeu de
caractères spécifique (Charset). «elle lit les octets et les décodes en caractères en utilisant
un encodage »
– Ce jeu de caractères sera celui de la plateforme par défaut, mais peut être spécifié
par l’utilisateur lors de l’instanciation :
InputStreamReader(InputStream in, Charset cs).
Permet la conversion octets vers caractères.
– Objectif :
choisir un encodage adapté aux type de données a lire.
construire un Reader à partir d’un InputStream. C’est particulièrement utile avec
System.in.
M.AFILAL 59 M.AFILAL 60
FileReader:
– cette classe (qui hérite de InputStreamReader) décrit tout flux texte entrant attaché à un fichier.
Accès séquentiel caractère en lecture Exemple pour lecture de caractère
StringReader :
– Cette classe permet la lecture d’une chaîne de caractères fournie dans le constructeur Remarque
StringReader(String s).
– boolean markSupported( ): indique si le flux supporte la possibilité de marquer des
CharArrayReader : positions
– Cette classe permet la lecture d’un tableau de caractères « flux de caractères d’entrée ». – boolean ready( ): indique si le flux est prêt à être lu
BufferedReader :
– Cette classe va permettre Exemple
une lecture bufférisée des flux de caractères d’entrée,
– public static void testLectureTexte (InputStream input) throws IOException {
la possibilité de fixer la taille du buffer dans le constructeur.
la possibilité de revenir en arrière dans la lecture, à l’aide des méthodes mark et reset try (InputStreamReader f lux= new InputStreamReader(input)){
– void mark (int _limite) throws IOException // Le résultat de read doit être un entier (à cause du -1 qui est renvoyé en
place une marque à la position courante de lecture. l’argument limite permet de fixer fin de fichier)
le nombre maximum de caractères qui pourront être lus en préservant la marque.
– void reset ( ) throws IOException int cc;
remet la tête de lecture à la position de la dernière marque posée. – while ( (cc = f lux.read()) != -1){
– public long skip(long n) throws IOException
Permet de sauter n caractères dans le flux // Pour ranger cc dans un char, il faut un cast :
Un autre intérêt du BufferedReader est la disponibilité de la méthode readLine : char c= (char)cc;
– String readLine ( ) throws IOException
– }
renvoie la ligne suivante sous forme de String, ou null si on est à la fin du fichier.
ce sont des caractères Unicode qui sont lus, représentés par 2 octets consecutives. }
LineNumberReader: – }
– C’est une sous classe de BufferedReader, elle est dotée d’une méthode getLineNumber qui permet de
connaître le numéro de la ligne courante.
M.AFILAL 61 M.AFILAL 62

Accès séquentiel caractère en lecture Accès séquentiel caractère en écriture

import java.io.*;
Pour l’écriture en mode texte:
public class LectureTextFichier { – on utilise la classe Writer (classe abstraite) et ces classes dérivées.
elle permet l’écriture de flux de caractères.
– void lectureSimple(String nomFile) { elle impose l’implémentation des méthodes suivantes :
try(LineNumberReader in = new LineNumberReader(new FileReader(nomFile))) { – close( ):
/* lecture d'un fichier avec FileReader puis bufferisé avec affichage de Elle permet la fermeture d’un flux (il faudra tout d’abord vider les
ligne par la classe LineNumberReader */ ressources utilisées par le flux avec la méthode flush( )).
– String ligne; – flush( ):
/* lecture ligne par ligne*/
Elle permet de vider les ressources utilisées par le flux courant.
– while((ligne = in.readLine( )) != null) // fin de fichier si null
System.out.println(in.getLineNumber( ) + " : " + ligne) ; – write(char[] cbuf, int off, int len):
}catch (IOException e) { Elle permet l’écriture d’intervalle de tableau de caractères
– System.out.println(e); Elle permet d’écrire un caractère ou un tableau de caractères
– System.exit(0);
}
– }
} // voir la classe LectureTextFichier

M.AFILAL 63 M.AFILAL 64
Accès séquentiel caractère en écriture Accès séquentiel caractère en écriture

PipedWriter :
– classe utilisée pour les communications via les réseaux et les gestions des processus.
Cette classe va être connectée à un PipedReader.

PrintWriter :
– Cette classe implémente toutes les méthodes de la classe PrintStream, elle produira une
sortie formatée en string comme printf en C++.
– Cette classe peut utiliser un encodage différent du celui du système hôte
– Les méthodes de la classe PrintWriter
ne déclenchant pas d’exception, ce sera à l’utilisateur de gérer cela en utilisant la
méthode checkError( ), qui existe aussi dans PrintStream
– public boolean checkError ():
Appelle la méthode flush () et renvoie true si une erreur ou exception est
survenue pendant un appel aux autres méthodes de cette classe.

public static void main(String[] args) {


– String s = "Hello World ";
– PrintWriter pw = new PrintWriter(System.out);
// on concatène le string
– pw.append(s);
– // On vérifie s’il y’a une erreur ou problème de flush
– System.out.println("" + pw.checkError( ) );
M.AFILAL 65 } M.AFILAL 66

Accès séquentiel caractère en écriture Exemple d’encodage

OutputStreamWriter :
– Cette classe va permettre
la communication entre les flux d’entrée de caractères et les flux d’entrée d’octets.
– Elle permet la conversion d’un flux de caractères en un flux de données
binaires.
– En fait,

Ce flux convertie les caractères en bytes, avec un encodage utilisant un jeu de


caractères spécifique (Charset = encodage utilisé), puis les écrits sous forme de flux
de sortie en mode binaire.

L’encodage utilisé, en général, sera celui de la plateforme par défaut,

L’encodage à utiliser (différent de celui du système utilisé), il peut être spécifié par
l’utilisateur lors de l’instanciation, avec
– OutputStreamWriter (OutputStream out, Charset cs).

M.AFILAL 67 M.AFILAL 68
Accès séquentiel caractère en écriture Exemple _1

FilterWriter : On utilise un objet adapté (ici un Writer pour l’écriture et un reader pour la lecture).
– Permettre d’écrire des flux de données filtrées.
– public static void testEcritureTexte(String fname) throws IOException {
BufferedWriter : – // On crée un objet Writer, ce qui ouvre le fichier fname
– Permet l’écriture Bufférisée de texte vers un flux de sortie de caractères FileWriter f= new FileWriter(fname);// On utilise cet objet pour écrire
f.write("Bonjours ");
FileWriter: f.write(" à tous.");
– écriture dans un fichier. f.write(" Java c’est facile !");
f. flush( ); // on vide les ressources utilisées par le flux f
CharArrayWriter : f.close( ); // On ferme le flux
– Cette classe implémente une mémoire tampon de caractères qui peut être utilisée comme – }
un objet Writer.
– On peut spécifier sa taille à l’aide du constructeur CharArrayWriter. – public static void testLectureTexte (String fname) throws IOException {
FileReader f = new FileReader(fname);
StringWriter : int cc = f.read();
– Cette classe représente un flux de caractères qui recueille sa sortie dans un objet while (cc != -1){
StringBuffer // Pour ranger cc dans un char, il faut un cast :
«ceci en utilisant la méthode getBuffer() de la classe StringWriter qui retourne un – System.out.println((char) cc);
objet de type StringBuffer)», – cc = f.read(); // Lecture du suivant.
lequel peut alors être utilisé pour construire une chaîne de caractères }
f.close();
69
– } 70
M.AFILAL M.AFILAL

Exemple _2: écriture bufférisée dans un fichier Exemple _3: lecture bufférisée dans un fichier

Ecriture de caractères dans un fichier, en utilisant BufferedWriter Lecture de caractères depuis le fichier de l’exemple précédant, en utilisant BufferedReader
– import java.io.*; – import java.io.*;
– public class Ecrire{
public static void main(String[] args){ – public class LireLigne{
– Try(FileWriter fw =new FileWriter(“C:\\temp\\essai.txt"); – public static void main(String[ ] args){
BufferedWriter bw= new BufferedWriter(fw)){ try(FileReader fr = new FileReader(“C:\\temp\\essai.txt ");
bw.write("Ceci est mon fichier"); BufferedReader br = new BufferedReader(fr)){
bw.newLine(); // saut de ligne
bw.write("Il est à moi...");
– }catch (Exception e){ while (br.ready()) System.out.println(br.readLine());
System.out.println("Erreur "+ e);
– } }catch (Exception e){
} System.out.println("Erreur "+e);
– } }
– }
}

M.AFILAL 71 M.AFILAL 72
Remarque: conversion d’information Exemple avec modification d’encodage

Ecriture de caractère sur un fichier sous format binaire avec un encodage spécifique UTF-8,
ceci en utilisant OutputStreamWriter
Conversion des caractères d ’un fichier avec un codage explicitement indiqué. – public void writeOutput(String str) {
– InputStreamReader in = new InputStreamReader ( new FileInputStream try (FileOutputStream fos = new FileOutputStream("C:/dossier1/output.doc");
("C:\\dossier1\\chinoix.doc"), "ISO2022CN");
Writer out = new OutputStreamWriter(fos, "UTF-8") ){
– out.write(str);
}catch (IOException e) {
– e.printStackTrace();
}
– }

M.AFILAL 73 M.AFILAL 74

Exemple avec modification d’encodage Affichage écran et saisie clavier


Lecture rapide de caractères depuis un fichier, les caractères sont stockées sous format binaire
et un encodage UTF-8 comme dans le cas d’écriture. Java contient les classes et méthodes pour afficher à l’écran et réaliser des saisies au clavier.

On utilise pour cela de la concaténation de flux


– Java définit classiquement trois flux standard :
– public String readInput( ) { La sortie standard (par défaut l’écran),
StringBuffer buffer = new StringBuffer(); La sortie standard erreur (par défaut l’écran),
try (FileInputStream fis = new FileInputStream("C:\\output.doc"); L’entrée standard (par défaut le clavier).
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
Reader in = new BufferedReader(isr)){
– int ch; – Les objets correspondants aux trois flux sont :
– while ( (ch = in.read()) > -1) { System.out de type PrintStream:
buffer.append( (char) ch); – Affichage du flux en chaîne de caractères
– } System.err de type PrintStream:
– return buffer.toString();
– Affichage du flux en chaîne de caractères
}catch (IOException e) {
– e.printStackTrace(); System.in de type InputStream.
– return null; – qui représente un flux entrant binaire.
} Exemple: class LectureEcritureClavierEcran
– }
Exemple teste
– Voir les fonctions EcritureCaractereAvecEncodage+lectureCaractereAvecEncodage
M.AFILAL 75 M.AFILAL 76
de la classe LectureEcritureCaractereFichier_New
Exemple _1 Exemple _2

Lecture des caractères depuis le clavier


try {
– int c; La classe InputStream ne propose que des méthodes élémentaires.
– while((c = System.in.read()) != -1) { Préférez la classe BufferedReader qui permet de récupérer des chaînes de caractères.
System.out.print((char) c);
– } try (Reader reader = new InputStreamReader(System.in);
} catch(IOException e) { BufferedReader keyboard = new BufferedReader(reader) ){
– System.out.print(e); – System.out.print("Entrez une ligne de texte : ");
} – String line = keyboard.readLine();
– System.out.println("Vous avez saisi : " + line);
Remarque: } catch(IOException e) { System.out.print(e);}
– Comme il s'agit de l'entrée standard (clavier) le flux n'a pas de fin, la méthode read() ne
retournera jamais -1.

M.AFILAL 77 M.AFILAL 78

Exemple_3 Exemple _4

– //version mieux adaptée que la solution de l’exemple 3


Lecture de n’importe quel type via clavier ceci en utilisant BufferedInputStream Lecture de n’importe quel type via clavier ceci en utilisant BufferedReader
– public void lectureClavier1() { – public static void lectureClavier2( ) throws IOException {
try (DataInputStream inn = new DataInputStream(new
BufferedInputStream(System.in))){
– //BufferedInputStream: utiliser un buffer de type bytes pour accélérer la – /* InputStreamReader permet de convertir flux bytes en flux caractères: elle lit
lecture. les bytes et les décode en caractères en utilisant un encodage */
– System.out.println("entrer le string à lire du clavier");
– String stringEntree = inn.readLine(); try( InputStreamReader isr = new InputStreamReader(System.in);
– System.out.println("entrer un int"); BufferedReader inn = new BufferedReader(isr)){
– int ii = (new Integer(inn.readLine())).intValue(); System.out.println("entrer le string à lire du clavier");
– System.out.println("les valeurs des variables entrées sont: "+stringEntree+ " String stringEntree = inn.readLine();
"+ii); System.out.println("entrer un int");
}catch (Exception e) { int ii = (new Integer(inn.readLine())).intValue();
– System.out.println(e); System.out.println("entrer resultat2 : "+stringEntree + " "+ii);
– System.exit(1); – }
} – }
– }

M.AFILAL 79 M.AFILAL 80
Fichier En accès directe Fichier En accès directe

Contrairement aux flux séquentielle, il existe un mécanisme qui permet d’accéder directement Les méthodes de lecture permettent de lire des données binaires.
à toute information présente dans un fichier sans obligation de lire le début du fichier.
– int read ( ) throws IOException
La classe RandomAccessFile gère: lit un octet non signé.
– L’accès directe aux données dans un fichier. On a de même que pour les Reader, des méthodes read à un et trois arguments, qui
– L’accès en lecture ou lecture/écriture en mode binaire. permettent de remplir des tableaux.
– La lecture et l’écriture des types primitifs java et des chaînes de caractères. – Des méthodes pour lire une valeur d’un type de base:
readBoolean, readByte, readChar, readDouble, readFloat, readInt, readLong,
Cette classe implémente les interfaces DataOutput et DataInput readShort, readUnsignedByte, readUnsignedShort.
– boolean readBoolean () throws IOException
Les fonctions importantes: lit un booléen.
– RandomAccessFile (String nomFile, String mode) throws FileNotFoundException – readByte lit un octet signé (entre -127 et 128.)
Crée un RandomAccessFile ouvert sur le fichier nommé nomFile. – readUTF: permette de lire sous format unicode UTF8
Le mode peut être
– r pour un fichier ouvert en lecture seule Les méthodes d’écriture qui permettent d’écrire des données binaires:
– rw pour un fichier ouvert en lecture/écriture. – on a d’une part trois méthodes write :
– void close () throws IOException : elle ferme le fichier void write (byte [ ] b, int dep, int longueur) throws IOException
– écrit les octets du tableau b, de l’indice dep à l’indice (longueur -1),
une méthode writeXXX par type de base, comme par exemple
– writeChar.
– writeUTF: permette d’écrire sous format unicode UTF8
M.AFILAL 81 M.AFILAL 82

Déplacement dans un Fichier en accès directe Remarques

long getFilePointer ( ) throws IOException Flux communément utilisés


– cette méthode retourne la position du pointeur de fichier (retourne la position courante),
elle indique la position de la donnée suivante. – Pour lire et écrire des types primitifs (entier, réel, booléens et caractères), on utilise:
DataInputStream et DataOutputStream ainsi que PrintWriter
void seek (long pos) throws IOException – Pour lire des textes (String), on utilise :
– déplace et fixe la position courante. BufferedReader
– pos est le nombre d’octets depuis le début du fichier. – Pour écrire des textes (String), on utilise :
– la prochaine lecture ou la prochaine écriture auront lieu à partir de la nouvelle position. PrintWriter
– Pour sérialiser des objets, on utilise :
long length ( ) throws IOException ObjectInputStream, ObjectOutputStream.
– retourne la longueur max du fichier

void setLength (long newLength) throws IOException


– fixe la longueur du fichier à newLength.

Exemple: voir la classe FichierAccesDirect

M.AFILAL 83 M.AFILAL 84
Interfaces graphiques
Interface Graphique

L’objectif de ce chapitre est de présenter les différentes techniques concernant la construction


d’une interface graphique en JAVA.

La conception d’interfaces graphiques étant loin d’être triviale, et les packages proposés pour
cela par le JDK étant parmi les plus « complexes ».

Le but de ce chapitre est de donner les éléments de bases qui permettent la construction et la
création d’interfaces utilisateur.

M.AFILAL 85 M.AFILAL 86

Interface Graphique Interface Graphique


Problème : (les serveurs graphiques jouent un rôle très important dans la construction des
interfaces graphique)
Java est un langage indépendant des plateformes (cross-platform language) – Les serveurs graphiques ou les systèmes de gestion d'interface utilisateur (GUI Graphical
User Interface Systems), des systèmes d’exploitations
– un même programme peut être utilisé dans des environnements (matériel et OS) dans lesquels la machine virtuelle de JAVA est implantée,
différents sans recompilation. – sont très différents les uns des autres

Ceci nécessité une API pour les interfaces graphiques indépendantes des – Les concepteurs de machines virtuelles JAVA étaient contraints de fournir
plateformes
un package AWT différent pour chaque système d’exploitation.
Ensemble de classes et interfaces java
Ainsi qu’un modèle de gestion des événements
– Ce package AWT chargé de faire le lien
entre le langage java et les serveurs graphiques des différents systèmes
– Exemple : une classe TextField pour définir un champ de saisie de texte d’exploitation dans lesquels la machine virtuelle de JAVA est implantée :
TextField(String content); TextField()
void setText(String content); String getText() – Exemple de serveur graphique
X Window + motif, X Window + gtk
MacOS X, MS Windows

– On général, on a deux stratégies sont possibles pour construire des interfaces graphiques
Faire une utilisation maximale du système graphique cible (qui sera utilisé)

Faire une utilisation minimale du système graphique cible (qui sera utilisé)
M.AFILAL 87 M.AFILAL 88
Interface Graphique Interface Graphique
Utilisation minimale du système graphique sous-jacent
Utilisation maximale du système graphique sous-jacent
– Utiliser des éléments natifs ou des adaptateurs uniquement pour des opérations de base
– Exemple: L'objet TextField délègue la plupart de ses tâches à un composant natif (un Ouvrir une fenêtre, dessiner des lignes/du texte, gestion primitive des événements.
composant du serveur graphique du système d’exploitation utilisé). C.à.d.
Si le programmeur java utilise un objet TextField
– Réaliser tout le reste en Java
Objet TextField délègue la plupart de ses tâches à une classe adaptatrice(un fichier)
propre au système d’exploitation utilisé, qui peut être: L'objet TextField s'affiche en dessinant des lignes,...
– MotifTextField, GTKTextField, WindowsTextField, MacOSTextField .... – pas d’utilisation de classes adaptatrices propres au système.
Donc, le système graphique natif réalise le plus gros du travail
– Avantages / désavantages
– Avantages / désavantages (+) facilité d'éviter les différences entre plateformes
(+) apparence et le comportement (look and feel) des interfaces Java identique à (+) n'importe quel nouveau composant d'interface est immédiatement disponible sur
celui d'applications "ordinaires du système utilisé" toutes les plateformes
(+) pas besoin de ré-implémenter des composants existants (-) besoin de ré-implémenter tous les composants d'interface
(-) ne fournit que les composants disponibles sur la plateforme utilisatrice (-) les applications java n'ont pas le même look and feel que les applications
"ordinaires"
(-) difficile de garantir un comportement identique sur toutes les plateformes
(-) lenteur.
– Choix adopté, dans ce cas, est java.awt
Le choix adopté, dans ce cas est, SWING
AWT Abstract Window Toolkit
– Le package est javax.swing. dans JDK depuis version 1.2
Le package java.awt.* présent dans Java depuis la version 1.0.
– Swing s'intègre aux JFC (Java Fundation Classes lancés par SUN en 97 pour la création
Palette de composants fournies par AWT ne contient que des composants simples, d’interfaces graphiques plus élaborées que AWT et intégré depuis version 2 de Java(1.2))
seuls les composants standard existants dans tous les systèmes d’exploitation
peuvent être présents dans AWT. JFC = Java 2D API + copier coller inter-applications + Swing + Accessibilité.
M.AFILAL 89 M.AFILAL 90

Interface Graphique Interface Graphique


– Swing
Composants légers (lightweight) 100% java (un gain de place mémoire par rapport
à AWT). Structure d’une interface graphique :

– La racine de l’arborescence, de la plupart, des classes et interfaces de Swing – Conteneurs: Japplet,JFrame,JPanel...


est la classe JComponent
Prennent en charge leur affichage sans passer par des objets « natifs ou – Composants « atomiques »: JButton, Jlist, JPopupMenu, ….
adaptateurs» gérés par le système
– Gestionnaire de disposition.
– Les composants JApplet, JDialog, JFrame, JWindow dérivent de Container .
– Pour l’interaction avec l’utilisateur : gestionnaire d’évènements.
– Multiplication des composants plus riches en fonctionnalités (listes
arborescentes, grilles….)
Remarque:
On peut par exemple faire que les boutons présentent une image à la
place de texte, on peut avoir des boutons ronds, on peut créer des bords – Avant de ce lancer dans le développement d’une interface graphique, on commence
variés pour les composants. systématiquement par ce demander s’il n’existe pas un composant tout fait qui
correspond exactement à ce dont on a besoin.
Pour procéder à un choix de composant,
Propose plusieurs aspect de l’IHM (interface Homme machine) : Motif, Windows,
Métal, Mac, ... – On doit ce rendre sur le site suivant :
– https://docs.oracle.com/javase/tutorial/uiswing/
La structure des composants utilise le modèle MVC (Model View Controler) – puis cliquer sur « Using Swing components »,
– ce qui nous donne un accès au catalogue.
Swing utilise le même mécanisme de gestion d'événement que le package java.awt.
M.AFILAL 91 M.AFILAL 92
Le choix fait dans ce cours est d'utiliser les composants graphiques de type Swing
Les conteneurs
Les conteneurs (Containers)
Containers de haut niveau
– JApplet :
Cette classe est une sous classe de java.applet.Applet. Elle permet de faire des
Les conteneurs (containers) ont pour seul but: applets où les composants sont des composants Swing et non AWT. Son usage est
similaire à celui de java.applet.Applet.
– de contenir d’autres composants Une applet est une petite application que l’on exécute au travers du navigateur
– de structurer les interfaces en zone logique. WEB.
– JDialog :
C’est une classe swing qui permet de créer des dialogues sur mesure. Pour des
Les conteneurs sont tous dérivés de la classe Container, ce qui permet de construire des dialogues simples, on préfèrera utiliser la classe JOptionPane.
interfaces de structures complexes. – Il existe une classe toute faite pour obtenir une boite de dialogue permettant de
choisir un fichier: JFileChooser.
– JFrame :
C’est la classe qui remplace Frame dans l’AWT. Il s’agit d’une fenêtre avec les
boutons standards (fermeture, …) et avec un bordure.
– JWindow:
C’est la classe qui remplace Window dans l’AWT. Il s’agit d’une fenêtre sans
bordure

M.AFILAL 93 M.AFILAL 94

Les conteneurs
Containers de haut niveau
Containers génériques
– JPanel :
Object Il s’agit d’un conteneur rectangulaire opaque et invisible, qui est destiné à contenir
d’autres composants.
Contient un FlowLayout par défaut. (gestionnaire de disposition ou placement)
Les attributs généralement modifiés sont la couleur du fond, les bords, ainsi que
ObjectComponent l’appel à la méthode
– void setLayout(LayoutManager mgr):
pour gérer la manière dont ses composants sont placés.
– JScrollPane :
Container Permet de visualiser un objet trop grand.
– Au lieu d’écrire directement dans un JPanel :
textArea = new JTextArea(5, 30);
UnFrame.add(textArea, BorderLayout.CENTER);
JComponent Window Panel – On peut le mettre préalablement dans un JScrollPane :
textArea = new JTextArea(5, 30);
JScrollPane scrollPane = new JScrollPane(textArea);
JWindow Frame Dialog Applet unFrame.add(scrollPane, BorderLayout.CENTER);

JFrame JDialog JApplet


M.AFILAL 95 M.AFILAL 96
Les conteneurs Les conteneurs
JTabbedPane :
– Permet de mettre plusieurs JPanel dans des onglets. La méthode pour ajouter des onglets est JSplitPane :
add : – Permet d’afficher deux JPanels séparés verticalement ou horizontalement.
JTabbedPane tabbedPane = new JTabbedPane(); – JSplitPane hSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1,
panel2);
ImageIcon icon = createImageIcon("images/middle.gif"); Containers spécifiques
JComponent panel1 = makeTextPanel("Panel1"); – JInternalFrame :
tabbedPane.addTab("One", icon, panel1, "Does nothing"); Permet d’afficher une fenêtre dans un JFrame.
…. – JLayeredPane :
JToolBar : Permet d’afficher des JPanels à différents niveaux ou des composants en couches.
– Permet de créer une barre de boutons. Les boutons sont des instances de JButton. Un On peut donner des valeurs de niveau aux composants indiquant qui est affiché au
dessus.
ActionListener leur est attribué. Il suffit de les ajouter dans la toolbar à l’aide de la commande
add. – Utilise null Layout comme gestionnaire de position, donc positionner ses
enfants avec setBounds().
public class RecupereLesActions implements ActionListener {
– public void actionPerformed(ActionEvent e) { ….. }
}
JToolBar toolBar = new JToolBar();
JButton button = new JButton();
RecupereLesActions recupereLesActions = new RecupereLesActions();
button.addActionListener(recupereLesActions);
toolBar.add(button);

M.AFILAL M.AFILAL 98

Les composants de la librairie Swing: similaire à l’awt


Remarque
JButton JChekBoxMenuItem
AbstractButton

JMenuItem JMenu
Pour construire une application autonome graphique, il faut dériver de javax.swing.JFrame JComboBox
– On utilisera: JRadioButtonMenuItem
Des JFrames pour construire des applications. JLabel JToggleButton
JCheckBox
Des boites de dialogues afin d’afficher des messages d’avertissement ou autres.
JList
JRadioButton
Ci-dessous, on retrouve l’arborescence, qui dérive de JComponent, dont:
– Les containers: génériques, spécifiques.
JMenuBar
– Les composants de base.
JComponent
JPanel

JPopupMenu

JScrollBar JEditorPane JTextPane

JScroolPane JTextArea

JTextComponent JTextField JPasswordField


M.AFILAL 99 M.AFILAL 100
Les composants de la librairie Swing: nouveaux composants Les composants graphiques de base
JColorChooser

JFileChooser

JInternalFrame Pour utiliser un composant, il faut créer un nouvel objet représentant le composant et l'ajouter
JDesktopPane
à un conteneur qui existe avec la méthode add( ).
JLayeredPane

JOptionPane Les composants graphiques sensibles :


JProgressBar – JButton.
JRootPane
– JCheckBox.
JPopupMenuSeparator – JLabel.
JSepartor
– JTextArea.
JComponent JToolBarSeparator
JSlider – JTextField.
– …..
JSplitPane

JTabbedPane

JTable

JToolBar

JToolTip

JTree

JViewport

JInternalFrame.JDesktopPane
M.AFILAL 101 M.AFILAL 102

JLabel (étiquette) JLabel (étiquette)


Un JLabel « une étiquette »affiche du texte ou une image, sur une simple ligne en lecture uniquement Remarque
– contentPane: panneau « ou conteneur » inclus dans une JFrame dans lequel les
Possibilité: composants d’une JFrame sont insérés
– Ajouter une icône
– Spécifier la position horizontale et verticale du texte par rapport à l’icône – Les composant d’une JFrame
– Spécifier une position relative du contenu interne du composant La fenêtre,
Exemple:
Le RootPane: le container (conteneur) principal qui contient les autres composants:
– public class TesteJLabel extends JFrame { LayeredPane, MenuBar, ContentPane et le GlassPane.
public TesteJLabel() {
– setTitle("Teste de Jlabel");
– getContentPane().setLayout(new GridLayout(2,1)); Le LayeredPane: qui forme juste un panneau composé du ContentPane et de la barre
– JLabel label1 = new JLabel("PremierLabel" , JLabel.LEFT);
de menu (MenuBar),
– JLabel label2 = new JLabel("DeuximeLabel");
– getContentPane().add(label1); Le MenuBar : la barre de menu quand il y en a une...
– Font uneFonte = new Font("Serif", Font.BOLD | Font.ITALIC, 12);
– label2.setFont(uneFonte); Le ContentPane : c'est dans celui-ci que nous mettrons nos composants,
– Icon image = new ImageIcon("C:\\ImageTeste.gif");
– label2.setIcon(image); Le GlassPane: couche utilisée pour intercepter les actions de l'utilisateur avant
// alignement au centre de l’axe horizontal qu'elles ne parviennent aux composants.
– label2.setHorizontalAlignment(JLabel.CENTER);
– getContentPane().add(label2);
// permet d'arrêter le thread, associé à la fenêtre, à la fermeture de celle ci
– setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
– pack( );
} 103 104
– }
JLabel (étiquette) JLabel (étiquette)
Les constructeurs
– public JLabel() Remarque
– public JLabel(String text) – Il est possible de préciser l’alignement ou la position d’un JLabel à la création.
– public JLabel(Icon imageIcon) Pour définir ces différentes positions, on utilise des constantes (héritées de
l'interface SwingConstants) : LEFT, RIGHT, CENTER, TOP, BOTTOM,
– public JLabel(String text, Icon imageIcon) LEADING et TRAILING «en avant plan et à gauche, à droite à la fin de ligne si un
– public JLabel (String text, int horizontalAlignment) objet ComponentOrientation est attaché à un composant Swing
– public JLabel(String text, Icon imageIcon, int horizontalAlignment)
– La disposition générale du JLabel dans le conteneur est déterminée par les propriétés
Remarque setHorizontalAlignment(JLabel.RIGHT) et setVerticalAlignment(JLabel.Top).
– Dans la définition des constructeurs, on a par défaut l’alignement vertical est donné par
le JLabel.CENTER. – Il est possible de positionner un message par rapport à une icône lorsque ceux-ci figurent
– Par défaut: horizontalAlignment = JLabel.LEFT sur un objet JLabel (ou Jbutton) :
Autres fonctions setVerticalTextPosition(JLabel.CENTER);
– setForGround(Color) setHorizontalTextPosition(JLabel.TOP);
– setFont(Font)
Avec un objet font = new Font(font, style, taille) – La création d'icône fait appel à la classe ImageIcon qui possède un constructeur
permettant de lire l'icône à partir d'un fichier au format GIF (y compris les images
animées GIF89A) ou JPEG ou PNG.

– L'espace entre l'image et le texte est déterminé par la propriété iconTextGap (utilisation
105 de la méthode setIconTextGap(int)). 106

JChekbox(boite à cocher) JChekbox(boite à cocher)

Un JCheckbox est un composant graphique pouvant être soit dans l’état « on » (true) ou dans
l’état « off » (false).
– C’est en cliquant sur la boite à cocher que son état passe de la valeur « on » à la valeur « Constructeur Rôle
off » et vice-et-versa.
JCheckbox() Créer une boite à cocher sans libellé «une
Il est possible de spécifier deux icônes qui seront utilisés pour afficher les états «on» et «off» étiquette ou texte associé a la boite à cocher»
d’un JChekBox « en construisant une classe personnalisée qui implémente l’interface Icone.
JCheckbox (String label) Créer une boite à cocher avec un libellé.
Les états des JChekbox sont indépendants les uns des autres. JCheckbox (String label, boolean state) Créer une boite à cocher avec un libellé et
indication de l’état :
Cette classe possède plusieurs constructeurs false = désactivée, true = activée.
JCheckbox (Icon icon) Créer une boite à cocher en spécifiant une
image.
JCheckbox (Icon icon, boolean state) Créer une boite à cocher spécifiant une image
et un état initial.
JCheckbox (String label, con icon, boolean state) Créer une boite à cocher spécifiant un libellé,
une image et un état initial.

M.AFILAL 107 M.AFILAL 108


Événement spécifique de JCheckbox Autres méthodes de JCheckbox (héritées de AbstractButton)

void addActionListener(ActionListener L)
– Pour enregistrer un écouteur d’événement ActionEvent auprès de la boite à cocher. public void setText(String text)
L’écouteur recevra le message actionPerformed(ActionEvent) à chaque fois que – Pour spécifier le libellé de la boite à cocher
l’état de la boite à cocher aura été modifié
public void setSelected(boolean b)
void removeActionListener(ActionLitener L)
– Pour spécifier l’état de la boite à cocher.
– Pour désinscrire un écouteur d’événement.

public boolean isSelected()


– Retourne l’état de la boite à cocher

Dans l’exemple suivant, on va construire


– Des Jchekbox ordinaires
– un Jchekbox personnalisé qui va être associé à des icône personnalisés afin d’exprimer,
via les icones, les états être cocher ou non cocher d’un Jchekbox

M.AFILAL 109 M.AFILAL 110

Exemple Exemple
class AfficheIcon implements Icon {
public class TesteJCheckBox extends JFrame {
– boolean etat;
– public TesteJCheckBox() { – public AfficheIcon(boolean etat) {
setTitle("Teste boite à cocher"); this.etat = etat;
getContentPane().setLayout(new FlowLayout()); – }
JCheckBox ch1 = new JCheckBox("option1"); – public void paintIcon(Component c, Graphics g, int x, int y) {
getContentPane().add(ch1); g.setColor(Color.red);
JCheckBox ch2 = new JCheckBox("option2"); if (etat) {
– g.fillOval(x, y, getIconWidth(), getIconHeight());
getContentPane().add(ch2);
}
getContentPane().add(new JCheckBox("option3", true)); else {
– // associé un Icon à un JChekBox – g.draw3DRect(x, y, getIconWidth(), getIconHeight(), true);
Icon imageIconTrue = new AfficheIcon(true); }
Icon imageIconFalse = new AfficheIcon(false); – }
JCheckBox ch4 = new JCheckBox("option4"); – public int getIconWidth() {
ch4.setIcon(imageIconFalse);// CheckBox non sélectionné return 10;
– }
ch4.setSelectedIcon(imageIconTrue); );// CheckBox sélectionné
– public int getIconHeight() {
getContentPane().add(ch4); return 10;
this.pack(); – }
//this.setSize(150, 150); }
– } public class testeMain {
} – public testeMain() { }
– public static void main(String[ ] args) {
(new TesteJCheckBox()).setVisible(true);
M.AFILAL 111
– } M.AFILAL 112
}
JRadioButton(boutons radio) et GroupButton Exemple *

public class TesteJRadioButtonEtGroupButton extends JFrame {


– public TesteJRadioButtonEtGroupButton() {
setTitle("Teste JRadioButton et GroupButton.");
Les JRadioButton sont pareils que les JChekbox mais leurs états dépendant au sein d'un getContentPane().setLayout(new FlowLayout());
groupe de boutons (ButtonGroup). JRadioButton b1 = new JRadioButton("Homme");
JRadioButton b2 = new JRadioButton("Animal");
La classe ButtonGroup permet de gérer un ensemble de boutons (les boutons se sont des JRadioButton b3 = new JRadioButton("Machine");
objets héritant de la classe AbstractButton, exemple: ButtonGroup groupe = new ButtonGroup();
JCheckBox, JRadioButton, JButton, JMenuItem, JToggleButton b1.setSelected(true);
JCheckBoxMenuItem, JMenu, JRadioButtonMenuItem ) b1.setToolTipText("utilisateur des autres: animal et machine");
– en garantissant qu'un seul bouton du groupe sera sélectionné. b2.setForeground(Color.red);
Icon imageIconTrue = new AfficheIcon(true);
Icon imageIconFalse = new AfficheIcon(false);
Pour utiliser la classe ButtonGroup, il suffit d'instancier un objet de cette classe et d‘y ajouter b3.setIcon(imageIconFalse);
des boutons grâce à la méthode add().
b3.setSelectedIcon(imageIconTrue);
groupe.add(b1);
Exemple: groupe.add(b2);
groupe.add(b3);
getContentPane().add(b1);
getContentPane().add(b2);
getContentPane().add(b3);
setSize(250, 250);
– }
}
M.AFILAL 113 M.AFILAL 114

JButton (bouton) JButton (bouton)

Il est possible d’insérer une image à l’intérieur d’un bouton, l’image devant être manipuler au
Un JButton est un composant graphique, susceptible d’être « cliqué » par l’utilisateur, travers d’un objet de type icon.
– et qui répondra à cet événement en envoyant le message actionPerformed(…) à tous les – L’image peut être spécifiée dans le constructeur lui-même, ou en utilisant la méthode
objets qui seront inscrit auprès de lui en tant qu’écouteur. setIcon(uneIcone)

Exemple: Exemple
– public class TesteJButton extends JFrame{ – public class TesteJButton extends JFrame{
public TesteJButton() { public TesteJButton() {
– setTitle("Teste de Jbutton"); – setTitle("Teste de Jbutton");
– JButton bouton = new JButton("mon bouton"); – getContentPane( ).setLayout(new GridLayout(2, 1));
– getContentPane().add(bouton); – JButton bouton = new JButton("premier bouton");
– pack(); – getContentPane( ).add(bouton);
} //ajout d'une image dans un bouton
– } – Icon image = new ImageIcon("C:\\ImageTeste.gif");
– JButton boutonImage = new JButton("deuxieme bouton", image);
– getContentPane().add(boutonImage);
– pack();
}
– }

M.AFILAL 115 M.AFILAL 116


Les constructeurs de JButton Événement et méthodes spécifiques de Jbutton*

void addActionListener(ActionListener L)
– Pour enregistrer un écouteur d’événement ActionEvent auprès d’un bouton.
JButton() Créer un bouton, sans labelle ni image – L’écouteur recevra le message actionPerformed(ActionEvent) à chaque fois que le
bouton aura été cliqué
JButton(Icon icon) Créer un bouton avec une image void removeActionListener(ActionListener L)
– Pour désinscrire un écouteur d’événement
JButton(String text) Créer un bouton avec un labelle
Autres méthodes (héritées de AbstractButton)
– void setIcon(Icon defaultIcon)
JButton(String text, Icon icon) Créer un bouton avec un labelle et une image
Pour spécifier l’image du bouton
– void setText(String text)
Pour spécifier le labelle du bouton
– void doClick(int pressTime)
Pour donner l’impression que le bouton est pressé pour un temps pressTime en
millisecondes.

M.AFILAL 117 M.AFILAL 118

JToggleButton JComboBox (menu déroulante)


La classe JComboBox représente un menu déroulant comprenant une liste de choix préétablie
(les items), à la manière de la classe Choice de l’awt, mais donnant en plus la possibilité à
l’utilisateur de saisir une nouvelle valeur.
Cette classe définit un bouton à deux états :
– c'est la classe mère des composants JCheckBox et JRadioButton. Exemple
– public class TesteJComboBox extends JFrame {
Cette classe est utilisée lorsque l'aspect visuel du bouton doit refléter état (ON/OFF) d'une String choix[ ] = { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn",
"Uranus", "Neptune", "Pluto"};
fonctionnalité.
public TesteJComboBox() {
– setTitle("Teste de JComboBox");
Exemple: – getContentPane().setLayout(new FlowLayout());
– Icon image = new ImageIcon("C:\\ImageTeste.gif"); – JComboBox combo1 = new JComboBox();
– JToggleButton togleBouton = new JToggleButton("toggleButton"); – JComboBox combo2 = new JComboBox();
– togleBouton.setIcon(image); – for (int i = 0; i < choix.length; i++) {
– add(togleBouton); combo1.addItem(choix[i]);
combo2.addItem(choix[i]);
Remarque: – }
– La méthode setSelected() héritée de AbstractButton permet de mettre à jour l'état du – combo2.setEditable(true);
bouton. – combo2.setSelectedItem("Venus");
– La méthode isSelected() permet de connaître cet état. – combo2.setMaximumRowCount(4);
– getContentPane().add(combo1);
– getContentPane().add(combo2);
– setSize(250, 250);
M.AFILAL 119 } M.AFILAL 120
– }
Les constructeurs de JComboBox Événement spécifiques de JComboBox
public void addActionListener(ActionListener L)
– Pour inscrire un écouteur d’événement auprès de JComboBox
– L’écouteur recevra le message actionPerformed(ActionEvent) quand une sélection sera opérée par
l’utilisateur.
– Un événement sera également notifié si le ComboBox possède un champ d’édition et qu’une saisie
Pour créer un ComboBox, avec une liste aura été opérée par l’utilisateur.
public JComboBox()
de choix vide
void removeActionListener(ActionListener L)
– Pour désinscrire un écouteur d’événement
Pour créer un ComboBox avec une liste de
public JComboBox(Vector<E> items)
choix stockée dans un vector Autres méthodes de JComboBox
– public addItem(Object anObject)
Pour ajouter un nouvel élément dans la liste de choix
Pour créer un ComboBox avec une liste – int getSelectedIndex( )
public JComboBox(E [ ] items)
stockée dans un tableau Pour retourner l’indice de l’élément sélectionné
– void setSelectedIndex(int anIndex)
Pour pré-sélectionner un élément en spécifiant son indice
– Object getSelectedItem( )
Pour retourner l’élément sélectionné
– void setSelectedItem( Object anItem)
Pour pré-sélectionner un élément
– void setEditable(boolean aFlag)
Pour déterminer si le comboBox possède ou non un champ de saisie pour compléter la liste de
choix.
– void setMaximumRowCount(int count)
Pour spécifier le nombre maximum de choix qui seront affiché dans le menu déroulant. Si ce
M.AFILAL 121 tous les choix possibles, une barre de défilement sera122
M.AFILAL
nombre n’est pas suffisant pour afficher
automatiquement rajouté.

JList Exemple

C’est une classe qui représente une liste déroulante dans laquelle l'utilisateur peut choisir un Exemple
item – JList liste = new JList();
– à préférer à une comboBox lorsque le nombre d'éléments de la liste est grand
– liste.setListData(choix);
– Permet une sélection multiple.
– liste.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
Les méthodes remarquables – liste.setLayoutOrientation(liste.HORIZONTAL_WRAP);
– setSelectionMode(int) – JScrollPane scrollPane = new JScrollPane(liste);
définir le mode de sélection: – add(scrollPane );
– ListSelectionModel.SINGLE_SELECTION
Remarque pour la méthode setLayoutOrientation:
– ListSelectionModel.SINGLE_INTERVAL_SELECTION
– ListSelectionModel.MULTIPLE_INTERVAL_SELECTION – Cette méthode permet de demander le JList.VERTICAL si la couche est une simple
– setLayoutOrientation(int) colonne de cellule,
définir l'arrangement des éléments – le JList.VERTICAL_WRAP si la couche est un style de journal avec du contenu
– VERTICAL circulant vertical puis horizontalement
– HORIZONTAL_WRAP – le JList.HORIZONTAL_WRAP si le couche est un style de journal avec contenu
– VERTICAL_WRAP circulant horizontal puis verticalement.

Les méthodes qui permettent de récupérer la sélection


– getSelectedIndex(), getSelectedIndices( )
– getSelectedValue(), getSelectedValues( )

M.AFILAL 123 M.AFILAL 124


JTextComponents JTextField et JTextArea

JTextComponent est une classe générique (abstraite) qui contient toutes les caractéristiques JTextField tf = new JTextField();
qui pourraient être utiles à la création d’un petit éditeur simplifié. – Instancier un champ de saisie (limité à une ligne de texte)

Quelques Méthodes de cette classe: JTextArea ta = new JTextArea();


– copy( ); cut( ); paste( ); – Instancier une zone de saisie (qui peut comprendre plusieurs ligne de texte)
– getSelectedTexte( ); setSelectionStart( );
– setSelectionEnd( ); selectAll( ); replaceSelection( ); tf.setTex(“TextField"); ta. setTex(“TextArea");
– getText( ); setText( ); setEditable( ); – Initialiser ou modifier leur contenu
– ……
ta.getText(); tf.getText(); //Pour récupérer le texte
Cette classe ne sera jamais instanciée directement.
– Il existe 3 sous-classes importantes: tf.copy( ) ou tf.cut( ); // Pour copier
JTextField, JTextArea et JEditorPane.
– Trois autres sous classes: JPasswordField, JFormattedTextField et JTextPane qui tf.past( ); // pour coller
présentent aussi un certain intérêt.
tf.setEditable(false); // Pour interdire la saisie
Remarque:
– Si l’utilisateur doit avoir la possibilité de visualiser un contenu qui dépasse l’espace mis
à disposition dans la fenêtre d’affichage, add(tf); // Pour ajouter l’objet tf dans un conteneur
le composant texte devra être placé à l’intérieur d’un JScrollPane qui offre la
possibilité de visualiser tout le contenu au moyen d’une barre de navigation add(new JScrollPane(ta)); // Ajouter l’objet ta dans une barre de défilement.

M.AFILAL 125 M.AFILAL 126

JTextField et JTextArea JTextField et JTextArea*


Les constructeurs de JTextField public class TesteJTextFieldEtArea extends JFrame {
– JTextField( ) – public TesteJTextFieldEtArea() {
setTitle("Teste JTextField et JTextArea");
– JTextField(int largeur):
getContentPane().setLayout(new FlowLayout());
Largeur en caractères, utilisée pour calculer la preferredSize
JLabel lab1 = new JLabel("Pret");
– JTextField(String txtInitial) JTextField tf1 = new JTextField(10);
– JTextField(String txtInitial, int largeur) JPanel panel = new JPanel();
– JTextField(Document doc, String txtInitial, int largeur) lab1.setHorizontalAlignment(lab1.RIGHT);
Permet de forcer le modèle utilisé mais s’il est null, un modèle par défaut sera créé lab1.setSize(10, 20);
Réaction "en direct" à la saisie dans un JTextField , il y a deux types d'écouteurs tf1.setBackground(Color.GREEN);
lab1.setForeground(Color.RED);// couleur du premier plan
– CaretListener “ ecouteur du curseur”
// tf1.setOpaque(false);
Possède une méthode : caretUpdate(CaretEvent): elle exécuté s’il y a un
// tf1.setEditable(false);
déplacement du curseur dans le champ texte.
tf1.setText("Bonjour");
Méthodes de CaretEvent : int getDot() et int getMark() JTextArea textA = new JTextArea(5, 20);
– DocumentListener: possède trois méthodes : textA.setText("Le lignes sont vides pour l'instant");
changedUpdate(DocumentEvent e) textA.setBackground(Color.blue);
Changement du texte ou une partie du texte dans le document panel.setLayout(new FlowLayout());
insertUpdate(DocumentEvent e) panel.add(new JScrollPane(textA));
– Insertion du texte dans le document getContentPane().add(lab1);
getContentPane().add(tf1);
removeUpdate(DocumentEvent e)
M.AFILAL 127 getContentPane().add(panel); M.AFILAL 128
– Suppression du texte dans le document pack();}}
Création d’un menu Exemple *
Classes utilisées pour la création d’un menu sont:
– JMenuBar : public class TesteMenu extends JFrame{
représente la barre de menu d’une fenêtre. public TesteMenu() {
setTitle("Teste Menu");
– JMenu : JMenuBar menuBar = new JMenuBar();
options visibles dans la barre de menu. JMenuItem quitter, nouveau, aPropos;
JMenu menuFichier = new JMenu("Fichier");
nouveau = new JMenuItem("Nouveau");
– JMenuItem :
quitter = new JMenuItem("Quitter");
Items qui déclencheront par leur sélection des actions définies par le programmeur. menuFichier.add(nouveau);
menuFichier.addSeparator();
– Remarque: menuFichier.add(quitter);
Seule une instance de la classe JFrame (ou JDialog) peut héberger un menu JMenu menuAide = new JMenu("Aide");
aPropos = new JMenuItem("A propos");
On peut construire aussi JPopupMenu: Un menu surgissant se construit comme un menuAide.add(aPropos);
menu. – //un menu qui est un item d'un autre menu
JMenu menuAutre = new JMenu("AutreSousMenu");
menuAutre.add(new JMenuItem("autreItem"));
JRadioButtonMenuItem menuAide.add(menuAutre);
– Un item de menu peut être un bouton radio. Ceci permet de faire des choix – // fin
exclusifs : on utilise un ButtonGroup pour grouper les boutons radio : un seul menuBar.add(menuFichier);
bouton du groupe peut être sélectionné. menuBar.add(menuAide);
setJMenuBar(menuBar);
JCheckBoxMenuItem pack( );
– Un item de menu peut être une boîte à cocher. les méthodes isSelected() et – }
setSelected(boolean b) permettent
M.AFILAL de savoir si une boîte est cochée, et de la 129 } M.AFILAL 130
cocher ou de la décocher.

JToolTip JToolBar (barre d’outils)

Un tooltip est un texte fugitif, dépendant du contexte, qui s’affiche dans une petite fenêtre Le composant JToolBar
quand la souris s’attarde sur un objet spécifique de l’écran. – est une espèce de conteneur qui affiche ses composants à la manière d’une barre d’outils,
verticalement ou horizontalement, en s’adaptant à la surface de l’écran dans laquelle il se
trouve placé.
La classe JToolTip, elle-même sera rarement utilisée directement.
Une barre d’outil peut être flottante:
Il suffit en effet d’utiliser la méthode setToolTipText() héritée de la classe JComponent.
– l’utilisateur pouvant la déplacer et la placer où bon lui semble
en draguant la barre d’outil dans une autre région de l’écran,
Exemple:
ou même en déplaçant cette dernière dans une fenêtre externe au conteneur
– JButton bouton = new JButton("Bonjour"); d’origine.
– Bouton.setToolTipText(“tout le monde");
– add(bouton); Pour que cette fonctionnalité fonctionne correctement:
– Il est recommandé d’ajouter la barre d’outils à l’un ou l’autre des 4 cotés d’un conteneur
dont le gestionnaire de disposition est de type BorderLayout et de ne rien placer dans les
3 côtés restants.

Par défaut, une barre d’outils à la faculté d’être flottante, pour supprimer la faculté
‘flottabilité’ d’une barre d’outils:
– unToolBar.setFlotable(false).

M.AFILAL 131 M.AFILAL 132


Ajout de composants dans JToolBar Capacités communes à tous les composants

Pour créer un JToolBar, on utilise l’un des constructeurs suivant: setEnabled(boolean);


– JToolBar( ); – Activation / désactivation du composant (le composant réagit ou non aux interactions
– JToolBar(int); avec l'utilisateur)
Le paramètre permet de spécifier l’orientation de la barre d’outils: HORIZONTAL isEnabled()
(par défaut) ou VERTICAL, ces deux constantes étant définies dans l’interface – Retourne état d’activation ou non d’un objet
javax.swing.SwingConstants: JToolBar.VERTICAL ou JToolBar.HORIZONTAL setVisible(boolean);
– Visibilité / invisibilité d'un composant
– JToolBar(String) isVisible()
Le paramètre permet de spécifier un titre, qui apparaîtra lorsque la barre d’outils – Retourne état d’activation ou non d’un objet
aura quitté son port d’attache. setCursor(java.awt.Cursor)
– Apparence du curseur (changement de l'apparence du curseur en fonction du composant
– JToolBar(String, int) qu'il survole)
Les deux paramètres sont expliqué dans les deux définitions précédentes. – Exemple:
bt.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Les composants dans un JToolBar sont disposés à la manière d’un gestionnaire de type setToolTopText(String)
BoxLayout. Concrètement, pour ajouter un composant, il suffit d’utiliser la méthode add: – Bulles d'aide ("tooltips")
– unToolBar.add(unComposant); – Remarque:
Possibilité d'utiliser HTML pour formater le texte d'aide
Un espace vide peut être rajouter entre les composants au moyen de la méthode setBackGroundColor(java.awt.Color), setForeGroundColor(java.awt.Color)
addSeparator():
– unToolBar.addSeparator( ); – Couleur d’arrière plan et avant plan

M.AFILAL 133 M.AFILAL 134

Gestionnaire de disposition (de géométrie, de position ou de placement) Gestionnaire de disposition


Ces stratégies sont implantées sous forme d’ensemble de classes dérivées de la classe
LayoutManager, en charge de :
La conception d’interface graphique standard, spécifie la position et la taille des composants
en valeur absolue – Placer les composants les uns par rapport aux autres.
( c.a.d: on met en dure dans l’interface les composants dans une position et une taille
qui peut être très grandes et donc pour les petits écrans, on ne peut voir correctement – Repositionner les composants dans le cas d’ajout ou suppressions de composants
le composant), (interfaces dynamique).
– ce qui entraîne que l’interface graphique n’apparaîtra pas correctement sur toutes les
plates formes.
– Réarranger les composants lorsque le conteneur change de taille (il peut être
redimensionné par exemple parce qu’il se trouve lui-même dans un autre conteneur qui
Pour résoudre ce problème de portabilité, Java fournit un système portable de change de taille).
gestionnaire de disposition
– Repartir l’espace libre entre composants.
Ses gestionnaires de disposition permettent de spécifier les règles et les contraintes
de disposition ou d’ajout/suppression des composants au sein de l’interface
utilisateur Remarque:

Donc, la manière de positionner les composants dans une interface est contrôlée par: – Un conteneur d’interface utilisateur Java (java.awt.Container) utilise un objet spécial,
appelé gestionnaire de disposition afin de:
– Des gestionnaires de disposition (LayoutManagers).
Contrôler l’emplacement et la taille des composants chaque fois qu’ils sont affichés

M.AFILAL 135
D’arranger automatiquement lesM.AFILAL
composants dans un conteneur, selon un ensemble
136
de règles propre à chaque gestionnaire de disposition
Gestionnaire par défaut des conteneurs Taille et emplacement de l’interface utilisateur

Pour toute interface utilisateur qui dérive de java.awt.Window (comme la classe JFrame), on
Certains types de conteneurs utilisent par défaut des gestionnaires de disposition spécifiques: peut contrôler sa taille et son emplacement à l’exécution
– Tous les panneaux (Les JPanel) utilisent par défaut le gestionnaire FlowLayout
La taille de la fenêtre utilisateur, telle qu’elle est définie par l’application et avant toute
– Toutes les fenêtres (y compris les cadres et les dialogues) utilisent BorderLayout intervention de l’utilisateur, est déterminée par la méthode qui est appelée en dernier dans le
code, parmi les deux suivantes:
On peut modifier le gestionnaire par défaut d’un conteneur, au moyen de la méthode
– pack( ).
setLayout(…),
Donne à la fenêtre, automatiquement, la taille minimale en respectant la taille
– Exemple: preferredSize des composants contenus dans cette fenêtre.
leContainer.setLayout(null);
leContainer.setLayout(new BorderLayout()); – setSize(largeur, hauteur).
La taille du conteneur est définie explicitement, en pixels.

– setLocation(int x, int y)
L’emplacement de l’interface utilisateur à l’exécution sera 0,0
– jusqu’à ce que l’application donne une valeur à la propriété location du
conteneur (par exemple setLocation(..) avant que l’interface ne soit affichée)

M.AFILAL 137 M.AFILAL 138

Portabilité de l’interface utilisateur Les principaux gestionnaires


L’association d’une stratégie de placement à un conteneur se fait en
En général, pour que l’interface soit portable, – instanciant un objet de la classe dérivée de LayoutManager représentant la stratégie
– On utilise pack( ) mais pas de setSize() choisie,
– Ou bien, on prend compte les tailles des pixels des différents écrans et évaluer – Puis, en invoquant sur le conteneur la méthode setLayout(LayoutManager layout)
raisonnablement la taille à définir. pour spécifier le gestionnaire de disposition qui lui est associé
– Puis, appel de la méthode add( ) qui permet d’ajouter des composants dans un conteneur:
Exemple: La fonction add possède plusieurs formes d’appel « avec un seul paramètre ou
plusieurs paramètres), en fonction du gestionnaire choisi.
– Afficher l’interface utilisateur de sorte qu’elle occupe toujours 75% de la largeur et de la
hauteur de l’écran.
Les principaux gestionnaires mis à disposition par swing et l’awt:
Pour cela, on utilise le code suivant:
– BorderLayout. [awt]
– Dimenion screenSize = Toolkit.getDefaultToolkit().getScreenSize(); – FlowLayout. [awt]
frame.setSize(screenSize.width*3/4, screenSize.height*3/4); – GridLayout. [awt]
– BoxLayout. [swing]
– Pour positionner la fenêtre au milieu de l’écran – GridBagLayout. [awt]
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); – null.
Dimension frameSize = frame.getSize(); – …….
– if(frameSize.height > screenSize.height)
frameSize.height = screenSize.height; Remarque
– if(frameSize.width > screenSize.width) – On peut créer des dispositions personnalisées, ou ceux de Sun ou d’autres fournisseurs
– Les interfaces utilisateur sont constituées, en général, de différentes dispositions, sous
frameSize.width = screenSize.width; forme de panneaux imbriqués les uns dans les autres.
– frame.setLocation((screenSize.width - frameSize.width) /2, (screenSize.height
- frameSize.height) /2)); M.AFILAL 139 M.AFILAL 140
FlowLayout Exemple
FlowLayout est un conteneur dont les composants sont placés dans l’ordre de leur ajout.
import javax.swing.*;
Ils sont alignés de gauche a droite, puis de haut en bas import java.awt.*;
public class TesteFlowLayout extends JFrame {
Un composant est placé dans un FlowLayout en utilisant la méthode add.
– String tab[ ] = { "un", "deux", "trois", "quatre", "cinq", "six"};
Il y a plusieurs constructeurs possibles: – public TesteFlowLayout() {
– FlowLayout( ); //  FlowLayout(FlowLayout.CENTER, 5, 5). super("Teste de FlowLayout");
– FlowLayout( int align); getContentPane().setLayout(new FlowLayout());
Permet de préciser l'alignement des composants dans le conteneur (CENTER,
LEFT, RIGHT ... ). Par défaut, align vaut CENTER for (int i = 0; i < tab.length; i++) {
– FlowLayout( int, int hgap, int vgap); – getContentPane( ).add(new JButton(tab[i]));
Permet de préciser l'alignement horizontal et vertical dont la valeur par défaut est 5. }
pack();
– }
}

M.AFILAL 141 M.AFILAL 142

Exemple BorderLayout

Le conteneur est divisé en cinq zone nommées "géographiquement" :


Remarque – NORTH, SOUTH, EAST, WEST, CENTER.
– La méthode getPreferedSize() indique la taille souhaitée mais pas celle imposée.
Les composants au Nord et au Sud
– En fonction du Layout Manager, le composant pourra ou non imposer sa taille. – gardent une hauteur constante mais possèdent toujours comme largeur celle du
conteneur.
Layout Hauteur Largeur
Les composants a l’Est et a l’Ouest
Sans Layout oui oui – gardent leur largeur constante mais leur hauteur s’adapte de façon a ce qu’ils comblent
FlowLayout oui oui toujours l’espace libre
entre le bas du composant au Nord et le haut du composant Sud.
BorderLayout(East, West) non oui
BorderLayout(North, South) oui non Le composant au Centre occupe tout le reste de l’espace disponible au centre, donc sa largeur
et sa hauteur changent.
BorderLayout(Center) non non
GridLayout non non BorderLayout consacre tout l'espace du conteneur aux composants

Constructeur
– BorderLayout();
– BorderLayout(hgap, vgap);
Hgap(espace horizontal) et vgap(espace vertical) déterminent la distance entre les
composants (entre les zones)
M.AFILAL 143 M.AFILAL 144
Exemple *

public class TesteBorderLayout extends JFrame {


– String tab[] = { "NORD", "SUD", "OUEST", "EST", "CENTRE"};
– public TesteBorderLayout() {
super("Teste de BorderLayout");
getContentPane().setLayout(new BorderLayout());
getContentPane().add(new JButton(tab[0]), BorderLayout.NORTH);
getContentPane().add(new JButton(tab[1]), BorderLayout.SOUTH);
getContentPane().add(new JButton(tab[2]), BorderLayout.WEST);
getContentPane().add(new JButton(tab[3]), BorderLayout.EAST);
getContentPane().add(new JButton(tab[4]), BorderLayout.CENTER);
pack();
– }
}

Remarque:
– On peut utiliser la surcharge de la méthode add afin d’ajouter les composants dans le
panneau, comme ceci:
add("Center", new JButton(tab[4]));

M.AFILAL 145 M.AFILAL 146

GridLayout GridLayout

Remarque:
Le gestionnaire GridLayout place les composants sur une grille de cellule dont on spécifie:
– L’espace horizontal et l’espace vertical sont nuls par défaut
– le nombre de ligne ou le nombre de colonne et l’espace (en pixels) entre les cellules lors
de la création du GridLayout.
– Pour créer un GridLayout de 5 lignes, 8 colonnes, des espacements horizontaux et
verticaux de valeurs respectives 10 et 20 puis de l’associé à un conteneur, on fait:
Toutes les cellules ont obligatoirement la même hauteur (celle du composant le plus haut) et
même largeur(celle du composant le plus large) unConteneur.setLayout(new GridLayout(5, 8, 10, 20));

Chaque cellule contient un seul composant (simple ou conteneur) – Pour ajouter des composants dans un GridLayout, on utilise la méthode add. Les
composants sont placés dans l’ordre, ligne par ligne, en remplissant chaque ligne.
– qui occupe toute la place disponible dans la cellule
– Lorsque le nombre de ligne et de colonne est spécifié alors le nombre de colonne est
Outre le nombre de lignes et de colonnes qu’on peut spécifier, ignoré.
– on peut spécifier le nombre de pixels entre les cellules en utilisant les paramètres Ainsi par Exemple GridLayout(5,4) est équivalent à GridLayout(5,0). On aura 5
d’espace horizontal (hgap) et d’espace vertical (vgap) lignes (avec des lignes vides possibles, en fonction du nombre des composants à
insérer)
Constructeurs:
– GridLayout(lignes, colonnes); – GridLayout(0,4): on aura 4 colonnes (avec des colonnes vides possibles ou des cases
– GridLayout(lignes, colonnes, hgap, vgap); vides, en fonction du nombre des composants à insérer)

M.AFILAL 147 M.AFILAL 148


Exemple

public class TesteGridLayout extends JFrame {


– String tab[] = { "un", "deux", "trois", "quatre", "cinq", "six"};
– public TesteGridLayout() {
super("Teste GridLayout");
getContentPane().setLayout(new GridLayout(3, 2, 10, 10));
for (int i = 0; i < tab.length; i++) {
– getContentPane().add(new JButton(tab[i]));
}
pack();
– }
}

M.AFILAL 149 M.AFILAL 150

CardLayout CardLayout

Plusieurs méthodes permettent de sélectionner le composant à afficher, notamment :


Ce gestionnaire est typiquement utilisé pour simuler le comportement de Boite à angle ou – next (Container c)
carte de jeux: cache le composant actuellement affiché et affiche le suivant dans la liste.
– dans lesquelles le haut de la fenêtre contient un ensemble de boutons, ou autre ensemble – previous (Container c)
(un jcombobox, ..), permettant de passer d’un anglet a l’autre. cache le composant actuellement affiché et affiche le précèdent dans la liste
– last( )
Ce gestionnaire de disposition n’affiche qu’un seul composant à la fois, affiche le dernier composant,
– first( )
– on utilise (généralement) des panneaux (JPanel) comme composants.
affiche le premier composant,

Le composant affiché occupe toute la place disponible. Remarque


– l’ordre d’affichage est celui dans lequel on a ajouté les composants.
Les composants placés peuvent être indexés par des mots-clés
– La méthode show (Container, String) permet de spécifier par son index le composant à
afficher.

– Il est préférable d’utiliser un TabbedPane à la place d’un cardLayout.

M.AFILAL 151 M.AFILAL 152


CardLayout Exemple
import javax.swing.*; import java.awt.*; import java.awt.event.*;
public class TesteCardLayout extends JFrame implements ActionListener{
String tab[ ] = { "un", "deux", "trois", "quatre", "cinq", "six"};
public TesteCardLayout() {
setTitle("Teste cardLayout");
getContentPane().setLayout(new BorderLayout());
JPanel nord = new JPanel(new FlowLayout());
JPanel centre = new JPanel(new CardLayout());
for (int i = 0; i < tab.length; i++) {
– JButton but = new JButton(tab[i]);
– nord.add(but);
– but.addActionListener(this);
– centre.add(tab[i], new JButton("ONGLET " + tab[i]));
}
getContentPane().add(nord, BorderLayout.NORTH);
getContentPane().add(centre, BorderLayout.CENTER);
pack();
– }
– public void actionPerformed (ActionEvent e) {
( (CardLayout) ( (JPanel) getContentPane().getComponent(1)).getLayout()).
show((JPanel)(getContentPane().getComponent(1)), ( (JButton) e.getSource()).getText());
– }
M.AFILAL 153 M.AFILAL 154
}

BoxLayout BoxLayout
Ce gestionnaire de disposition permet d’aligner les composants selon un axe horizontal (une
seule ligne) ou un axe vertical (une seule une colonne).
– Les axes sont définis par les constantes : Avec BoxLayout:
X_AXIS (horizontale) et Y_AXIS (verticale), – chacun des composants utilise une place variable en fonction de ses dimensions
LINE_AXIS et PAGE_AXIS (dépendent de l'orientation: propriétés de l’objet PreferredSize et MaximalSize
ComponentOrientation)
– les composants sont alignés en fonction de leur paramètre alignementX et alignementY
Ce gestionnaire de disposition organise les composants, en respectant l’ordre d’addition, sur
une seule ligne (axe vertical ) ou une seule colonne(axe horizontal) Arrangement selon l’axe primaire:

Les composants sont ajoutés – Ce gestionnaire permet à chaque composant d’occuper un espace spécifique selon l’axe
– de gauche à droite pour une ligne, primaire.
– de haut en bas pour une colonne (depuis la gauche par défaut).
– Si on choisit l’axe vertical comme axe primaire,
Lorsque la ligne ou la colonne est pleine, on ne déborde pas, mais il y a réduction ou un JTextField occupera moins de place verticalement qu’un JTextArea.
masquage.

En utilisant
– composant. setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT), dans
le cas d’alignement LINE_AXIS et PAGE_AXIS, l’ajout sera
de droite vers la gauche pour une ligne
de haut vers le bas mais depuis la droite pour une colonne

Remarque: M.AFILAL 155 M.AFILAL 156


– JMenuBar et Box utilisent un BoxLayout comme gestionnaire de disposition.
BoxLayout BoxLayout et conteneur Box
Arrangement selon l’axe complémentaire
– S’il s’agit d’un BoxLayout horizontal (l’axe primaire = BoxLayout.X_AXIS ou La classe Box (est un conteneur, sous-classe de Container) a un BoxLayout pour gestionnaire
BoxLayout.LINE_AXIS), de disposition.
le gestionnaire tentera de rendre chaque composant aussi haut que le plus haut – C'est une classe très utile : elle est d’un maniement plus simple que JPanel et elle
parmi tous les composants. possède des constructeurs intéressants (en particulier pour gérer les espaces).

– S’il s’agit d’un BoxLayout vertical (l’axe primaire = BoxLayout.Y_AXIS ou Pour améliorer la disposition des éléments on peut :
BoxLayout.PAGE_AXIS), – régler les espaces entre les éléments en ajoutant des composants invisibles:
le gestionnaire tentera de rendre chaque composant aussi large que le plus large composants Strut et Glue (pour placer un menu Aide à droite dans une barre de
parmi tous les composants. menus par exemple), Filler et Rigid Area.

– Si le composant ne peut s’accroître de cette manière (sachant que la size max est donné Pour enjoliver les cadres : utiliser des JPanel pour contenir les Box, et leur mettre des bords
par getMaximumSize()), (avec titres ou autres).
le gestionnaire appliquera les propriétés Y-Alignement ou X-Alignement du
composant afin de déterminer comment placer le composant au sein de l’espace
disponible.

Constructeur:

– new BoxLayout(unConteneur, BoxLayout.Y_AXIS);


Le premier paramètre correspond à un conteneur (un Panel en général), et le second
indique l’axe primaire: X_AXIS ou LINE_AXIS ou Y_AXIS ou PAGE_AXIS.

– Pour ajouter un composant dans le conteneur, on utilise la méthode add:


M.AFILAL 157 M.AFILAL 158
Add(unComposant)

BoxLayout et conteneur Box BoxLayout et conteneur Box


Le composant Box est un conteneur transparent dont le gestionnaire est BoxLayout. on peut
ajouter des composants invisibles:
Tout container utilisant un BoxLayout peut utiliser les composants glue, struts et aire.
– Une glue qui fait du remplissage qui peut s’allonger en verticale ou en horizontal(comme – Container conteneur = ...
une colle ou une sorte de ressort): – conteneur.setLayout(new BoxLayout(conteneur, BoxLayout.Y_AXIS));
Box.createHorizontalGlue( ), Box.createVerticalGlue( ) – conteneur.add(Box.createHorinzontalStruts(5));

– Un strut, est un composant réduit à un espacement rigide « de taille fixe » qui Tout espace supplémentaire est absorbé par la glue.
représentent:
soit un séparateur horizontal avec une largeur fixée: Plusieurs glues peuvent se partager l’espace libre.
– Box.createHorizontalStrut(5)
soit un séparateur verticale avec une hauteur fixée: Remarque :
– Box.createVerticalStrut(5)
– On centre les composants en mettant une glue des deux côtés
– Un area (un aire) est une zone rigide avec une dimension fixée en largeur et en hauteur:
Box.createRigidArea(new Dimension(50, 20)))). – On maintient les composants aux bords en insérant une glue au milieu

– Un Filler (un remplisseur), il défini un espacement non rigide avec une dimension
minimale, une dimension préférée et une dimension maximale:
new Box.Filler(new Dimension(5,100), new Dimension(50,150), new
Dimension(Short.MAX_VALUE,200))

M.AFILAL 159 M.AFILAL 160


JTabbedPane (panneaux à onglets)
Exemple
Le JTabbedPane empile les composants (généralement des panneaux) les uns sur les autres,
public class TesteBoxLayout extends JFrame { comme dans un jeu de cartes.
– public TesteBoxLayout() {
super("Teste BoxLayout"); On ne peut voir qu’un composant à la fois et on peut feuilleter les panneaux à l’aide des
getContentPane().setLayout(new BoxLayout(this.getContentPane(), anglets correspondant.
BoxLayout.Y_AXIS));
JTextField champText = new JTextField();
JTextArea textArea = new JTextArea(4, 20);
JButton but = new JButton("boule", new ImageIcon("C:\\ImageTeste.gif"));
getContentPane().add(new JLabel("ChampText"));
getContentPane().add(champText);
getContentPane().add(new JLabel("TexteArea"));
getContentPane().add(textArea);
getContentPane().add(but);
pack();
– }
}

M.AFILAL 161 M.AFILAL 162

JTabbedPane (panneaux à onglets) JTabbedPane (panneaux à onglets)


Constructeurs: Permettent de créer un panneau à onglets avec un emplacement spécifique des
onglets et une politique de disposition des onglets: Pour ajouter une carte dans un TabbedPane, on utilise la méthode addTab ( qui existe sous
trois formes) ou la méhotde insertTab
– JTabbedPane( ); // emplacement et politiquePosition par défaut.
– JTabbedPane(int tabPlacement); // politiquePosition par défaut. – addTab(String title, Composant component)
– JTbbedPane(int tabPlacement, int tabLayoutPolicy); Pour ajouter une nouvelle carte en spécifiant son étiquette (un texte) et le composant
qui sera affiché une fois sélectionné.
L’emplacement des onglets peut prendre l’une ou l’autre des valeurs suivantes:
– JTabbedPane.TOP, JTabbedPane.BOTTOM, JTabbedPane.LEFT ou – addTab(String title, Icon icon, Composant component)
JTabbedPane.RIGHT. Permet de spécifier une image de type Icon qui serait associée au texte de
– Par défaut, on a JTabbedPane.TOP l’étiquette. Le texte, tout comme l’image, peuvent prendre la valeur null

La politique de disposition des onglets peut prendre l’une ou l’autre des valeurs – addTab(String title, Icon icon, Composant component, String tip)
suivantes: Permet en plus de spécifier un tooltip (texte d’aide fugitif) qui serait associé à
– JTabbedPane.WRAP_TAB_LAYOUT l’onglets
ou
– JTabbedPane.SCROLL_TAB_LAYOUT. – insertTab(String title, Icon icon, Composant component, String tip, int index)
Pour insérer une carte, qui occupera la position indiquée par l’index, passé en
paramètre.
– Par défaut, on a WRAP_TAB_LAYOUT

Remarque
– La politique de disposition est mise en œuvre dès l’instant qu’il n’y a pas assez de place
pour afficher les onglets sur une seule ligne ou une seule verticale.
M.AFILAL
– Par défaut (dans les deux premiers constructeurs): TOP et WRAP_TAB_LAYOUT 163 M.AFILAL 164
JTabbedPane (panneaux à onglets)
JTabbedPane (panneaux à onglets)
Pour retirer des cartes:
– Remove(int index)
Pour supprimer l’onglet correspond à l’indice passé en paramètre
– removeAll( )
Pour supprimer tous les onglets.

Événements spécifiques:
– addChangeListener(ChangeListener L)
Pour s’inscrire un écouteur de changement
– removeChangeListener(ChangeListener L)
Pour retirer un écouteur de changement.

M.AFILAL 165 M.AFILAL 166

Exemple * GridBagLayout

public class TesteTabbedPane extends JFrame{ GridBagLayout est une disposition extrêmement souple et puissante
– String tab[ ] = { "un", "deux", "trois", "quatre", "cinq", "six"}; – Elle permet un meilleur contrôle du positionnement des composants sur la grille
– public TesteTabbedPane() { (matrice) que celui fourni par Gridlayout.
super("Teste TabbedPane");
getContentPane().setLayout(new BorderLayout()); GridBagLayout positionne les composants horizontalement et verticalement sur une grille
JTabbedPane centre = new JTabbedPane(JTabbedPane.BOTTOM, rectangulaire.
JTabbedPane.SCROLL_TAB_LAYOUT); – il n’est pas nécessaire que les composants soient de même taille, chacun pouvant remplir
for (int i = 0; i < tab.length; i++) { plusieurs cellules.
– centre.addTab(tab[i], new JButton("ONGLET " + tab[i])); – il place les composants sur une grille, mais la hauteur des lignes et la largeur des
colonnes peut varier.
}
– un composant est placé sur une cellule dans la grille (sa position) mais il peut occuper
getContentPane().add(centre, BorderLayout.CENTER); plusieurs cellules en hauteur ou en largeur
pack();
– } GridBagLayout détermine l’emplacement des composants qu’il contient en se basant sur:
} – les contraintes et la taille minimum et préférée de chaque composant,
– La taille préférée (preferredSize) du composant est utilisée si la taille du conteneur le
permet si non, la taille minimale est utilisée.

Pour utiliser un GridBagLayout, il faut


– construire un objet de type GridBagConstraints qui spécifie de quelle manière le
composant sera placé dans le GridBaglayout

M.AFILAL 167 M.AFILAL 168


GridBagLayout GridBagLayout

Bien que GridBagLayout puisse supporter des grilles complexes,


Les lignes ci-dessous décrivent plusieurs champs de la classe GridBagConstraints
– il se comportera mieux si on organise les composants en panneaux plus petits et – gridx:
imbriqués dans le conteneur GridBagLayout . Colonne où sera placé le composant (par défaut = RELATIVE)

– gridy:
– Ces panneaux imbriqués peuvent utiliser d’autres dispositions, et contenir d’autres
Ligne où sera placé le composant (par défaut = RELATIVE)
panneaux de composants si nécessaires.
– gridwidht:
La première étape lors de l’utilisation d’un GridBaglayout est de schématiser l’apparence de Nombre de colonnes couverte par le composant (par défaut = 1)
l’interface utilisateur.
– gridheight:
– Tracer une grille sur l’interface afin de décomposer ses éléments en lignes et colonnes. Nombre de lignes couverte par le composant (par défaut = 1)

– Les numéros de lignes et de colonnes serviront à indiquer l’emplacement exacte de


chaque composant.
Les numéros de lignes et de colonnes commencent par 0.

M.AFILAL 169 M.AFILAL 170

GridBagLayout GridBagLayout

fill:
Les lignes ci-dessous décrivent plusieurs champs de la classe GridBagConstraints
– Indique dans quelle direction doit éventuellement augmenter le sous-composant dans le cas de
redimensionnement
– Les deux champs weightx et weighty permettent de définir comment l'espace Elle est caractérisée par l’une des constantes suivantes de GridBagConstraints:
supplémentaire, existant sur une ligne ou colonne sera distribué parmi les composants – NONE: signifie pas de changement dans la dimension horizontale et dans la dimension
horizontalement (weightx) et verticalement (weighty). verticale du composant (valeur par défaut).
– VERTICALE: signifie que la dimension verticale du composant sera modifiée.
weightx: « le poids horizontale associé à un composant » – HORIZONTAL: signifie que la dimension horizontal du composant sera modifiée.
– Partie de l’espace supplémentaire horizontale à attribuer à un composant(cas – BOTH: signifie que la taille du composant pourra changer dans les deux dimensions
de redimensionnement).. Les composants peuvent s’élargir si on leur attribue anchor:
de l’espace supplémentaire – Définie l’emplacement du composant quand ce dernier est plus petit que son espace réservé:
si weightx = 0 le composant garde sa taille horizontale dans le cas de Elle est associée aux constantes suivantes:
redimensionnement à condition que tous les autres composants se – NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST,
trouvant dans la même colonne ne s’agrandit pas. NORTHWEST ou CENTER (par défaut centre)
REMAINDER:
weighty: « le poids verticale associé à un composant »
– Spécifie qu’un composant est le dernier composant dans sa ligne ou sa colonne.
– Partie de l’espace supplémentaire vertical à attribuer(cas de RELATIVE:
redimensionnement. . Les composants peuvent s’agrandir si on leur attribue de – Dans le cas gridx ou gridy:
l’espace supplémentaire.
spécifie qu’un composant est placé juste après le précédant composant placé.
si weighty = 0 le composant garde sa taille verticale dans le cas de – gbc.gridy = GridBagConstraints.RELATIVE;
redimensionnement à condition que tous les autres composants se – Dans le cas gridwidth, gridheight:
trouvant dans la même ligne ne s’agrandit pas).
spécifie qu’un composant est l’avant dernier composant dans sa ligne ou sa colonne
INSET
La somme des poids horizontaux d’une même ligne doit être 1. – spécifie le remplissage externe du composant, spécifie l’espace minimum entre le composant et la
La somme des poids verticaux d’une même colonne doit être 1. frontière de sa zone d’affichage (donnant les marges en haut, à gauche, à droite et en bas ).
M.AFILAL 171 M.AFILAL 172
Exemple Aucun gestionnaire de placement
ipadx:
– ajoute des pixels « ipadx » à la taille minimum du composant en largeur Il suffit de spécifier la valeur null lors de l’appel a setLayout.
– Espacement à gauche et à droite autour du composant
largeurNew = largeurOld+2*ipadx.
Il faut alors spécifier les coordonnées des composants dans un repère dont
ipady:
– ajoute des pixels « ipady » à la taille minimum du composant en hauteur – l’origine est le coin en haut à gauche conteneur,
– Espacement au dessus et au dessous du composant. – l’axe des X dirigé vers la droite
– l’axe des Y vers le bas.
Voir la classe: – On utilise pour cela la méthode
– ProjetInterfaceGraphSwing.TesteGridBagLayout
setBounds (int x, int y, int w, int h) définie dans la classe Component.
A faire
– ProjetInterfaceGraphSwing. ExoAvecGridBagLayout
– ProjetInterfaceGraphSwing. Exo1AvecGridBagLayout

M.AFILAL 173 M.AFILAL 174

Exemple Boite de dialogues


Une boîte de dialogue:
public class TesteNullLayout extends JFrame { – s’affiche dans le contexte d’une autre fenêtre: son parent.
– public TesteNullLayout() { – gère la saisie de données et/ou affiche des messages.
super("Teste de nullLayout");
getContentPane().setLayout(null); Il y a deux types de boite de dialogue:
JButton but = new JButton("UN"); – JDialog:
but.setBounds(24, 24, 96, 28); C’est une fenêtre Window spécialisée permettant de définir une boîte de dialogue
getContentPane().add(but); personnalisée.
JButton but2 = new JButton("DEUX"); – JOptionPane:
but2.setBounds(24, 84, 96, 28); C’est une classe de Java qui permet de créer facilement des boîtes de dialogue
prédéfinies.
getContentPane().add(but2);
setSize(250, 250);
Les boîtes de dialogues ont deux types de fonctionnement distincts :
– }
– Modal:
}
La fenêtre de dialogue qui reste toujours au premier plan, au détriment des autre
Remarque: fenêtres (Exemple: boite de dialogue pour entrer un mot de passe)
– Il est possible de construire son propre Layout – Non modal:
– Un layout doit implanter l’interface java.awt.LayoutManager ou La fenêtre de dialogue peut rester à l’écran sans bloquer les autres fenêtres de
java.awt.LayoutManager2 l’application.
Elle sert principalement pour des remarques.

Remarque:
– La valeur par défaut de JDialog est non modal
M.AFILAL 175 – La valeur par défaut de JOptionPane est modal, si son parent est non null. 176
Boite de dialogues: JDialog Boite de dialogues: JDialog
un Jdialog est une fenêtre secondaire permettant à l'utilisateur:
– d’afficher des messages informatifs ou d’avertissements,
– de demander certaines informations à l’utilisateur, Constructeur (exemple) Barre de titre Parent Mode
– de répondre à une question, etc….
Une fois les actions terminées, la boite de dialogue se referme. JDialog( ) vide hidden frame non modal

Un JDialog dépend toujours d'une fenêtre (frame ou Dialog ou Window ) qu'on appelle son JDialog(Frame parent, String title) title parent non modal
propriétaire (owner).
– Le JDialog subira les mêmes actions que sa fenêtre propriétaire, JDialog(Frame parent, boolean modal) vide parent modal (si true)
par exemple,
JDialog(Frame parent, String title,
– si le propriétaire est iconisé, le JDialog disparaîtra pour réapparaître dés que title parent modal (si true)
boolean modal)
son propriétaire sera agrandi.

Pour construire un JDialog, on peut utiliser, comme pour les JFrame: JDialog(Frame parent) Vide Parent non modal
– sa méthode getContentPane( ) qui renvoie son panel racine, on peut ensuite y mettre ce
que l'on veut comme composant.
– sa méthode setContentPane() pour fixer brutalement son panel racine.

Exemple:
– Un JFileChooser est un JDialog tout prêt pour sélectionner un fichier.
M.AFILAL 177 M.AFILAL 178

Exemple *
Boite de dialogues: JDialog
public class TesteJDialog extends JFrame {
– JDialog boitDia;
– public TesteJDialog() {
setTitle("Teste JDialog");
boitDia = new JDialog(this, "Avertissement !!!. ", true);
getContentPane().setLayout(new FlowLayout());
setLocation(350, 350);
setSize(250, 250);
boitDia.setSize(150, 150);
/*boitDia.setLocation( (int) (getLocation().getX() + (getWidth() -
boitDia.getWidth()) / 2), (int) (getLocation().getY() + (getHeight() -
boitDia.getHeight()) / 2));*/
boitDia.setLocationRelativeTo(this);
setVisible(true);
boitDia.setVisible(true);
– }
}

M.AFILAL 179 M.AFILAL 180


Boite de dialogues : JOptionPane
Boite de dialogues : JOptionPane

Les prototypes des méthodes, dans le cas le plus générale, sont donnés par:
Un JOptionPane permet d’afficher des boîtes de dialogue simples sans avoir à gérer d’objet,
c’est une fenêtres modales: elle bloque l’exécution. – static int showConfirmDialog(Component parentComponent, Object message,
String title, int optionType, int messageType, Icon icon)
La classe JOptionPane définit un certain nombre de méthodes statiques, de la forme
showXxxDialog afin de créer et d’afficher des boîtes de dialogue modales standards. – static Object showInputDialog(Component parentComponent, Object message,
String title, int messageType, Icon icon, Object[ ] selectionValues,
Object initialSelectionValue)

– static void showMessageDialog(Component parentComponent, Object message,


Méthodes Description Retourne String title, int messageType, Icon icon)
Demande une question qui se répond par
showConfirmDialog Option – static int showOptionDialog(Component parentComponent, Object message, String title,
oui/non/annulé.
int optionType, int messageType, Icon icon, Object[ ] options, Object initialValue)
showInputDialog Demande de taper une réponse. String (Object)

showMessageDialog Affiche un message à l’utilisateur. Rien

Une méthode générale réunissant les 3


précédentes.
showOptionDialog Option
Il peut afficher plusieurs boutons, un message
ou un ensemble
M.AFILAL
de composants JButton. 181 M.AFILAL 182

Boite de dialogues : JOptionPane Boite de dialogues : JOptionPane


OptionType :
Les paramètres de ces méthodes sont comme suit : – Indique les boutons par défaut qui seront affichés:
DEFAULT_OPTION
– parentComponent YES_NO_OPTION
YES_NO_CANCEL_OPTION
La composante qui est le parent de la boîte de dialogue. C’est un conteneur de type
Frame ou Dialog ou Window. Si le paramètre est null, la boîte de dialogue sera OK_CANCEL_OPTION
centrée à l’écran.
Options:
– Si l’affichage par défaut n’est pas approprié, il est possible de fournir un tableau d’objets
– message (habituellement String) qui seront affichés à la place du libellé par défaut.
Le message affiché sur la boîte de dialogue.
Icon :
– messageType – L’icône qui sera affiché sur la boîte de dialogue. Un icône par défaut est associé au paramètre
messageType.
Le style de message, souvent associé à un icône par défaut. Les valeurs possibles
sont :
Title :
– Le titre de la boîte de dialogue.
Type Icône par défaut (Look & Feel Java)
InitialValue :
ERROR_MESSAGE – La valeur par défaut des options.

INFORMATION_MESSAGE Valeur retournée:


– Si une méthode showXxxDialog retourne un entier (cas de retour de type option), les valeurs
WARNING_MESSAGE possibles sont :
YES_OPTION
NO_OPTION
QUESTION_MESSAGE
CANCEL_OPTION
M.AFILAL 183 M.AFILAL 184
OK_OPTION
PLAIN_MESSAGE aucun
CLOSED_OPTION
Boite de dialogues : JOptionPane Boite de dialogues : JOptionPane
Pour poser une question et saisir la réponse :
– String rep = JOptionPane.showInputDialog("Quel est votre album préféré?")
Les méthodes showInternalXXXDialog: limitent les déplacements de la boite de dialogue à
l'intérieur de la fenêtre. C’est une boîte de dialogue de type QUESTION_MESSAGE.
Le texte sur les boutons et le titre sont par défaut et dépendent de la langue utilisée
par le système
– static int showInternalOptionDialog(Component parentComponent, Object message,
String title, int optionType, int messageType, Icon icon, Object[] options,
Object initialValue) Pour afficher un message en spécifiant le type et titre, la boîte de dialogue sera centrée à
l’écran, n’aura qu’un bouton d’option et ne retournera aucun résultat.
– static void showInternalMessageDialog(Component parentComponent, Object message, – JOptionPane.showMessageDialog(null,"Bonjour le monde", "Afficher un message",
String title, int messageType, Icon icon) JOptionPane.INFORMATION_MESSAGE);

– static Object showInternalInputDialog(Component parentComponent, Object message, Pour afficher un message en offrant des boutons comme options de réponse.
String title, int messageType, Icon icon, Object[] selectionValues, – int rep = JOptionPane.showConfirmDialog(null,"Ça va bien?", "Bonjour",
Object initialSelectionValue) JOptionPane.YES_NO_OPTION);
if(rep == JOptionPane.YES_OPTION) //traitement si l’utilisateur a appuyé sur oui.
– static int showInternalConfirmDialog(Component parentComponent, Object message, else //traitement si l’usager a appuyé sur non.
String title, int optionType, int messageType, Icon icon) – Le nombre de boutons et le texte qui y est inscrit dépend du paramètre optionType, la
langue du texte des boutons dépend de la configuration du système.

M.AFILAL 185 M.AFILAL 186

Boite de dialogues : JOptionPane Boite de dialogues : JOptionPane


Pour offrir une liste de réponses possibles, il faut utiliser la méthode showInputDialog. Pour changer l’affichage par défaut sur les boutons, il faut utiliser showOptionDialog.
– String[ ] langues = {"Français", "Anglais", "Espagnol"} – Le nombre de boutons dépend du paramètre optionType.
– //Le tableau avec les icônes à afficher sur les boutons.
– String choix = (String) JOptionPane.showInputDialog( null, "Choisissez la langue de Object[ ] optionsBoutons = { new ImageIcon("shrek.GIF"), new
travail", "Sélection langue", JOptionPane.QUESTION_MESSAGE, null, langues, ImageIcon("shrek2.GIF"), new ImageIcon("shrek3.GIF") };
langues[0]);
– On a trois boutons, donc
if(choix !=null) …. // Traitement si pas annulé. optionType= YES_NO_CANCEL_OPTION
– null // parent int reponse = JOptionPane.showOptionDialog(null, "Cliquez sur un film", "Films" ,
– "Choisissez la langue de travail" // message, JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE, null, optionsBoutons,
– "Sélection langue" // titre, optionsBoutons[0]) ;
– JOptionPane.QUESTION_MESSAGE // type de message, if(reponse == JOptionPane.YES_OPTION ) System.out.println("Shrek");
– null // icône, = null conserve icône par défaut, else if (reponse == JOptionPane.NO_OPTION ) System.out.println("Shrek 2");
– langues /* Options = choix (un tableau d'objets), else System.out.println("Shrek 3");
– langues[0] // valeur par défaut qui sera affichée
– Remarque:
Remarque Le premier bouton correspond au bouton Oui, le second à Non et le dernier à
– les choix sont passés dans un tableau d’objets, s’ils sont des images (par exemple, des Annulé.
icônes), elles sont affichées, sinon, les chaînes retournées par toString sont affichées. S'il y a plus de trois boutons dans le tableau optionsBoutons , les autres boutons
correspondent à la constante CANCEL_OPTION.

M.AFILAL 187 M.AFILAL 188


Boite de dialogues : JOptionPane Autres Effets: bordures et marges
Autre exemple avec showOptionDialog. Permet d’afficher plusiuers boutons dans le libellé est
donné le tableau choixIntervalle et où les valeurs d’options sont spécifiées par les premiers boutons. La classe java.awt.Insets permet de gérer les marges d’un composant
– public void choisMultipleBoutons() { – Insets(int top, int left, int bottom, int right)
Object[] choixIntervalle = {"18-25 ans", "26-35 ans", "36-45 ans", "46-55 ans", "56 ans et
plus"};
toute classe qui implémente l’interface javax.swing.border permet de gérer les bordures des
int choix = JOptionPane.showOptionDialog(null, "Quelle est votre tranche d'âge?", "Tranche
d'age", composants Swing
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, – panel.setBorder(new BorderUIResource.TitledBorderUIResource(new
choixIntervalle, choixIntervalle[0]); BorderUIResource.MatteBorderUIResource(largeurHaut, largeurGauche, largeurBas,
if (choix == JOptionPane.YES_OPTION) { System.out.println("YES_OPTION");} largeurDroit, CouleurBordure), titreBordure, posX_titreBordure, posY_titreBordure));
else if (choix == JOptionPane.NO_OPTION) {System.out.println("NO_OPTION"); + ",
choix = "+choix}
else {System.out.println(" Ni YES_OPTION Ni NO_OPTION");}
switch (choix) {
– case 0: System.out.println("ton age est entre 18 et 25");
break;
– case 1: System.out.println("ton age est entre 26 et 35");
break;
– case 2: System.out.println("ton age est entre 36 et 45");
break;
– case 3: System.out.println("ton age est entre 46 et 55");
break;
– default: System.out.println("ton age est 56 et plus");
break;
M.AFILAL 189 M.AFILAL 190
}

Gestion des événements Gestion des événements


Les événements sont des classes ou des interfaces appartenant aux packages java.awt.event
Un événement est un "stimulus, excitation, stimulation" apporté à une interface graphique. ou javax.swing.event
Un évènement reflète « ou indique » une action de l'utilisateur (click, passage souris, – Ses classes permettant la gestion des événements dérivent de:
redimensionnement de la fenêtre, etc.) dans une interface graphique. – java.util.EventObject:
– Un tel événement a toujours: la classe d'évènements la plus générique (elle dérive directement de
une source: java.lang.Object) avec des sous classes: ConnectionEvent,
– l’entité qui l’a provoquée : souris, clavier, bouton, etc. PopupMenuEvent, TreeSelectionEvent, Java.awt.AWTEvent ,…
un destinataire: Java.awt.AWTEvent:
– une fenêtre, un bouton, une barre de défilement, etc. classe racine, dont dérive tous les évènements de l'AWT et certains
événements de Swing : c'est la classe mère des évènements
– Ces événements peuvent être de différentes natures(un click, une sélection, perte de d'interfaces utilisateurs.
focus, …) et provenir de différentes sources (bouton, souris ou autre).
–  et des classes ou interfaces du package javax.swing.event.
La gestion de l'évènement est déléguée « ou transmise » à un écouteur d'évènements qui
active le traitement associé selon le type d’évènement (Action, Key, Mouse listener) Au-dessous, on a la hiérarchie des classes et interfaces, de java.awt.AWTEvent, qui gèrent les
événements
Exemple événements:
– événements souris:
click ou déplacement, entrée ou sortie du curseur de la souris dans une certaine
portion de l’écran, surface image, etc.
– événements clavier:
appui sur une touche, une touche relevée, etc.

Depuis Java 1.1, on introduit la notion d’écoute d’événements :


– N’importe quel objet peut potentiellement réagir aux évènements qui surviennent sur un
M.AFILAL
191 M.AFILAL 192
autre objet, même s’ils ne sont pas dans le même conteneur.
Les événements : une hiérarchie de classes structurées
Les événements : une hiérarchie de classes structurées
Remarques sur la hiérarchie :

– FocusEvent : – ItemEvent :
Se produit s’il y a changement de place du curseur déclenché quand un item d'un objet (implémentant ItemAdjustable) a changé d'état
– InputEvent : « classe abstraite » (sélection ou désélection d’un item d'une liste, cochage d'une case …)
Se produit lorsque l’événement provient du clavier (KeyEvent) ou souris – AdjustmentEvent :
(MouseEvent) déclenché quand une valeur a été modifiée dans un objet implémentant Adjustable
– KeyEvent : (barre de défilement, JScrollBar,…)
Se produit lorsque un événement provient du clavier. Exp : pression d'une touche. – TextEvent :
– MouseEvent : déclenché si la valeur d'un objet texte a changé.
Se produit lorsque un événement provient de la souris. Exp : déplacement de la
souris. Remarque:
– ContainerEvent : – Des méthodes sont attachées à chacune de ces classes pour avoir accès à plus de détails
Lors d'ajout ou de suppression d'un composant dans un conteneur sur l'événement.
– WindowEvent : Exemple:
Lors d’ouverture, fermeture, redimensionnement et mouvement de la fenêtre – récupérer le composant source de l'événement,
– ActionEvent : – la position de la souris lors du click,
Lors des actions effectuées sur des objets de l'interface (click sur un bouton, – Etc.….
double-click dans une liste, …)

M.AFILAL 193 M.AFILAL 194

Les événements : une hiérarchie de classes structurées Les événements : une hiérarchie de classes structurées: non

Tout événement a: Le message envoyé par l’écouteur, en réponse de l’événement xxxEvent déclenchée par le
– une source composant graphique (la source de l’événement), correspondra:
– une destination qui va subir les conséquences de cet événement.
au résultat de l’appel d’une méthode, définie dans la classe de l’écouteur, qui prend
en argument l’événement reçu ( xxxEvent).
Exemple d’événement:
– Ceci entraîne une action sur la destination via le code de cette méthode.
– Clique sur un bouton,
– déplacement de la souris sur une zone,
Pour lier et associer, l’objet écouteur à l’objet écouté qui peut déclencher un événement
– pression sur un bouton du clavier ou autre. xxxEvent,
– le composant écouté (la source) doit appeler la méthode addXXXListener() en passant en
Exemple de classes événement: paramètre l’objet Ecouteur qui se chargera de traiter l’événement.
– ActionEvent, ItemEvent, KeyEvent, MouseEvent, … objetEcouté.addxxxListener(ObjetEcouteur)
– Donc, Ces écouteurs doivent être explicitement affectés aux composants concernés
Un composant graphique peut déclencher un événement xxxEvent, qui doit être capturé par
un écouteur (un observateur) capable de le traiter. Remarque
– Un composant peut déclencher plusieurs types d’événements.
– xxx est la catégorie d’événement déclenchée par le composant graphique. – Il faut alors un écouteur pour chaque type d’événement afin de le gérer
Exemple de catégorie
– key, mouse, window, …
– L’écouteur est un objet d’une classe
implémentant l’interface xxxListener
ou extends une classe xxxAdapter.
M.AFILAL 195 M.AFILAL 196
Principe de délégation: non Principe de gestion des événements**
Pour chaque composant pouvant déclencher un événement, donc nécessitant une gestion
Les composants stimulés(clique sur un composant par exemple), délèguent la gestion des d’événement, on doit:
événements utilisateur à un objet d’une classe (externe, interne ou anonyme) – spécifier le type ou la catégorie d’événement pour lequel il doit réagir (exemple de catégorie:
Mouse, Key, Action …, )
Principe de délégation Pour gérer ce type d’événement (xxxEvent: xxx est la catégorie de l’événement)
– on doit spécifier un objet observateur d’événement (cad un écouteur ou un Listener),
– Une source d'événement (un composant graphique dérivant de Component) qui est C’est un objet d’une classe (interne ou externe ou anonyme) qui gèrera l’événement.
l’objet observé: – L’écouteur est un objet d’une classe
Émet un événement vers un observateur (délégué ou écouteur ou un listener) implémentant l’interface xxxListener
capable de le traiter. ou extends une classe xxxAdapter
Pour cela:
– L’observateur (objet d’une classe implémentant les interfaces Listener ou extends des – L’observateur doit s’abonner ou être relié aux événements du composant.
classes Adapter) – Car la gestion de l'évènement est déléguée « ou transmise » à un écouteur d'évènements qui
active le traitement associé selon le type d’évènement (Action, Key, Mouse listener)
Indique qu'il est intéressé par l’événement envoyé par la source, en implémentant
une (ou plusieurs) interface graphique spécifique dérivant de java.util.EventListener Pour s’abonner:
ou extends une classe adaptatrice. – Un observateur s’abonnera à une catégorie d’événements déclenchée par l’objetObservé, en
faisant appel à la méthode addXXXListener(.):
– Pour relier effectivement la source et l’observateur, ce dernier doit préalablement
s'enregistrer auprès de la source (l’objet observé) via la méthode addxxxListener(.); objetObservé.addXXXListener(objetObservateur) ;

– avec « XXX » désigne la catégorie d’événement (mouse, key, action)


– objetObservé est l’objet déclencheur de l’événement
M.AFILAL 197 – objetObservateur est l’objetM.AFILAL 198
écouteur qui se chargera d’activé et traiter l’événement

Principe de gestion des événements Listes des interfaces « Listener »

la classe de l’observateur ou de objetObservateur Remarque:


– doit implémenter les interfaces XXXListener ou les classes XXXAdapter
– Le message envoyé par l’observateur, en réponse du stimuli de l’objet observé ou de – Un observateur doit implémenter tous:
l’événement déclanché, correspondra:
au résultat de l’appel d’une méthode définie dans la classe de l’observateur et les gestionnaires (les interfaces XXXListener ou les classes XXXAdapter) de tous
invoquée par l’objet objetObservateur . les événements appartenant aux catégories (XXX) pour lesquelles il s’est abonnée

Exemple: – car l’observateur est susceptible de recevoir un message de l’un ou l’autre des
événements pour des catégories d’événements différents.
– un observateur s’abonnant aux événements d’un bouton, clique sur le bouton, donc
l’événement est de catégorie « Action »
Une ou plusieurs méthodes doivent être prévue pour chacun d’entre eux

leBouton.addActionListener(this) ; Exemple:
– Cas de clique de boutons ou déplacement de souris dans une fenêtre qui est définie (c.a.d
La JFrame contenant le bouton est l’observateur, elle doit: la fenetre) comme observateur des deux événements

– Implémenter l’interface ActionListener

– Définir toutes les méthodes de l’interface ActionListener afin de répondre au


clique du bouton.

M.AFILAL 199 M.AFILAL 200


Explication des catégories d’événements Les listeners

Les interfaces listener définissent des méthodes différentes,


– ce qui permet d’utiliser la méthode concernée par type d'événement qu'elle permet de
gérer

M.AFILAL 201 M.AFILAL 202

Liste des catégories d’événements Autres infos concernant les interfaces Listener ou les gestionnaires d’événements
Le tableau présenté ci-dessous dresse la liste de certaines catégories d’événement qu’un
composant graphique est susceptibles de générer

M.AFILAL 203 M.AFILAL 204


Les listeners  ComponentListener
componentHidden(ComponentEvent) Composant caché
Une hiérarchie de listeners : componentMoved(ComponentEvent) Composant déplacé
componentResized(ComponentEvent) Composant redimensionné
componentShown(ComponentEvent) Composant rendu visible
 FocusListener
focusGained(FocusEvent): gain du focus
focusLost(FocusEvent): perte du focus
KeyListener
keyPressed(KeyEvent): touche enfoncée
keyReleased(KeyEvent): touche relâchée
MouseListener
mouseClicked(MouseEvent): bouton souris a été enfonce et relâché
mouseReleased(MouseEvent): bouton de la souris est relâché
mousePressed(MouseEvent): bouton de la souris est enfoncé
mouseEntered(MouseEvent): la souris a atteint un composant
M.AFILAL 205 mouseExited(MouseEvent): la souris a quittée
M.AFILALun composant 206

Classement par composant émetteur Remarque

Méthodes pour enregistrer un observateur à un composant émetteur: AWT 1.1 fournit pour chaque interface Listener une classe adaptatrice
– Javax.swing.JComponent
addComponentListener(ComponentListener) – qui définit toutes les méthodes, de l’interface Listener, avec un corps vide.
addFocusListener(FocusListener)
addKeyListener(KeyListener) – leurs noms correspondent à ceux des interfaces, à ceci près que ‘Listener’ est remplacé
addMouseListener(MouseListener) par ‘Adapter’.
addMouseMotionListener(MouseMotionListener) Ainsi, il suffit de sous-classer cette classe adaptatrice et de redéfinir simplement la
méthode qui nous intéresse : les autres sont héritées de la classe adaptatrice
– Javax.swing.JDialog – (création de classe qui extends la classe adaptatrice puis de l’utiliser comme
observateur dans le code principale).
addWindowListener(WindowListener)
– Attention :
– Javax.swing.JFrame
Java ne fournit pas d’héritage multiple,
addWindowListener(WindowListener)
il est impossible de dériver une classe de plusieurs classes adaptatrices, il faut alors
implémenter les interfaces correspondantes ou des sous classes internes, externes ou
– Javax.swing.JButton anonymes.
addActionListener(ActionListener)

– Javax.swing.JComboBox
addItemListener(ItemListener)

– Javax.swing.JScrollbar
addAdjustmentListener(AdjustmentListener)
M.AFILAL 207 M.AFILAL 208
Listes des interfaces « Listener »
Remarque

L’une des grandes innovations de Java 1.1 est l’apparition des classes internes :
Le tableau ci-dessus décrit la liste de ces interfaces
– celles-ci sont des classes définies à l’intérieur d’autres classes. – La première colonne indique la catégorie d’événements
– La seconde colonne indique le nom de l’interface
– Une classe interne peut accéder à tous les membres définis dans la classe englobante. – La troisième colonne indique le nom de la classe d’adaptatrice correspondante (si elle
existe)
– On utilisera très souvent de telles classes pour la gestion des évènements : – La quatrième, enfin, donne la liste des méthodes contenues dans l’interface
on définit pour chaque type d’évènement que l’on veut traiter une classe interne, qui
implémente l’interface correspondante ou dérive d’une classe adaptatrice.

On peut aller encore plus loin, et ne même pas nommer la classe destinée à traiter un
évènement, en la définissant directement dans l’appel à son constructeur:
– Se sont les classes anonymes

M.AFILAL 209 M.AFILAL 210

Tableau décrivant la liste des interfaces Listener Exemple

Exemple avec une JFrame


– avec implémentation d’interface Listener
– Avec Extends XXXAdapter
– Avec une classe externe
– Avec une classe interne
– Avec une classe anonyme
– Voir les classes:
TestEvenementPresentation1
TestEvenementPresentation2
TestEvenementPresentation3
TestEvenementPresentation4
TestEvenementPresentation5

M.AFILAL 211 M.AFILAL 212


JDBC: manipuler une base de données en Java JDBC: manipuler une base de données en Java

Les bases de données fournissent un mécanisme de stockage persistant pour les données
Remarque
d’application et dans bien des cas,
– l'API analogue au JDBC en langage C est ODBC.
– elles sont essentielles au fonctionnement des applications.
– Les paquetages java.sql et javax.sql (JDBC 2.0) regroupent la plupart des interfaces et les
classes de l'API JDBC et les autres peuvent venir du paquetage du Driver utilisé
Java propose une API pour l’accès aux bases de données relationnelles
« dont la plupart de ses classes et interfaces dérivent de java.sql et javax.sql ».
– JDBC (Java DataBase Connectivity).
– La plupart des méthodes se trouvant dans les classes du paquetage java.sql ou javax.sql
ou du paquetage du Driver utilisé
JDBC: lèvent l'exception java.sql.SQLException.
– c'est une API (Application Programming Interface) constituée d’un ensemble de classes
et d’interfaces permettant de développer des applications capables de se connecter à des
Les principales étapes pour manipuler une base de données sont:
serveurs de bases de données (SGBD)
– Charger et installer un pilote (un driver) JDBC approprié à la BD.
Donc, cette API permet l'accès à des données de type table de bases de données
SGBD relationnelles : – Se connecter à une base de données.
– ouvrir une connexion avec le SGBD – Envoyer une requête SQL.
– envoyer des requêtes SQL au SGBD – Manipuler (récupérer et parcourir) le résultat.
– récupérer des données retournées par des requêtes SQL
– traiter ces données "table"
– gérer les erreurs retournées par des requêtes au SGBD
M.AFILAL 213 M.AFILAL 214

Étape 1: charger le pilote Étape 1: charger le pilote


Charger le pilote (le driver JDBC)
– Pilote: c’est un fichier .jar qui contient toutes les interfaces et les classes nécessaires
pour :
Ce qui se passe réellement pour le chargement du pilote
communiquer avec une base de données,
manipuler des requêtes SQL. – La méthode Class.forName charge ou inscrit le driver, dans la liste des driver ,donné
– Pour charger le pilote, il faut utiliser la méthode forName de la classe Class comme paramètre
– En commençant par créer une instance de type de la classe du driver donné
– Exemple: comme paramètre.
SQL Server 2000: – Class.forName("com.mysql.cj.jdbc.Driver“)Class.forName("com.mysql
– Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); .cj.jdbc.Driver“).newInstance()::
Pont ODBC-JDBC (le pilote fournit une connectivité à une source de données – Cette instance exécute dynamiquement le bloc static
ODBC) – { DriverManager.registerDriver(InstanceCréerDuDriver)}
– Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
– ce qui permet d’enregistrer le pilote ou le driver dans la liste
ORACLE
des driver déjà inscrits.
– Class.forName("oracle.jdbc.driver.OracleDriver");
MySql – Ce qui permet la possibilité de créer une connexion à la base
– Class.forName("com.mysql.cj.jdbc.Driver“); de données cible en utilisant son URL ou son adresse.
– La méthode forName charge en mémoire la classe demandée et exécute son éventuel
bloc static.
static {DriverManager.registerDriver(new SQLServerDriver()); }

Remarque
– Il existe différents drivers JDBC pour différentes bases de données (les drivers
M.AFILAL 215 M.AFILAL 216
dépendent du SGBD utilisé)
Étape 1: charger le pilote Étape 1: charger le pilote
Ce qui se passe réellement
– La méthode Class.forName charge ou inscrit le driver, dans la liste des driver ,donné comme
paramètre
ceci via la création d’une instance de type de la classe donné comme paramètre.
– Exemple: Class.forName("com.mysql.cj.jdbc.Driver“);
Retourne la classe com.mysql.cj.jdbc.Driver.class
Puis, cette classe créer une instance:
(com.mysql.cj.jdbc.Driver.class).newInstance()
 Class.forName("com.mysql.cj.jdbc.Driver“).newInstance()
– Cette instance exécute dynamiquement la méthode DriverManager.registerDriver(pilote), ceci
permet
d’enregistrer le driver ou le pilote donné comme paramètre de la méthode registerDriver
et de l’ajouter dans la liste des driver
– Exemple: DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver( ) );

Ce qui fait que maintenant la possibilité de créer une connexion


– à une base de données correspondant au type du driver utilisé celui qu’on vient
d’enregistrer Remarque
Ceci en utilisant un URL ou chemin de la base de données – Le driver manager permet de charger et configurer les pilotes JDBC nécessaires à
Donc: la méthode forName permet d’inscrire ou de charger le pilote l'application
– Attention: le pilote contient toutes les classes nécessaire pour communiquer avec une
base de données. Ses classes implémentent
M.AFILAL les interfaces Driver, celles de java.sql et de217 M.AFILAL 218
javax.sql

Étape 2: établir une connexion Étape 2: établir une connexion

On ouvre une connexion avec une des méthodes La syntaxe d'une URL JDBC est:
– DriverManager.getConnection( URLjdbc ); – jdbc:<sous-protocole>:<baseID>
– DriverManager.getConnection( URLjdbc, "dbUser1", "pwuser1" ); « la plus générale »
Exemples d’URL
Qui retourne un objet d'une classe qui implémente l'interface Connection.
– jdbc:odbc:DsnName
DSN (Data Source Name)
Ces méthodes contiennent: – jdbc:odbc:test1
– Le premier argument est une "URL JDBC". – jdbc:odbc:@orsay:1751:test1
– Puis, un nom d’utilisateur et un mot de passe (pour la plus part des pilotes) pour valider la – jdbc:oracle:thin:@orsay:1751:test1
connexion à la base de données . – jdbc:mysql://db.myhost.com:3306/mydatabase

Ces méthodes recherchent le pilote adapté pour gérer la connexion avec la base repérée par Remarque
cette URL.
– La classe DriverManager:
– Une URL JDBC
Cette classe est une classe qui ne contient que des méthodes statiques.
Doit commencer par le protocole qui est toujours égal au préfix jdbc.
Elle fournit des méthodes qui sont des utilitaires pour gérer l'accès aux bases de
Le second argument est le sous protocole: il donne le driver utilisé. données par Java et les différents drivers JDBC à l'intérieur d'un programme Java.
– Il définit le mécanisme de connexion pour un type de base de données – baseID peut contenir
Le troisième argument est un identificateur de base de données. le nom de la machine hôte ou son adresse IP,
le port du sous protocole utilisé ou le port par défaut ou port de connexion
M.AFILAL 219 M.AFILAL 220
le nom de la base de données à laquelle on doit se connecter
Étape 2: établir une connexion Étape 2: établir une connexion

Pour établir la connexion avec SQL Server, il faut préciser Pour établir la connexion avec MySQL, il faut préciser
– le nom de la machine (ou son numéro IP), – le nom de la machine (ou son numéro IP),
– le port où le service SQL est démarré (quasiment toujours 1433), – le port où le service SQL est démarré (quasiment toujours 3306),
– le nom de la base de données, – le nom de la base de données,
– le login utilisé ainsi que son mot de passe. – le login utilisé ainsi que son mot de passe.

La syntaxe utilisée pour spécifier la base de données à laquelle nous souhaitons nous La syntaxe utilisée est:
connecter prend la forme d’une URL – Connection conn = null;
try {
try { – Class.forName("com.mysql.cj.jdbc.Driver");
– String strClassName = "com.microsoft.jdbc.sqlserver.SQLServerDriver"; – conn =
– String strUrl = "jdbc:microsoft:sqlserver://hostname:1433; databaseName = DriverManager.getConnection("jdbc:mysql://db.myhost.com:3306/mydatabase");
databaseName; user = sa; password = passWord";
Charger le pilote – /* conn =
– Class.forName(strClassName); DriverManager.getConnection("jdbc:mysql://localhost:3306/database", "user",
– Connection conn = DriverManager.getConnection(strUrl); "password"); */ // avec trois paramètres
– // un seul parametre dans getConnection. . . . .
Ouvrir une connexion – // . . .
– conn.close(); – conn.close();
Autres opérations } catch(ClassNotFoundException e) {
} catch(ClassNotFoundException e) { – System.err.println("Driver non chargé !");
– System.err.println("Driver non chargé !"); – e.printStackTrace();
– e.printStackTrace(); } catch(SQLException e) {
} catch(SQLException e) { // . . . } M.AFILAL 221
– // . . . M.AFILAL 222
}

Étape 2: établir une connexion Étape 3: Requête SQL

Remarque Il existe différentes sortes de requêtes SQL dont :


– Le format général d’une URL de base de données MySQL est le suivant : – les requêtes SELECT
jdbc:mysql://hôte:port/baseDeDonnées qui opèrent sur une ou plusieurs tables et placent le résultat dans une "table"
résultante.
– jdbc:mysql://db.myhost.com:3306/mydatabase – les requêtes d'action
Cette URL spécifie une base de données MySQL sur l’hôte db.myhost.com pour une qui modifient une ou des colonnes ou lignes de table.
connexion sur le port 3306 avec le nom de base de données mydatabase. – Exemple de requêtes d'action: UPDATE, INSERT, DELETE, APPEND.
– des SQL DDL (Data Definition Language) comme CREATE TABLE, DROP TABLE, ...
– Souvent, on ajoute au nom de la base de données:
l’identifiant de l’utilisateur et un mot de passe afin de se connecter. SQL est un langage non sensible à la casse.
– En général les mots clés du langage SQL sont mis en majuscules.

Syntaxiquement en Java, on utilise


– executeQuery(...) si la requête est une requête SELECT,
– executeUpdate(...) si la requête est une requête d'action ou une SQL DDL.

M.AFILAL 223 M.AFILAL 224


Étape 3: Requête SQL Étape 3: Requête SQL avec statement
Création d’un objet de type Statement (Statement est une interface du Package java.sql)
– On obtient un objet Statement avec la méthode: createStatement.
L’exécution d’une requête SQL passe par l'utilisation d'une classe, spécifique au pilote utilisé,
– Statement statement = connection.createStatement();
implémentant l’une des interfaces suivantes:
cet objet permet l’exécution de requêtes SQL,
– Statement
un seul objet ResultSet (un résultat de requête) lui est associé.
– PreparedStatement
– Un objet de type Statement se doit d'être adapté à la base manipulée.
– CallableStatement
Remarque
C.à.d: les objets des classe implémentant ses interfaces permettent d’exécuter des
requêtes SQL – L’instruction
Les instructions (les requêtes) PreparedStatement «requête préparée » sont précompilée Statement stmt =connection.createStatement( );
– Plus, elles donnent la possibilité de créer des requêtes paramétrées. – retourne un objet concret de classe (par exemple: stmt est de type OracleStatement
« dans le cas Oracle », SUN_JDBC_ODBC_Statement « dans le cas de
SUN_JDBC_ODBC » et com.mysql.cj.jdbc.StatementImpl « dans le cas Mysql »)
On utilise CallableStatement pour lancer une procédure du SGBD. Toutes ses classes implémentes Statement, ce qui permet d’adapter le statement au base
manipulée.
Résultat d’exécution de requêtes sur l’objet Statement
– int executeUpdate("requête modification (insert/update/delete)");
– ResultSet executeQuery("requête de consultation (select)");
Remarque
– executeUpdate return un int  donne le nombre de ligne affecté par la mise à jour.
M.AFILAL 225 – Dans le cas d’une instruction DDl M.AFILAL
le retour est = à 0 car on fait une mise à jour de 0 226
ligne.

Requête SQL avec statement: Exemple Requête SQL avec statement: exemple avec SELECT

try { Statement smt = conX.createStatement( );


– String strClassName = "com.mysql.cj.jdbc.Driver"; ResultSet rs = smt.executeQuery( "SELECT nom, prenom, adresse FROM Personnes" );
– String strUrl = "jdbc:mysql://localhost/database"; Les résultats des requêtes SELECT ont été mis dans un ResultSet rs.
– String strInsert = "INSERT INTO T_Users " + "(Login, Password, ConnectionNumber) " L’ objet rs récupéré, modélise le résultat qui peut être vu comme une table.
+ "VALUES ('Toto', 'Titi', 0);";
– Class.forName(strClassName);
– conn = DriverManager.getConnection(strUrl, "login", "password");
– Statement stAddUser = conn.createStatement();
– stAddUser.executeUpdate(strInsert);
– // . . .
Créer un Statement
} catch(ClassNotFoundException e) {
Exécuter un ordre SQL
– // . . .
} catch(SQLException e) { Un pointeur géré par Java jdbc qui permet de parcourir tout le ResultSet est initialisé
– // . . . il est automatiquement positionné avant la première ligne de résultat.
} finaly{ On parcourt alors tout le ResultSet pour avoir l'ensemble des réponses à la requête.
– if(conn != null) conn.close(); La boucle de parcours est :
} while( rs.next() ) { // traitement …. }
La méthode next(): renvoie VRAI s’il reste des lignes à lire et FAUX sinon
M.AFILAL 227 M.AFILAL 228
Requête SQL avec statement: exemple avec SELECT Requête SQL avec statement: exemple avec SELECT
Les colonnes demandées ( colonne: nom, prenom, age, date) par la requête sont On peut aussi désigner les colonnes par leur nom dans la BD (c'est moins rapide mais plus
– numérotées à partir de 1 lisible) et réécrire :
– typées en type SQL. – ResultSet rs = stmt.executeQuery("SELECT nom, prenom, age, date FROM LaTable");
Remarque – String leNom = rs.getString( "nom") );
– La numérotation est relative à l'ordre des champs de la requête et non pas l'ordre des – String lePrenom = rs.getString( "prenom" );
champs de la table interrogée. – int lage = rs.getInt ( "age" );
– Date laDate = rs.getDate( "date ");
Pour récupérer une valeur de colonne, il faut indiquer le numéro de la colonne ou son nom et
faire la conversion de type approprié.
L’accès aux valeurs des colonnes via L’interface java.sql.ResultSet est donné par les
méthodes:
Exemple 1 (avec numéro des colonnes): – Type getType( int numeroDeColonne );
– Statement stmt = ...; – Type getType( String nomDeColonne );
– ResultSet rs = stmt.executeQuery("SELECT nom, prenom, age, date FROM LaTable"); – Les fonctionnalités avancées d’un ResultSet pour les positionnements sur les tuples
– String leNom = rs.getString( 1 ) ); first( ), last(), next( ), absolute(int row), previous( ), relative(int rows),
– String lePrenom = rs.getString( 2 ); moveToCurrentRow;
– int lage = rs.getInt ( 3 );
– Date laDate = rs.getDate( 4); Le Type peut être:

On utilisera dans ce cas les méthodes getXXX( ) de la classe ResultSet où XXX est
M.AFILAL 229 M.AFILAL 230
un type java,
Les getXXX( ) permettent de convertir les types sql en type Java

Correspondance type SQL - type Java Exemple complet


Les correspondances entre type SQL et type Java sont données par les spécifications JDBC.
Création de la table personnes
En voici certaines :
– DROP TABLE personnes;
– CREATE TABLE personnes (
id_personne INTEGER NOT NULL,
nom VARCHAR(100) NOT NULL,
prenom VARCHAR(100),
annee INTEGER,
PRIMARY KEY (id_personne)
– );

public class Personne {


– private int id, annee;
– private String nom, prenom;
– public Personne(int id, String nom, String prenom, int annee) {
this .id = id;
this .nom = nom;
this .prenom = prenom;
this .annee = annee;
M.AFILAL 231 – } M.AFILAL 232
Exemple complet Exemple complet
– public Personne(int id, Connection connection) throws SQLException { – public void writeObjectInBD(Connection connection) throws SQLException {
this .id = id; Statement transaction = connection.createStatement( );
boolean res = readObjectFromBD(connection); if (readObjectFromBD(connection)) // si pers existe en update ses infos
if (!res) this.id = 0; – transaction.executeUpdate("UPDATE personnes SET nom=' "+nom+" ',
prenom=' "+prenom+" ', annee="+annee+" WHERE id_personne="+id);
– }
else
– transaction.executeUpdate("INSERT INTO personnes (id_personne, nom,
– // Si id existe comme clé dans la table personne alors cette méthode permet d’initialiser
prenom, annee) VALUES ("+id+", ' "+nom+" ', ' "+prenom+" ',"+annee+")");
les autres champs avec les infos de la ligne dont la clé est id
– }

public boolean readObjectFromBD(Connection connection) throws SQLException {


– public int getId( ) {
– Statement transaction = connection.createStatement();
return id;
– ResultSet resultat = transaction.executeQuery("SELECT * FROM personnes WHERE
id_personne= " + id); – }
– if (resultat.next( )) {// initialisation des autres champs via les données de la base – public String toString( ) {
nom = resultat.getString("nom"); return getClass().getName()+"("+id+") : "+prenom+" "+nom+" ("+annee+")";
prenom = resultat.getString("prenom"); – }
annee = resultat.getInt("annee"); } // Fin de la classe Personne
return true; Remarque
– } – Pour ne valider une transaction que si en commit explicitement, on fait:
M.AFILAL 233 M.AFILAL 234
– return false ; connection.setAutoCommit(false);
} – Par défaut, on a : connection.setAutoCommit(true);

Exemple complet Requête SQL avec PreparedStatement

public class TestJDBC { Lors de l'envoi d'une requête pour exécution par le SGBD, 4 étapes doivent être faites
– public static void main(String[] args) throws SQLException { – analyse de la requête (recherche d’une stratégie d’exécution adéquate, type requête ),
try { – compilation de la requête,
– Class.forName("com.mysql.cj.jdbc.Driver"); – optimisation de la requête,
– Connection connection = – exécution de la requête.
DriverManager.getConnection("jdbc:mysql://localhost:3306/afilaldb", "root",
"mouni"); Ceci est le cas, même si cette requête est la même que la précédente !!
– Personne p1 = new Personne(1,"Alaoui", "Ahmed", 1983); – Or les 3 premières étapes ont déjà été effectuées dans ce cas, d’où perte de performances.
– p1.writeObjectInBD(connection);
– Personne p2 = new Personne(2,"Acharki", "Rachida", 1960); Les bases de données définissent la notion de requête préparée,
– p2.writeObjectInBD(connection); – requête où les 3 premières étapes ne sont effectuées qu'une seule fois.
– Personne p3 = new Personne(1, connection); // construc p3 via infos p1
– System.out.println(p3); JDBC propose l'interface PreparedStatement pour modéliser cette notion de requête préparée
– connection.close(); – Cette interface dérive de l'interface Statement.
} catch (ClassNotFoundException e) {
– System.err.println("Le driver n’est pas accessible"); D’autre part, avec un Statement, on ne peut pas construire des requêtes où un des arguments
– e.printStackTrace(System.err); est une variable (i.e. requêtes paramétrées).
} Pour remédier à cela, il faut pour cela utiliser un PreparedStatement.
– } M.AFILAL 235 M.AFILAL 236
}
Requête SQL avec PreparedStatement Requête SQL avec PreparedStatement: requêtes paramétrées

Un PreparedStatement ne s'utilise pas comme un Statement. Remarque


– Au lieu d'écrire : – La fonctionnalité principale d'un objet PreparedStatement, contrairement à l'objet
Statement smt = conX.createStatement( ); Statement,
ResultSet rs = smt.executeQuery( "SELECT * FROM Livres" ); est de lui fournir une instruction SQL dès sa création.
– On écrit : – Ainsi l'instruction SQL sera directement envoyée au SGBD, où elle y sera
PreparedStatement pSmt = conX.prepareStatement("SELECT * FROM Livres" ); compilée.
ResultSet rs = pSmt.executeQuery( );
– Le résultat obtenu est que l'objet PreparedStatement
Remarque ne contient plus seulement une instruction SQL, mais bien une instruction
– On a la requête associée à un objet de type PreparedStatement est décrite au moment de SQL précompilée.
la "construction"
mais pas lors de l'exécution qui est lancée par executeQuery( ) ou executeUpdate() – Cela signifie que quand le PreparedStatement est exécuté,
sans argument. le SGBD a juste à lancer l'instruction SQL du PreparedStatement sans
– Si l’une des instructions SQL doit être exécutée de nombreuses fois, avoir à la compiler avant.
il est plus efficace d’utiliser une requête PreparedStatement. Ce qui permet d’augmenter l’efficacité et les performances de
l’application:
on fait les étapes coûteuses une seule fois (analyse, compilation,
optimisation) pour une requête et on l’exécute autant de fois que
l’on veut.
M.AFILAL 237 M.AFILAL 238

Requête SQL avec PreparedStatement: requêtes paramétrées Requête SQL avec PreparedStatement: requêtes paramétrées
Avec un PreparedStatement:
– On est plus rapide qu’un statement lors d’envoi de requêtes . Exemple avec une boucle (mise à jour de la colonne Vents pour tous les cafés de la table
– On peut utiliser des requêtes paramétrées. CAFE : c’est une requête qui se répète dans la boucle):
– On envoi une requête sans paramètre à la base de données pour compilation
Et puis spécification, le moment voulu, de la valeur des paramètres afin d’éxécuter la – PreparedStatement updateVentes;
requête.
– String updateString = "update CAFE SET Ventes = ? WHERE Nom_Cafe LIKE ?";
On utilise un PreparedStatement dans le cas de requêtes paramétrées et on les écrits sous la forme
suivante:
– updateVentes = conn.prepareStatement(updateString);
– SELECT nom FROM Personnes WHERE age > ? AND adresse = ?
– int [ ] VentesDeLaSemaine = {175 , 150, 60, 155};
Puis, si on veut exécuter la requête, on appel avant cela des méthodes qui permettent de donner des – String [ ] cafes = {"Colombian","FrenchRoast","Espresso","ColombianDecaf"};
valeurs à la place des points d'interrogation, données par: – int len = cafes.length;
– setType(numéroDeLArgument, valeur);
Pour positionner chacun des arguments et leur donner une valeur – for(int i = 0 ; i < len ; i ++){
– Les numéros commencent à 1 dans l'ordre d'apparition dans la requête. updateVentes.setInt(1, VentesDeLaSemaine[i]);
updateVentes.setString(2, cafes[i]);
On a donc un code comme:
updateVentes.executeUpdate();// exécution à chaque boucle
– PreparedStatement pSmt = conX.prepareStatement("SELECT nom FROM Personnes WHERE
age > ? AND adresse = ?" );
– }
– pSmt .setInt(1, 22); // pour le premier paramètre
– pSmt .setString(2, "Turin"); // pour le deuxième paramètre
M.AFILAL 239 M.AFILAL 240

– ResultSet rs = pSmt.executeQuery( ); // exécution de la requête


Requête SQL avec PreparedStatement: requêtes paramétrées Requête SQL: Les mises à jour de masse (Batch Updates)

Un objet PreparedStatement peut être aussi utilisé dans une instruction SQL sans paramètre, Un batch (JDBC 2.0) permet de réaliser des mises à jour de masse (insert, update, delete, ..) en
– mais peut être aussi utiliser pour des instructions SQL avec paramètre. regroupant plusieurs traitements (requêtes) pour les envoyer en une seule fois au SGBD.
– Ceci permet d'améliorer les performances surtout si le nombre de traitements (requêtes)
L'avantage de ceci est qu’on peut utiliser une instruction SQL requérant des paramètres: est important.
– On peut alors utiliser la même instruction en fournissant différentes valeurs à chaque fois
qu’on l'exécute. Attention: Cette fonctionnalité n'est pas obligatoirement supportée par le pilote.
– La méthode supportsBatchUpdates( ) de la classe DatabaseMetaData permet de savoir si
IL faut donner des valeurs « à la place des points d'interrogation » Avant qu’on puisse exécuter elle est utilisable avec le pilote.
un objet PreparedStatement
Plusieurs méthodes ont été ajoutées à l'interface Statement pour pouvoir utiliser les mises à
– Pour faire ça, il faudra utiliser l'une des méthodes setXXX définies dans la classe jour de masse :
PreparedStatement.
Si la valeur qu’on veut substituer au point d'interrogation est un int Java, on doit Méthode Rôle
appeler la méthode setInt. permet d'ajouter une chaîne
void addBatch(String)
contenant une requête SQL
Si la valeur est un String Java, on doit appeler la méthode setString, et ainsi de suite.
En général, il y a un setXXX pour chaque type Java. permet d'exécuter toutes les
requêtes. Elle renvoie un tableau
int[] executeBatch() d'entiers qui contient pour chaque
requête, le nombre de mises à jour
Voir exemple main_3 effectuées.
M.AFILAL 241 M.AFILAL 242
supprime toutes les requêtes
void clearBatch()
stockées

Requête SQL: Les mises à jour de masse (Batch Updates) Requête SQL: Les mises à jour de masse (Batch Updates)

Remarque
L'exécution de commandes SQL en batch n'est possible que pour des requêtes de modification
de la base « donc pas de select », celles que l'on exécute avec la méthode – Lors de l'utilisation de batchUpdate, il est préférable de positionner l'attribut autocommit
executeUpdate(String). à false afin de faciliter
la gestion des transactions,
Toute requête SQL qui peut être exécutée de la sorte, peut également être passée en paramètre le traitement d'une erreur dans l'exécution d'un ou plusieurs traitements.
de la méthode addBatch(String).
– Une exception particulière peut être levée en plus de l'exception SQLException lors de
L'appel de cette méthode ne fait que stocker la requête SQL, sans l'exécuter. C'est l'appel à la l'exécution d'une mise à jour de masse.
méthode executeBatch( ) qui déclenche l'exécution. L'exception SQLException est levée si une requête SQL d'interrogation doit être
exécutée (requête de type SELECT).
L'exception BatchUpdateException est levée si une des requêtes de mise à jour
échoue.

– L'exception BatchUpdateException possède une méthode getUpdateCounts() qui renvoie


un tableau d'entiers contenant le nombre d'occurrences impactées par chaque requête
réussie.

M.AFILAL 243 M.AFILAL 244


Requête SQL: Batch de plusieurs ordres SQL Requête SQL: les MetaData
Exemple (Statement)
– con.setAutoCommit(false);
On peut avoir sur une BD, des informations sur la BD elle même.
– Statement stmt = con.creatStatement();
– Ces informations sont appelées des métadatas.
– stmt.addBatch(" INSERT INTO Employe VALUES(100, 'Jacque', 400) ");
– stmt.addBatch(" INSERT INTO Employe VALUES(101, 'Paul', 700) ");
La méthode getMetaData( ) « de ResultSet » permet d’obtenir des informations sur les types
– stmt.addBatch(" INSERT INTO Employe VALUES(102, 'Marie', NULL) "); de données du resultSet
– int [ ] updateCounts = stmt.executeBatch();
– con.commit(); – Elle renvoie des ResultSetMataData
– con.setAutoCommit(true);
Exemple (PreparedStatement) – On peut connaitre entre autres:
– con.setAutoCommit(false); Le nombre de colonne: getColumnCount()
– PreparedStatement pstmt = con.preparedStatement("INSERT INTO Employe Le nom d’une colonne: getColomnName(int col)
VALUES(?, ?, ?) ");
Le nom de la table: getTableName( )
– pstmt.setInt(1, 103); pstmt.setString(2, "Pierre"); pstmt.setFloat(3, 3000f);
Si un NULL SQL peut être stocké dans une colonne: isNullable( )
– pstmt.addBatch( ); // pas de paramètre dans addBatch pour PreparedStatement
– pstmt.setInt(1, 104); pstmt.setString(2, "Madelaine"); pstmt.setNull(3,
java.sql.Types.FLOAT);
– pstmt.addBatch( );
– int[ ] updateCount = pstmt.executeBatch( );
– con.commit( ); M.AFILAL 245 M.AFILAL 246

– con.setAutoCommit(true);

Requête SQL: les MetaData Contenu de l’interface ResultSetMetaData

Les métadatas depuis un ResultSet :


– Exemple :Affichage des colonnes de la table
ResultSet rs = smt.executeQuery( "SELECT * FROM Livres " );
ResultSetMetaData rsmd = rs.getMetaData( ); getCatalogName() getColumnType()
int numCols = rsmd.getColumnCount( ); getColumnClassName() getPrecision()
for ( int i = 1; i <= numCols; i++ ){ getColumnCount() getScale()
– System.out.println( "\t" + i + " : " + rsmd.getColumnName( i ) );
getColumnDisplaySize() getSchemaName()
getColumnLabel() getTableName()
}
getColumnName() getColumnTypeName()
Aussi, on obtient un objet de la classe DatabaseMetaData à partir de la connexion par : isAutoIncrement() isReadOnly()
– conX = DriverManager.getConnection(dbURL, "login", "passWord"); isCaseSensitive() isSeisSigned()
– DatabaseMetaData dmd = conX.getMetaData();
isCurrency() isWritable()
isDefinitelyWritable() archable()
isNullable()
La classe DatabaseMetaData fournit beaucoup de renseignements sur la base par exemple :
– String nomSGBD = dmd.getDatabaseProductName();
– ResultSet rs = dmd.getTables(); //donne les tables de la base
– String user = dmd.getUserName(); //donne le nom de l’utilisateur
M.AFILAL 247 M.AFILAL 248
Requête SQL: les valeurs nulles Requête SQL: les transactions
Pour repérer les valeurs NULL de la base
Une transaction est un ensemble d’instructions effectuées ensembles pour assurer la cohérence
– On utilise la méthode wasNull( ) de ResultSet des données de la base.
renvoi true si l’on vient de lire un NULL, false sinon – Une transaction permet de ne valider un ensemble de traitements (requêtes) sur la base de
– Les méthodes getXXX( ) de ResultSet convertissent une valeur NULL SQL en une valeur données que s'ils se sont tous effectués correctement
acceptable par le type d’objet demandé:
Les méthodes retournant un objet (getString, getDate(), …) retournent un « null » Validation de transaction – commit
java
– Utiliser pour valider tout un groupe de transaction à la fois
Les méthodes numériques (getByte(), getInt(), etc) retourne « 0 »
– Par défaut: mode auto-commit
getBoolean( ) retourne « false »
Un commit est effectué automatiquement après chaque ordre sql
– Pour repasser en mode manuel:
Exemple
Connexion.setAutoCommit(false);
– ResultSet resultat = stmt.executeQuery("SELECT numemp, name, salary FROM
– L’application doit alors envoyer à la base un commit pour rendre permanent tous les
employe");
changements occasionnés par la transaction
– while(rs.next( )) {
Connexion.commit( );
String s = rs.getString(2);
float fL = rs.getFloat("salary");
Annulation de transaction- Rollback
if (rs.wasNull( )) System.out.println(s + " n’a pas de salaire ");
– De même, pour annuler une transaction (ensemble de requêtes SQL), l’application peut
else System.out.println(s + " gagne "+ fL+ " DH "); envoyer à la base un rollback par:
– } Connexion.rollback( );
– rs.close M.AFILAL 249 M.AFILAL
– Ce qui permet une restauration de l’état de la base après le dernier commit
250

Requête SQL: les transactions ??????

Méthode d’utilisation
– connection.setAutoCommit(false);
– try {
// instructions SQL de la transaction
connection.commit(); // validation de la transaction
– }
– catch (SQLException e) {
connection.rollback(); // annulation de la transaction
– }
– connection.setAutoCommit(true);

M.AFILAL 251 M.AFILAL 252


Requête SQL avec CallableStatement Requête SQL avec CallableStatement
Une instance de CallableStatement s'obtient grâce aux méthodes prepareCall de Connection.
Certaines requêtes pour une base de données sont si courantes qu'elles sont intégrées dans la – CallableStatement statement = connection.prepareCall( InstructionSQL, typeParcours,
modeDeMiseA_Jour);
base de données.
– Le premier argument est une chaîne de caractères définissant l'instruction SQL.
On les appelle des procédures stockées,
– Les autres arguments de la méthode prepareCall servent à déterminer les types de ResultSet
– elles sont modélisées en JDBC par l'interface CallableStatement obtenus à partir de la procédure.
CallableStatement dérive de PreparedStatement.
– Elles peut donc avoir des paramètres.
L’appel pour les procédures stockées :
– String InstructionSQL= "{call nomDeLaProcedure[(?, ?, ...)]}";
l'interface CallableStatement permet d'appeler des procédures et des fonctions stockées de //[(?, ?, ...)] sont les arguments de la procédure, qui sont de type IN, OUT ou INOUT
manière standard pour tous les SGBD.
– String InstructionSQL= "{call nomDeLaProcedure}";
On indique le nom de la procédure ou de la fonction requise lors de l'initialisation de l'objet – CallableStatement statement = connection.prepareCall(InstructionSQL);
CallableStatement grâce à la méthode prepareCall() de l'interface Connection
L’appel pour les fonctions stockées (procédures stockées renvoyant un résultat) :
Utilisation : (procédure avec paramètres mais pas de retour) – String InstructionSQL = "{? = call nomDeLaFonction[(?, ?, ...)]}";
– CallableStatement cStmt = conX.prepareCall("{call sp_setDomicile(?, ?) }"); //Le premier? est le résultat de la procédure et [(?, ?, ...)] sont les arguments de la fonction
– cStmt.setString(1, "Djorkaeff"); – CallableStatement statement = connection.prepareCall(InstructionSQL);
– cStmt.setString(2, "KaisersLautern");
– cSmt.executeUpdate(); Pour le cas avec plus d’un seul paramètres dans la fonction prepareCall
– String InstructionSQL = "{? = call max(?, ?)}";
M.AFILAL 253 – CallableStatement statement = connection.prepareCall(InstructionSQL,
M.AFILAL 254
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);

Requête SQL avec CallableStatement Requête SQL avec CallableStatement


Remarque
les paramètres d’une procédure se décomposent en trois parties:
– Les méthodes setXXX( ) permettent de fournir les valeurs de chaque paramètre défini
– Sens_du_parametre, Nom_parametre, Type_parametre dans la requêteInstruction associé à un CallableStatement.
Le premier paramètre de ces méthodes précise le numéro du paramètre dont la
Le sens du paramètre peut prendre 3 valeurs : méthode va fournir la valeur. Le second paramètre précise cette valeur.
– IN : le paramètre sera une valeur ou une variable d'entrée. Le paramètre indiqué peut être – En plus de l'index ou numéro, on peut cibler un paramètre grâce à son nom.
une valeur ou une variable que l’envoi lors de l'appel, et qui sera utilisée à l'intérieur de la XXX représente un type primitif ou certains objets tels que String, Date, Object, ...
procédure.
– OUT : le paramètre sera une variable de sortie. Le paramètre indiqué sera une variable (de
session ou de procédure) qui prendra une valeur lors de la procédure. – Les méthodes getXXX( )
– INOUT : le paramètre sera une variable d'entrée-sortie. Cette variable pourra être utilisé permettent d'obtenir la valeur du paramètre de retour en fournissant la valeur 1
ou non dans la procédure, et verra normalement sa valeur modifiée lors de la procédure. comme index de départ
et un autre index pour les paramètres définis en sortie ou entrée/sortie dans la
procédure stockée.
Le type de paramètre tout type SQL ( voir PLSQL) valide.

– Pour exécuter la requête, l'interface PreparedStatement propose deux méthodes :


Exemple de procédure
executeQuery( ) :
– CREATE PROCEDURE carre(IN valeur INT, OUT toto BIGINT)
– cette méthode permet d'exécuter une requête de type interrogation et renvoie un
BEGIN objet de type ResultSet qui contient les données issues de l'exécution de la
– SELECT valeur*valeur INTO toto; requête
END executeUpdate( ) :
255
M.AFILAL
une requête de type mise à jour et renvoie un256
M.AFILAL
– cette méthode permet d'exécuter
entier qui contient le nombre d'occurrences impactées par la mise à jour
Requête SQL avec CallableStatement: Enregistrer une procédure
Requête SQL avec CallableStatement
Le code qui suit met une 'instruction SQL dans une chaîne de caractères et l'assigne à la
Remarque variable createProcedure:
– Quand on emploi des paramètres OUT ou INOUT, on doit utiliser une méthode – String createProcedure = "create procedure SHOW_FOURNISSEURS () " +
supplémentaire (que les setXXX) de CallableStatement: "select FOURNISSEURS.NOM_FO, CAFE.NOM_CAFE " +
registerOutParameter ( ). "from FOURNISSEURS, CAFE " +
"where FOURNISSEURS.FO_ID = CAFE.FO_ID " +
– La méthode de registerOutParameter ( ) lie le type de données de JDBC au type de
"order by NOM_FO";
données que la procédure stockée doit retourner.
– Si on recherche la valeur du paramètre de SORTIE (OUT ou INOUT), on utilisera dans
ce cas la méthode getXXX ( ) appropriée. Puis, on crée un objet Statement, pour l'instruction SQL afin de créer la procédure stockée sur
la base de données :
Cette méthode caste la valeur recherchée du type de SQL à un type de données de
Java. – Statement stmt = conn.createStatement();
– stmt.executeUpdate(createProcedure);
Exemple
– String sql = "{getUnNombre(?)}"; La procédure SHOW_FOURNISSEURS sera compilée et stockée dans la base de données
comme un objet pouvant être appelé de façon similaire à l'appel d'une méthode d’une classe
– CallableStatement statement = connection.prepareCall(sql);
sauf elle est stockée dans le SGBD.
– statement.registerOutParameter(1,Types.INTEGER);
Remarque (avantages de CallableStatement)
– statement.execute( );
– efficacité (moins de transfert de données),
– int resultat = statement.getInt(1);
– compilation des procédures
– if(statement.wasNull()){ System.out.pritnln("Le résultat est de type SQL NULL"); }
M.AFILAL 257 M.AFILAL 258
– else{ System.out.println("Le résultat vaut "+resultat); }

Requête SQL avec CallableStatement: Exécuter une procédure Requête SQL avec CallableStatement: Exemple
JDBC permet d'appeler une procédure stockée sur la base de données depuis une application Exemple avec le sens de paramètre est IN
écrite en Java. Connection connection = DriverManager.getConnection(
– CallableStatement cs = conn.prepareCall("{call SHOW_FOURNISSEURS}"); "jdbc:mysql://localhost:3306/afilaldb", "root", "mounir");
ResultSet rs = cs.executeQuery(); Statement statement = connection.createStatement( );
– CallableStatement callableStatement = connection.prepareCall("{call statement.executeUpdate("DROP PROCEDURE IF EXISTS rechercherParPrenom");
calculateStatistics(?, ?)}", ResultSet.TYPE_FORWARD_ONLY, statement.executeUpdate( //***Création de la procédure***//
ResultSet.CONCUR_READ_ONLY ); – "CREATE PROCEDURE rechercherParPrenom(IN lePrenom VARCHAR(50))\n"
– + "BEGIN\n"
Remarque: – + " SELECT * FROM Personnes WHERE prenom = lePrenom;\n"
– Quand le driver rencontre "{call SHOW_FOURNISSEURS}", – + "END\n");
il traduira cette syntaxe en SQL natif utilisé par la base de données pour appeler la String sql = "{call rechercherParPrenom(?)}";
procédure stockée nommée SHOW_FOURNISSEURS CallableStatement calls = connection.prepareCall(sql); //Appel de la procédure
– La méthode pour exécuter cs est executeQuery calls.setString(1, "Nada"); //passage de la chaîne "Nada" comme valeur du premier paramètre
car cs appel une procédure stockée qui contient une requête et produit un resultset. if (calls.execute()) { // teste s’il y a des résultats ou pas
– Si la procédure avait contenue une mise à jour ou une des instructions DDL, – ResultSet resultat = calls.getResultSet( ); //Récupération du ResultSet
la méthode executeUpdate aurait été utilisée. – int nbColones = resultat.getMetaData().getColumnCount( );
– Parfois, une procédure stockée contient plus d'une instruction SQL, qui pourrait produire – while (resultat.next( )) {
plus d'un résultset, plus d'une mise à jour, ou une combinaison de resultSet et de mise à
jour. for (int i = 0; i < nbColones; i++) { System.out.print(resultat.getObject(i +1) +", "); }
Dans ce cas, lorsqu'il y a de multiples résultats, la méthode execute devra être 259
} 260
M.AFILAL M.AFILAL
utilisée pour exécuter CallableStatement. resultat.close( ); calls.close( ); connection.close( );
Requête SQL avec CallableStatement: Exemple Requête SQL avec CallableStatement: Exemple
Exemple avec le sens de paramètre est OUT Exemple avec le sens de paramètre est INOUT
Connection connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/afilaldb", Statement statement = connection.createStatement();
"root", "mounir"); statement.executeUpdate("DROP PROCEDURE IF EXISTS hello");
Statement statement = connection.createStatement();
statement.executeUpdate("DROP PROCEDURE IF EXISTS nombrePersonneEnregistres");
String procedureReq = "CREATE PROCEDURE hello(INOUT param VARCHAR(100))\n"
statement.executeUpdate(
– + "BEGIN\n"
– "CREATE PROCEDURE nombrePersonneEnregistres(OUT nbPersonne INTEGER)\n"
– + " SELECT CONCAT('Hello ', param, ' est ce que tu vas bien? ') INTO param\n;"
– + "BEGIN\n"
– + " SELECT COUNT(*) INTO nbPersonne FROM Personnes;\n" – + "END\n";
– + "END\n");
String sql = "{call nombrePersonneEnregistres(?)}"; statement.executeUpdate(procedureReq);
CallableStatement call = connection.prepareCall(sql); String sql = "{call hello(?)}";
– //enregistrement du paramètre de sortie en fonction de son type et de son nom CallableStatement call = connection.prepareCall(sql);
call.registerOutParameter("nbPersonne", java.sql.Types.INTEGER);
– /*//enregistrement du paramètre de sortie en fonction de son index et de son type call.setString(1, "AFILAL"); //passage de la valeur du paramètre
– statement.registerOutParameter(1, java.sql.Types.INTEGER);*/ – //enregistrement du paramètre en tant que paramètre OUT
call.execute( ); call.registerOutParameter(1, Types.VARCHAR);
int resultat = call.getInt(1); //récupération du résultat en fonction de l'index
– /*int resultat = statement.getInt("nb"); //récupération du résultat en fonction du nom du call.execute(); //exécution et récupération du résultat
paramètre*/
261 System.out.println(call.getString(1)); 262
System.out.println("Nombre d'abonnés = " + M.AFILAL
resultat); M.AFILAL

Requête SQL avec CallableStatement: Exemple JDBC 2.0


Exemple avec Une fonction Des ajouts ont été faits aux spécifications JDBC 1.0 et beaucoup d'interfaces et de classes de
Connection connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/afilaldb", "root", java.sql ont été enrichis.
"mounir");
Statement statement = connection.createStatement(); De plus certains ajouts sont implantés dans le paquetage javax.sql.
statement.executeUpdate("DROP FUNCTION IF EXISTS nbPersoParticulier");
String procedureReq = "CREATE FUNCTION "
+ "nbPersoParticulier( param_1 VARCHAR(30), param_2 VARCHAR(30)) "
Les ajouts de JDBC 2.0 concernent :
+ "RETURNS INTEGER\n" – des nouvelles possibilités de traiter les ResultSet (autrement que séquentiellement, ...)
– + "BEGIN\n" – le traitement par batch
+ " DECLARE leRetour INTEGER;\n" – des types de données avancés
+ " SET leRetour = (SELECT COUNT(*) FROM Personnes WHERE " – la notion d'ensemble de lignes manipulables même sans connexion maintenue ouverte :
+ " nom = param_1 AND prenom NOT LIKE param_2);\n" les RowSet.
+" RETURN leRetour;\n" – l'utilisation du service de nommage universel de Java, JNDI «Java Naming and Directory
– + "END\n"; Interface» (pour les connexions aux bases de données par exemple)
statement.executeUpdate(procedureReq);
– JNDI est utilisé si on utilise, par exemple, un serveur d’applications comme
String sql = "{? = call nbPersoParticulier(?, ?)}"; WebLogic de BEA ou WebSphere d’IBM.
CallableStatement call = connection.prepareCall(sql);
– JNDI est une API permettant aux programmes Java de localiser des données
call.registerOutParameter(1, Types.INTEGER);
ou des objets via un système de nommage
call.setString(2, "Afilal_5");
– Exemple d’autre service de nomage DNS (Domain Name System) :
call.setString(3, "Nada");
call.execute(); service de nommage utilisé sur internet pour permettre la
System.out.println(call.getInt(1)); M.AFILAL 263
correspondance entreM.AFILAL
un nom de domaine et une adresse IP 264
– l'utilisation du service de transaction de Java JTS.
Traitement avancé des ResultSet: types et modes de parcours Traitement avancé des ResultSet: types et modes de parcours

Les possibilités de l'objet ResultSet dans la version 1.0 de JDBC sont très limitées : Il est aussi possible de préciser les modes pour ResultSet, s’il peut être mis à jour ou non :
– parcours séquentiel de chaque occurrence de la table retournée. – ResultSet.CONCUR_READ_ONLY : lecture seule
– resultSet.CONCUR_UPDATABLE : mise à jour possible (updeter, insérer, détruire
certaines de ses lignes. )
La version 2.0 apporte de nombreuses améliorations à cet objet :
– le parcours des occurrences dans les deux sens,
C'est à la création d'un objet de type Statement qu'il faut préciser le type et le mode à utiliser.
– la possibilité de faire des mises à jour sur une occurrence.
Statement st = conn.createStatement(type, mode);
– Par défaut (se sont les caractéristiques de la version 1.0) on a:
Concernant le parcours, il est possible de préciser trois types de fonctionnement :
le type est: TYPE_FORWARD_ONLY
– forward-only : parcours séquentiel de chaque occurrence
(ResultSet.TYPE_FORWARD_ONLY) Le mode est: CONCUR_READ_ONLY
Remarque
– scroll-insensitive : Vision figée du résultat de la requête au moment de son évaluation – Les modifications d’un ResultSet updatable (de type CONCUR_UPDATABLE) sont
«les occurrences ne reflètent pas les mises à jour qui peuvent intervenir durant le faites sur le ResultSet pas dans la base.
parcours» (ResultSet.TYPE_SCROLL_INSENSITIVE) – Elles seront faites dans la base à l'aide de la méthode updateRow()
– Exemple :
– scroll-sensitive : les occurrences reflètent les mises à jour (modifiées/détruites) qui rs.first();
peuvent intervenir durant le parcours (ResultSet.TYPE_SCROLL_SENSITIVE) rs.updateString(1, "100020");
rs.updateFloat("salary", 10000.0f);
M.AFILAL 265 266
rs.updateRow(); // Pour appliquerM.AFILAL
les modifications dans la base de données

Traitement avancé des ResultSet: parcourt dans un resultSet Traitement avancé des ResultSet: parcourt dans un resultSet
A la création du ResultSet, le curseur est positionné avant la première occurrence à traiter.
Durant le parcours d'un ResultSet, il est possible d'effectuer des mises à jour sur la ligne
Pour se déplacer dans l'ensemble des occurrences, courante du curseur.
– il y a toujours la méthode next() pour se déplacer sur le suivant Pour cela, il faut déclarer l'objet ResultSet comme acceptant les mises à jour.
– mais aussi plusieurs autres méthodes pour permettre le parcours des occurrences en – Avec les versions précédentes de JDBC, il fallait utiliser la méthode executeUpdate()
fonctions du mode utilisé dont les principales sont : avec une requête SQL.
Méthode Rôle Maintenant pour réaliser ces mises à jour, JDBC 2.0 propose de les réaliser via des appels de
boolean isBeforeFirst()
Renvoyer un booléen qui indique si la position courante du curseur se trouve avant la méthodes plutôt que d'utiliser des requêtes SQL.
première ligne
Renvoyer un booléen qui indique si la position courante du curseur se trouve après la
boolean isAfterLast() Méthode Rôle
dernière ligne
boolean isFirst() Renvoyer un booléen qui indique si le curseur est positionné sur la première ligne permet de mettre à jour la colonne dont le nom est fourni en paramètre. Le
updateXXX(String, XXX)
type Java de cette colonne est XXX
boolean isLast() Renvoyer un booléen qui indique si le curseur est positionné sur la dernière ligne
boolean first() Déplacer le curseur sur la première ligne permet de mettre à jour la colonne dont l'index est fourni en paramètre. Le
updateXXX(int, XXX)
boolean last() Déplacer le curseur sur la dernière ligne type Java de cette colonne est XXX
Déplacer le curseur sur la ligne dont le numéro est fourni en paramètre à partir du permet d'actualiser les modifications réalisées avec des appels à
updateRow()
boolean absolute(int) début si il est positif et à partir de la fin si il est négatif. 1 déplace sur la première ligne, updateXXX()
-1 sur la dernière, -2 sur l'avant dernière ...
boolean rowsUpdated() indique si la ligne courante a été modifiée
Déplacer le curseur du nombre de lignes fourni en paramètre par rapport à la position
courante du curseur. Le paramètre doit être négatif pour se déplacer vers le début et deleteRow() supprime la ligne courante
boolean relative(int)
positif pour se déplacer vers la fin. Avant l'appel de cette méthode, il faut
obligatoirement que le curseur soit positionné sur une ligne. rowDeleted() indique si la ligne courante est supprimée
Déplacer le curseur sur la ligne précédente. Le booléen indique si la première
boolean previous()
occurrence est dépassée. moveToInsertRow() permet de créer une nouvelle ligne dans l'ensemble de résultat
void afterLast() Déplacer le curseur après la dernière ligne
void beforeFirst() M.AFILAL
Déplacer le curseur avant la première ligne 267 insertRow() permet de valider la M.AFILAL
création de la ligne 268

int getRow() Renvoyer le numéro de la ligne courante


Traitement avancé des ResultSet: parcourt dans un resultSet Traitement avancé des ResultSet: parcourt dans un resultSet

Remarque
Remarque
– Pour insérer une nouvelle ligne dans le jeu de résultat, il faut tout d'abord appeler la
– Pour réaliser une mise à jour dans la ligne courante désignée par le curseur, il faut utiliser méthode moveToInsertRow().
une des méthodes updateXXX() sur chacun des champs à modifier.

– Cette méthode déplace le curseur vers un buffer dédié à la création d'une nouvelle ligne.
– Une fois toutes les modifications faites dans une ligne, il faut appeler la méthode
updateRow() pour reporter ces modifications dans la base de données Il faut alimenter chacun des champs nécessaires dans cette nouvelle ligne.
car les méthodes updateXXX() ne font des mises à jour que dans le jeu de résultats. Pour valider la création de cette nouvelle ligne, il faut appeler la méthode
insertRow().

– Les mises à jour (vers la base de données) sont perdues si un changement de ligne
intervient avant l'appel à la méthode updateRow(). – Pour supprimer la ligne courante, il faut appeler la méthode deleteRow().
Cette méthode agit sur le jeu de résultats et sur la base de données.
– La méthode cancelRowUpdates( ) permet d'annuler toutes les modifications faites dans la
ligne.
L'appel à cette méthode doit être effectué avant l'appel à la méthode updateRow().

M.AFILAL 269 M.AFILAL 270

Traitement avancé des ResultSet: Exemples Traitement avancé des ResultSet: Exemples
Exemple (mise à jour d’enregistrement depuis java)
– Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, Exemple (Insertion et suppression depuis java)
ResultSet.CONCUR_UPDATABLE); – Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
– stmt.setFetchSize(25); ResultSEt.CONCUR_UPDATEABLE);
– ResultSet resultat = stmt.executeQuery(" SELECT * FROM Employe WHERE NumEmp – ResultSet resultat = stmt.executeQuery(" SELECT * From Employe" );
< 6 " ); – Resultat.moveToInsertRow(); //autres méthodes : first, relative, ….
– Resultat.last( ); – Resultat.updateString("Name", " Joseph ");
– Resultat.updateFloat("Salary", 20) – Resultat.updateInt(1, 100);
– Resultat.cancelRowUpdates() – Resultat.updateFloat("Salary", 1200);
– Resultat.updateFloat("Salary", 50) – Resultat.insertRow(); // ajout d’une nouvelle ligne dans le ResultatSet et dans la base
– Resultat.updateRow (); – Resultat.last();
– Resultat.deleteRow();// suppression de la dernière ligne dans ResultatSet et dans la base
Remarque
– Pour les requêtes qui retournent plusieurs milliers de lignes, ce comportement n'est pas du
tout souhaité.
Car le chargement de plusieurs Mo d'un coup en mémoire peut entrainer un
ralentissement de toute l'application.
– Dans ces cas, il est préférable de ne charger les lignes en mémoires qu'au fur et à mesure
qu'on en a besoin dans notre code.
Ceci est possible via l’utilisationM.AFILAL
de la méthode setFetchSize «définie le nombre de271 M.AFILAL 272
lignes à charger en mémoire »
Autres remarques Conseilles pour une bonne utilisation du JDBC

Remarques Remarques
– L'obtention des valeurs de clé générées automatiquement lors d'une insertion, on fait par – Lorsque vous travaillez avec des instructions et des résultats, veillez toujours à bien
exemple (avec idPersonne est la clé primaire auto incrémente): fermer les objets Connection, Statement et ResultSet lorsque vous avez terminé ceci via
leur méthode close();
stmt.executeUpdate( "INSERT INTO personne (nom, prenom, taille) " + "values – Ne retourner que les données utiles lors de l'utilisation de requêtes SQL
('nom1', 'prenom1', 174)", Statement.RETURN_GENERATED_KEYS); – Toujours assurer un traitement des warnings et des exceptions
– La qualité du pilote JDBC est importante notamment en termes de rapidité, type de pilote,
version de JDBC supportée, ...
– Le type du pilote influe grandement sur les performances :
Le type 1 (pont JDBC/ODBC) : les pilotes de ce type sont à éviter car les différentes
couches mises en œuvre (JDBC, pilote JDBC, ODBC, pilote ODBC, base de
données) dégradent les performances
Le type 2 (utilise une API native de la base de données ) : les pilotes de ce type ont
généralement des performances moyennes
Le type 3 (JDBC, pilote JDBC, middleware, DB) : les pilotes de type 3
communiquent avec un middleware généralement sur le serveur. Ils sont le plus
souvent plus performants que ceux de type 1 et 2
Le type 4 (JDBC, pilote JDBC, DB) les pilotes de type 4 offre en général les
meilleures performances car ils sont écrits en Java et communiquent directement
avec la base de données
M.AFILAL 273 M.AFILAL 274
– Il est donc préférable d'utiliser des pilotes de type 4 ou 3.

Graphisme 2D et les images Graphisme 2D et les images

L’objectif est de dessiner dans un composant graphique en utilisant un outil de dessin: le L’API Java 2D étend, les mécanismes de dessin Java.AWT précédents, pour dessiner des
graphiques 2D, afin de:
contexte graphique.
– manipuler du texte et des polices, charger et utiliser des images,
– Le contexte graphique est une instance de java.awt.Graphics ou java.awt.Graphics.2D.
– définir et gérer les couleurs et les espaces colorimétriques.
Ce contexte graphique permet de dessiner des formes, du textes et des images dans la
zone de dessin d’un composant.
Java 2D est une API qui permet de dessiner des graphiques en deux dimensions.
– C’est une technologie puissante qu’on peut utiliser pour créer
Donc, dans cette partie, on va voir comment des interfaces utilisateur riches,
– réaliser des tracés de formes, du textes et des images en deux dimensions des jeux, des animations,
des applications multimédias
Les classes qu’on va utiliser , entre autres, pour dessiner appartiennent aux paquetages : ou divers effets spéciaux.
– java.awt, java.awt.color, java.awt.font,
– java.awt.geom, java.awt.image et java.awt.print. L’outil de dessin sur un composant ou Le contexte graphique d’un composant fournit des
méthodes afin de réaliser trois sortes d'objets graphiques :
L’ensemble de ces classes constituent l'essentiel de l'API 2D, destinée au tracé: Les formes,
– de formes, de texte et d'images. Les textes,
Les images.

275 276
Graphisme 2D et les images Graphisme 2D et les images

Remarque
– Pour dessiner dans un composant graphique, on passe nécessairement par une instance de Cas de rafraichissement pour un affichage
Graphics – Il existe une méthode qui est automatiquement sollicitée pour afficher ou réafficher un
– Le JDK 1.2 a introduit la bibliothèque Java 2D , qui contient composant.
la classe Graphics2D, qui dérive de Graphics, afin de dessiner par exemple
– des lignes avec des épaisseur ou pas, paintComponent( ) « pour les composants javax.swing ».
– des rectangles, des ellipses,
– de faire pivoter ou translater des formes, etc... paint( ) « pour les composants java.awt »

– Chaque composant peut accéder implicitement ou explicitement à son contexte graphique. On doit passer par cette méthode pour être sûr que notre tracé spécifique ou
personnalisé soit réaffiché au moment du rafraîchissement.
Implicitement, comme paramètre de la méthode, dans,
– Ceci impose, de redéfinir cette méthode.
– la méthode paint( Graphics g ) « pour des composant AWT »
Avec utilisation d’un héritage par rapport à un composant graphique
– et paintComponent (Graphics  Graphics2D g) «pour des composants swing» existant
Explicitement dans «appel à super.paintComponent(Graphics g) ».
– un composant ou dans une image, par getGraphics( ),

– Un contexte graphique utilise des ressources systèmes.


L'acquisition explicite doit être accompagnée, in fine, par une libération explicite au
moyen de la fonction dispose( ).
277 278

Graphisme 2D et les images Graphisme 2D et les images: traçât spécifique

Remarque Exemple de fenêtre avec paintComponent personnalisée et qui est utilisée lors d’un
rafraichissement « Voir code en bas »
– Une fenêtre doit se réafficher (donc, rafraichissement de la fenêtre), dans le cas de
redimensionnant ou de translation.
d’ajout de composant ou de suppression de composant.
quand elle se situait en dessous d'une autre fenêtre et qu’elle obtient le focus,
etc…

– Lors du rafraichissement,

le cadre de la fenêtre s'affiche en premier, et ensuite c'est le tour de chacun des


composants qui constituent cette fenêtre,
– comme les boutons, les menus, les labels, les panneaux.

C'est à ce moment là que la méthode paintComponent( ) de chacun de ces


composants est sollicité.
– C'est à l'intérieur de cette dernière que se trouve tout le tracé correspondant au
composant, comme par exemple le tracé d'un bouton.

279 280
Graphisme 2D et les images Graphisme 2D et les images
public class Fenêtre extends JFrame {
– public Fenêtre( ) {
setSize(300, 250); Rôle de la fonction paintComponent
setTitle("Test dessins");
getContentPane().setBackground(Color.ORANGE); – Pour dessiner des formes avec la bibliothèque Java 2D, on doit utiliser un objet de la
classe Graphics2D (Graphics2D une classe fille de la classe Graphics).
getContentPane().add(new Zone( ) , BorderLayout.CENTER);
– }
– La méthode paintComponent() reçoit automatiquement en argument un objet de la classe
}
Graphics2D.
class Zone extends JPanel {
– protected void paintComponent(Graphics g1) {
Malgré que le paramètre de cette méthode est lui de type Graphics.
super.paintComponent(g1);
Graphics2D g = (Graphics2D) g1; //g.setColor(Color.BLUE);
On doit effectuer un transtypage dans le cas où on désire utiliser la technologie 2D.
g.drawArc(-40, 10, 100, 100, 0, 90);
– public void paintComponent(Graphics g) {
g.drawLine(10, 10, 10, 60);
Graphics2D zone = (Graphics2D) g;
g.drawLine(10, 60, 60, 60);
...
g.drawOval(10, 80, 120, 120);
– }
g.drawPolygon(new int[ ] {110, 170, 170}, new int[ ] {60, 60, 10}, 3);
g.drawPolyline(new int[ ] {210, 270, 270}, new int[ ] {60, 60, 10}, 3);
g.drawRect(10, 80, 120, 120);
g.drawRoundRect(150, 80, 120, 120, 10, 10);
– } 281 282
}

Graphisme 2D et les images Graphisme 2D et les images: le rendu

Remarque « rôle de la fonction repaint() dans le rafraichissement » Le rendu est le résultat final qui est retourné à la fin de la manipulation des formes, des textes
« cette méthode force le rafraichissement dans une situation donnée » et des images après utilisations de certaines règles et technologies sur leur manipulation.

– repaint ( ) en Swing est la méthode par excellence pour rafraîchir un affichage ! Le rendu (ce qui est présenté et comment: ce qui est livré ou renvoyé avec les règles
utilisées)
– est un processus qui consiste
– repaint( ) poste un appel à update(). Plusieurs appels peuvent être groupés.
à prendre un ensemble de formes, textes et images
et à définir une façon de les représenter, de les manipuler et d’agir sur eux:
– update( ) appelle paint( ).
– Les colorer sur un écran
– De les faire translater ou de leur faire des rotations (Transformations,
– paint( ) appelle successivement Clipping, Composition, …)
paintComponent( ) pour le dessin ( le paint( ) en AWT) – ou autre.
paintBorder()
paintChildren(). Le rendu Spécifie ou donne comment un objet sera dessiné dans la surface d’affichage.

– paintComponent( ) par défaut appelle ComponentUI.update() qui efface et redessine le Graphics2D gère quatre opérations pour un rendu :
fond si le composant est opaque (JPanel l’est par défaut). – Dessiner un contour d'une forme, avec la méthode draw( ).
– Remplir une forme, avec la méthode fill( ).
– Pour dessiner dans un composant, on redéfinit paintComponent( ) et – Dessiner du texte, avec la méthode drawString( ).
il est utile d’appeler super.paintComponent() dans paintComponent( ) . 283
– Dessiner une image, avec l'une des nombreuses formes de la méthode drawImage( ).
284
Graphisme 2D et les images: le rendu Graphisme 2D et les images: Attributs du contexte graphique
Par rapport aux méthodes proposées par la classe java.awt.Graphics, l'API Java 2D supporte
plusieurs options supplémentaires : donc avec l'API Java 2D
Le contexte graphique instancié par un objet Graphics2D possède les attributs ci-dessous
«couleur, trait, police, transformation,….», qui sont gérer par diverses méthodes d'accès :
– On peut facilement produire une grande quantité de formes différentes(exp: concaténation
des formes). – paint (= couleur):
Le paint en cours (un objet de type java.awt.Paint) définit les couleurs de
– L'outil de dessin peut être contrôlé (exp: changement de taille des lignes). remplissage (unies ou dégradées) d'une forme.
– Cela concerne également les contours et le texte.
– Les formes peuvent être remplies aves des couleurs uniformes, des dégradés ou des – il est possible de passer des Color à setPaint( ) lorsque nous désirons utiliser
des couleurs unies.
motifs répétés.

– stroke (= trait):
– Des transformations permettent de déplacer, d'agrandir, de rétrécir, de faire pivoter ou de
Graphics2D utilise le stroke pour déterminer comment dessiner le contour des
déformer des formes. formes qui sont passées à la méthode draw( ).
Il est possible de fixer le trait courant en utilisant setStroke( ).
– Les formes peuvent être coupées automatiquement pour ne pas déborder d'une zone L'API 2D fournie une classe très pratique, java.awt.BasicStrocke, qui permet
donnée. d'implémenter différentes épaisseurs, extrémités, jointures et pointillés de ligne.

– On peut sélectionner des règles de composition pour décrire la manière dont les pixels – font (= police):
d'une nouvelle forme doivent être combinées avec les pixels existants. Le texte est rendu « présenté » par une forme représentant des caractères à dessiner.
La police courante est définie à l'aide de setFont( ).
– Des conseils d'affichage peuvent être fournis pour choisir un compromis entre la vitesse285 286

et la qualité de l'affichage.

Graphisme 2D et les images: Attributs du contexte graphique Graphisme 2D et les images: Attributs du contexte graphique

– transformation: – forme de masquage:


Les formes, les textes et images sont géométriquement transformés avant d'être Toutes les opérations de rendu sont limitées à l'intérieur de la forme de masquage.
rendus. Aucun pixel extérieur à cette forme n'est modifié. La méthode setClip() permet de
– Cela signifie qu'ils peuvent être déplacés, retournés ou allongés. définir une zone de dessin.
La transformation de Graphics2D convertit les coordonnées de l'espace utilisateur en
coordonnées de l'espace de périphérique. – indications de rendu:
La transformation courante peut être modifiée à l'aide des méthodes Diverses techniques permettent le rendu de primitives graphiques.
– translate( ), rotate( ), scale( ) et shear( ). Elle représentent généralement un compromis entre vitesse de rendu et qualité
La méthode setTransform( ) permet de définir une transformation globale entre visuelle. «la vitesse d'affichage et la qualité d'affichage »
l'espace de l'utilisateur et l'espace matériel: Les indications de rendu (constantes définies dans la classe RenderingHints)
– setTransform( ) permet d’imposer une transformation définie comme paramètre précisent les techniques à utiliser. (via une clé et une valeur)
et qui va concerner toute le reste du code suivant l’appel de cette méthode. La méthode setRenderingHints() permet de définir des conseils d'affichage, qui
correspondent à des compromis entre la vitesse et la qualité d'affichage
– règle de composition:
Une règle de composition permet de définir la façon de combiner les couleurs d'une – Créer une forme:
primitive à des couleurs existantes de la surface de dessin d'un Graphics2D. L'API Java 2D fournit plusieurs objets de formes et des méthodes pour combiner ces
Cet attribut est défini par la méthode setComposite(), qui accepte en argument une formes.
instance de java.awt.AlphaComposite. – L'interface Shape représente un objet de forme quelconque
La composition permet
– de créer des parties de dessin ou d'image totalement ou partiellement
transparentes ou de les combiner selon d'autres façons intéressantes. 287 288
Graphisme 2D et les images: Pipeline d'affichage Graphisme 2D et les images: Méthodes de la classe Graphics
Les primitives graphiques (formes, texte et images) traversent le moteur de rendu suivant une
série d'opérations baptisée pipeline d'affichage (canal ou pipeline de rendu). Graphics2D comporte quelques méthodes permettant de dessiner et remplir des formes
Dans ce pipeline d'affichage, les opérations sont ainsi réalisées suivant une séquence, donc courantes qui sont héritées de la classe Graphics.
dans un ordre bien précis.
Les étapes suivantes sont possibles pour dessiner une forme :
– Le contour de la forme est dessinée.
Pour les formes dont les contours sont tracés à l'aide de draw(), le trait en cours est
utilisé.
Le texte est affiché en représentant les caractères par des formes, dans la police en
cours.
– La forme est transformée.
– La forme est restreinte à une zone d'affichage. Masquer le résultat à l'aide de la forme de
masquage en cours.
– Le reste de la forme est rempli. Déterminer les couleurs à utiliser. Pour une forme
remplie, l'objet paint() en cours définit les couleurs de remplissage de la forme. Pour une
image, les couleurs sont prises dans l'image elle-même.
– Les pixels de la forme remplie sont composés avec les pixels existants. Combiner les
couleurs avec la surface de dessin existante en utilisant la règle de composition en cours.

289 290

Graphisme 2D et les images: Méthodes de la classe Graphics Graphisme 2D et les images: Méthodes de la classe Graphics

Méthode Description
drawArc(int x, int y, int largeur, int hauteur, int angledébut, int
Dessine un arc de cercle (angle en degré) Remarque
angleDeplacement)
– Pour chacune des méthodes fill ( ) du tableau, il existe une méthode draw()
drawLine(int xdébut, int ydébut, int xfin, int yfin) Dessine une ligne
correspondante créant la forme selon un dessin non plein.
drawOval(int x, int y, int largeur, int hauteur) Dessine un ovale
Dessine un polygone fermé en joignant les – La méthode de dessin d’un polygone est définie par deux tableaux de coordonnées des
drawPolygon(int[ ] lesX, int[ ] lesY, int nombrePoint)
extrémités sommets, d'une part le tableau des X, et d'autre part le tableau des Y suivi ensuite du
Dessine une ligne en reliant une série de nombre de point.
drawPolyline(int[] lesX, int[] lesY, int nombrePoint)
points, sans la fermer Des méthodes de la classe Graphics lisent ces tableaux et dessinent le contour du
drawRect(int x, int y, int largeur, int hauteur) Dessine un rectangle polygone ou remplissent celui-ci.
drawRoundRect(int x, int y, int largeur, int hauteur, int
Dessine un rectangle à coins arrondis
largeurArc, int hauteurArc)
– La méthode fillArc() requiert six arguments entiers.
Les quatre premiers définissent le rectangle englobant un ovale - comme la méthode
fillArc(int x, int y, int largeur, int hauteur, int angledébut, int fillOval( ).
Dessine un arc de cercle plein
angleDeplacement)
Les deux derniers définissent la portion de l'ovale à dessiner, sous forme de position
fillOval(int x, int y, int largeur, int hauteur) Dessine un ovale plein angulaire de départ et de déplacement (tous deux en degrés).
fillPolygon(int[] lesX, int[] lesY, int nombrePoint) Dessine un polygone plein La position zéro degré se trouve à trois heures, l'angle croît (angle positif) dans le
fillRect(int x, int y, int largeur, int hauteur) Dessine un rectangle plein sens inverse des aiguilles d'une montre.
fillRoundRect(int x, int y, int largeur, int hauteur, int largeurArc,
Dessine un rectangle plein à coins arrondis
int hauteurArc) 291 292
Graphisme 2D et les images: Exemple * Graphisme 2D et les images
public class Fenêtre extends JFrame {
– public Fenêtre() {
setSize(300, 250); Les méthodes données dans le dernier exemple font parties de la classe Graphics.
setTitle("Test dessins");
getContentPane().setBackground(Color.ORANGE);
L'API 2D se sert d'une approche, entièrement différente, orientée objet.
getContentPane().add(new Zone() );
– Au lieu de méthodes, il existe les classes correspondantes suivantes :
– }
Line2D; Rectangle2D; RoundRectangle2D; Ellipse2D;
}
class Zone extends JPanel { Arcs2D; QuadCurve2D; CubicCurve2D; GeneralPath
– protected void paintComponent(Graphics g1) { – Ces classes implémentent toutes l'interface Shape.
super.paintComponent(g1);
Graphics2D g = (Graphics2D) g1; Les classes Line2D, Rectangle2D, RoundRectangle2D, Ellipse2D et Arc2D
g.drawArc(-40, 10, 100, 100, 0, 90); – correspondent respectivement aux méthodes drawLine(), drawRect(), drawRoundRect(),
g.drawLine(10, 10, 10, 60); drawOval() et drawArc() de la classe Graphics.
g.drawLine(10, 60, 60, 60);
g.drawOval(10, 80, 120, 120); L'API 2D rajoute deux classes supplémentaires :
g.drawPolygon(new int[ ] {110, 170, 170}, new int[ ] {60, 60, 10}, 3); – les courbes quadratiques et cubiques.
g.drawPolyline(new int[ ] {210, 270, 270}, new int[ ] {60, 60, 10}, 3);
g.drawRect(10, 80, 120, 120);
g.drawRoundRect(150, 80, 120, 120, 10, 10);
– }
293 294
}
Autre exemple: voir fenetre_2

Graphisme 2D et les images Diagramme UML des classes implémentant l'interface Shape

Remarque:
– Il n'existe aucune classe Polygone2D.

En remplacement de la classe Polygone2D,


– la classe GeneralPath décrit les tracés composées
de lignes, de courbes quadratiques ou cubiques.
– On peut utiliser GeneralPath pour construire un polygone.

Les classes Rectangle2D, RounRectangle2D, Ellipse2D et Arc2D héritent toutes d'une


superclasse commune, RectangleShape.
– Formellement,
les ellipses et les arcs ne sont pas des formes rectangulaires, mais ils peuvent être
contenus dans un rectangle :

Remarque
– il existe une classe abstract Point2D (utilisez ses clases dérivées: Point, Point2D.Double,
Point2D.Float) qui décrit un point avec des coordonnées x et y.
Les points sont utilisés pour définir des formes « bien qu'ils n'en soient pas eux-
mêmes ».
295 296
Graphisme 2D et les images: dessin d’objet 2D Graphisme 2D et les images: dessin d’objet 2D
Remarque
Pour dessiner une forme – On peut utiliser la méthode draw3DRect() de la classe Graphics pour dessiner un
rectangle.
– il faut commencer par créer un objet d'une classe qui implémente l'interface Shape, – Avec java 2D, on peut améliorer nos dessins avec les nombreux outils que fournit la
bibliothèque (transformation, dégradés, transparence, composition entre les formes et les
images, etc…
– ensuite dans la classe Graphics2D, faire appel uniquement à l'une des deux méthodes
– Les classes Xxx2D.Float et Xxx2D.Double constituent des sous-classes des classes
spécifiques,
abstraites Xxx2D.

soit la méthode draw( ) pour dessiner le contour de la forme,


L’interface Shape dispose d’un certain nombre de méthodes qui doivent être redéfinies dans
ses sous classes.
soit la méthode fill( ) si on veut remplir cette forme.
– boolean contains(Point ou Rectangle2D )
Cette méthode, surdéfinie suivant le type de paramètres requis, teste si le point « ou
Exemple: « tracer un rectangle» rectangle » passé en argument se trouve bien à l'intérieur de la zone délimitée par la
– class Zone extends JPanel { // extends JComponent forme réellement utilisée de type: rectangle, ellipse, portion d'ellipse, polygone, etc.
protected void paintComponent(Graphics g) {
– Graphics2D surface = (Graphics2D) g; // contexte graphique – Rectangle2D getBounds2D( ) ou Rectangle getBounds ( )
– Rectangle2D rectangle = new Rectangle2D.Double(10.0, 10.0, 200.0, 100.0); Les deux méthodes retournent la zone rectangulaire qui englobe la forme réelle.
– surface.draw(rectangle);
} – boolean intersects( Rectangle2D rect)
– } 297 Cette méthode teste si une partie ou la totalité de la zone rectangulaire passée en 298
argument est en contact avec la forme en cours (chevauchement ou pas).

Graphisme 2D et les images: dessin d’objet 2D Graphisme 2D et les images: dessin d’objet 2D

Construction des objets Rectangle2D et Ellipse2D. Lors de la construction d'une ellipse, on connait généralement
– le centre, la largeur et la hauteur, mais pas les points des angles du rectangle englobant.
On doit spécifier :
Il existe une méthode setFrameFromeCenter( ) qui utilise le point central mais elle
– Les coordonnées x et y du coin supérieur gauche ; requiert toujours l'un des quatre points d'angle.

– La largeur et la hauteur. – Pour tracer une ellipse en utilisant le centre comme critère, on fait:
Rectangle2D rectangle = new Rectangle2D.Double(x, y, largeur, hauteur);
Ellipse2D ellipse = new Ellipse2D.Double();
Ellipse2D ellipse = new Ellipse2D.Double(x, y, largeur, hauteur); ellipse.setFrameFromCenter(Xcentre, Ycentre, Xangle, Yangle);
– Pour les ellipses, ces valeurs font référence au rectangle englobant ellipse.setFrameFromCenter(PointCentre, PointAngle);

Rectangle2D rectangle = new Rectangle2D.Double();


– rectangle.setFrameFromDiagonal(xdébut, ydébut, xfin, yfin);

Rectangle2D rectangle = new Rectangle2D.Double();


– Point2D début = new Point2D.Double(xdébut, ydébut);
– Point2D fin = new Point2D.Double(xfin, yfin);
rectangle.setFrameFromDiagonal(début, fin);
299 300
Graphisme 2D et les images: dessin d’objet 2D Graphisme 2D et les images: dessin d’objet 2D
Exemple
– class Zone extends JComponent { Rectangle arrondi
private Ellipse2D ellipse = new Ellipse2D.Double();
private Rectangle2D rectangle = new Rectangle2D.Double(); – La classe RoundRectangle2D permet de tracer des rectangles avec les coins arrondis.
public Zone( ) {
– Rectangle2D r = new Rectangle2D.Double(50, 50, 190, 110); – Pour cette forme, on doit spécifier les coordonnées du coin supérieur gauche, la largeur,
– ellipse.setFrameFromCenter(r.getCenterX(), r.getCenterY(), r.getMaxX(), la hauteur ainsi que les dimensions en x et en y de la zone qui doit être arrondie.
r.getMaxY()); RoundRectangle2D rectangle = new RoundRectangle2D.Double(x, y, largeur,
– rectangle.setFrameFromDiagonal(ellipse.getX(), ellipse.getY(), r.getMaxX(), hauteur, largeurArc, hauteurArc);
r.getMaxY());
}
protected void paintComponent(Graphics g) {
– Graphics2D surface = (Graphics2D) g;
– surface.draw(rectangle);
– surface.draw(ellipse);
}
– }

301 302

Graphisme 2D et les images: dessin d’objet 2D Graphisme 2D et les images: dessin d’objet 2D
Arc de cercle Remarque
– La classe qui implémente l'arc de cercle est la classe Arc2D. – Attention: les angles s'expriment en degré.

– Pour construire un arc, il faut fournir un rectangle englobant cet arc, suivi de l'angle de Exemple
départ et de l'angle d'ouverture, ainsi que le type de fermeture, qui peut être:
– class Zone extends JPanel{
Arc2D.OPEN, Arc2D.PIE ou Arc2D.CHORD.
protected void paintComponent(Graphics g) {
– super.paintComponent(g);
– Arc2D arc = new Arc2D.Double(x, y, largeur, hauteur, angleDépart, angleArc,
– Graphics2D surface = (Graphics2D) g;
typeFermetureArc) ;
– Arc2D arc = new Arc2D.Double(10, 10, 200, 200, 60, 180, Arc2D.PIE);
– surface.draw(arc);
}
– }

303 304
Graphisme 2D et les images: dessin courbes 2D Graphisme 2D et les images: dessin courbes 2D
Dans cette partie, on va voir comment tracer Tracé de courbes
– des lignes, La bibliothèque Java 2D fournit
– des courbes quadratiques, – des courbes quadratiques représentées par la classe QuadCurve2D et
– des courbes cubiques, – des courbes cubiques représentées par la classe CubicCurve2D (courbes de Bézier).
– ainsi qu'un assemblage de ces différents types de tracé.
Les courbes quadratiques et cubiques sont spécifiées par deux points de terminaison, et un ou
deux points de contrôle.
Tracé de segments de lignes
– C'est le nombre de point de contrôle qui détermine le type de courbe.
– Le tracé des segments de lignes s'obtient au moyen de la classe Ligne2D.
Ainsi, la forme des courbes peut être modifié en changeant les points de contrôle.
– Pour construire une ligne, on donne les points de départ et d'arrivé, sous la forme
d'objets Point2D ou de paires de nombres. – QuadCurve2D qq = new QuadCurve2D.Double(xdébut, ydébut, contrôleX, contrôleY,
xfin, yfin);
Point2D début = new Point2D.Double(xdébut, ydébut);
– CubicCurve2D cc = new CubicCurve2D.Double(xdébut, ydébut, contrôleDébutX,
Point2D fin = new Point2D.Double(xfin, yfin);
contrôleDébutY, contrôleFinX, contrôleFinY, xfin, yfin);
– Ligne2D ligne _1 = new Ligne2D.Double(début, fin);
– Ligne2D ligne_2 = new Ligne2D.Double(xdébut, ydébut, xfin, yfin);

305 306

Graphisme 2D et les images: dessin courbes 2D


Graphisme 2D et les images: dessin courbes 2D Combinaison de plusieurs tracées avec un objet GeneralPath «tracé général »
Il est possible de construire plusieurs segments de lignes, de courbes quadratiques et de
Exemple courbes cubiques, et de les enregistrer dans un objet GeneralPath.
– class Zone extends JComponent {
protected void paintComponent(Graphics g) { – La procédure à suivre est :
– Graphics2D surface = (Graphics2D) g; Création d'un objet GeneralPath vierge.
– CubicCurve2D pétale = new CubicCurve2D.Double(150, 170, 10, 10, 290,
10, 150, 170); La première coordonnée du tracé peut alors être spécifiée à l'aide de la méthode
– surface.draw(pétale); moveTo( ).
}
– } Ensuite, il faut étendre le tracé en appelant l'une des méthodes suivantes :
– lineTo( ), quadTo( ), ou curveTo( ).
– Ces méthodes étendent respectivement le tracé avec une ligne, une courbe
quadratique ou une courbe cubique.
Pour appeler lineTo( ), fournissez le point de fin.
Pour les deux méthodes de courbes, fournissez les points de contrôle,
puis le point de fin (dans tous les cas, on a pas besoin de fournir le point
de début, puisqu'il correspond au point de fin du tracé précédent).

Le tracé peut être fermé en appelant la méthode closePath( ).


307 308
– Elle affiche une ligne entre le point courant et le dernier moveTo( ).
Graphisme 2D et les images: dessin courbes 2D Graphisme 2D et les images: dessin courbes 2D;
Remarque
Exemple – Un tracé général n'a pas besoin d'être connecté.
– Pour créer un polygone, – On peut appeler moveTo() à n'importe quel moment pour commencer un nouveau
appelez simplement moveTo() pour aller au premier point, segment de tracé.
suivi d'appels répétés lineTo( ) pour parcourir les autres points.
Pour terminer, appeler closePath() pour fermer le polygone. Exemple (Ok, Voir fenetre_3)
– Pour terminer, on peut avoir recours à la méthode append( ) pour ajouter des objets
– class Zone extends JPanel{ Shape à un tracé général (cad GeneralPath ).
protected void paintComponent(Graphics g) { – Le contour de la forme est alors ajouté à la fin du tracé.
– Graphics2D surface = (Graphics2D) g; – Le second paramètre de la méthode append( ) est true si la nouvelle forme doit être
connectée (via un segment) au premier point du tracé (le dernier moveTo avant appel de
– GeneralPath triangle = new GeneralPath( );
append), ou false dans le cas contraire.
– triangle.moveTo(10, 10);
– class Zone extends JComponent {
– triangle.lineTo(10, 100);
protected void paintComponent(Graphics g) {
– triangle.lineTo(100, 100);
– Graphics2D surface = (Graphics2D) g;
– triangle.closePath();
– GeneralPath dessin = new GeneralPath( );
– surface.draw(triangle);
– dessin.moveTo(150, 140);
}
– dessin.curveTo(10, 0, 290, 0, 150, 140);
– }
– dessin.append(new Ellipse2D.Double(120, 140, 60, 60), false);
– Voir fenetre_3
309
– surface.draw(dessin); 310
}
– }

Graphisme 2D et les images: dessin courbes 2D Graphisme 2D et les images: dessin courbes 2D
Zone (Area)
– On a vu comment générer des formes complexes en construisant des tracés généraux composés de
lignes et de courbes. Remarque
En utilisant un nombre suffisant de lignes et de courbes, on peut représenter quasiment – Pour construire une zone complexe, il faut commencer, en général, avec un objet de
n'importe quelle forme. zone par défaut (vierge).
Par exemple, les formes des caractères des polices visibles sur un écran ou sur un papier – Puis, on combine cette zone avec n'importe quelle autre forme.
imprimé sont toutes composées de lignes et de courbes cubiques: « D ».
Cependant, et de manière occasionnelle, il est plus simple de construire une courbe en utilisant des zones,
comme des zones formées par : des rectangles, des polygones ou des ellipses. – class Zone extends JComponent { //ok
protected void paintComponent(Graphics g) {
L'API Java 2D dispose d'un objet de zone représenté par la classe Area. – Graphics2D surface = (Graphics2D) g;
– Cette classe supporte quatre opérations de combinaisons de zones géométriques, qui mélangent les – GeneralPath dessin = new GeneralPath();
pixels de deux zones pour former une nouvelle zone:
– dessin.moveTo(150, 140);
add( ) : la zone résultat contient tous les points qui se trouvent dans la première zone ou dans la
seconde. – dessin.curveTo(10, 0, 290, 0, 150, 140);
substract( ) : la zone résultat contient tous les points de la première zone qui ne sont pas dans la – dessin.append(new Ellipse2D.Double(120, 140, 60, 60), false);
seconde zone. – Area zone = new Area();
intersect( ) : la zone résultat contient tous les points qui se trouvent dans la première zone et – zone.add(new Area(new Rectangle2D.Double(70, 20, 160, 190)));
dans la seconde.
exclusiveOr( ) : la zone résultat contient tous les points qui se trouvent dans la première zone ou
– zone.subtract(new Area(dessin));
dans la seconde, mais pas dans les deux. – surface.fill(zone);
}
– }
311
Graphisme 2D et les images: dessin courbes 2D Graphisme 2D et les images: outils de dessin
L'opération draw( ) de la classe Graphics2D dessine le contour d'une forme en utilisant l'outil de dessin
courant.
Exemple
– Par défaut, cet outil est une ligne pleine d'un seul pixel d'épaisseur.
– public void paintComponent(Graphics g) {
On peut sélectionner un autre outil de dessin de ligne en appelant la méthode setStroke( ). « setLigne »
Graphics2D g2 = (Graphics2D)g; – Il suffit de fournir à cette méthode en paramètre un objet d'une classe qui implémente l'interface
Ellipse2D ellipse = new Ellipse2D.Double(20, 20, 80, 80); Stroke.
Rectangle2D rectangle = new Rectangle2D.Double(60, 60, 100, 100); En fait, l'API Java 2D définit une seule classe adaptée, il s'agit de BasicStroke.
........... Epaisseur du trait
– On peut construire des outils de dessin de n'importe quel épaisseur :
Area areaOne = new Area(ellipse);
– Exemple
Area areaTwo = new Area(rectangle);
class Zone extends JComponent {
if (option.equals("add")) areaOne.add(areaTwo); – protected void paintComponent(Graphics g) {
else if (option.equals("intersection")) areaOne.intersect(areaTwo); Graphics2D surface = (Graphics2D) g;
else if (option.equals("subtract")) areaOne.subtract(areaTwo); GeneralPath dessin = new GeneralPath();
else if (option.equals("exclusive or")) areaOne.exclusiveOr(areaTwo); dessin.moveTo(180, 30);
g2.setPaint(Color.orange); dessin.lineTo(105, 105);
dessin.moveTo(30, 30);
g2.fill(areaOne); // remplissage via un area
dessin.lineTo(30, 180);
g2.setPaint(Color.black);
dessin.lineTo(180, 180);
g2.draw(areaOne); // draw conteur un area dessin.closePath();
…….. surface.setStroke(new BasicStroke(20));
– } surface.draw(dessin);
– }
}

Graphisme 2D et les images: outils de dessin Graphisme 2D et les images: outils de dessin
Intersection de deux traits
Extrémités du trait – Lorsque deux tracés épais se croisent, il existe trois possibilités pour le style de
– Lorsqu'un outil fait plus d'un pixel de large, son extrémité peut prendre différents l'intersection :
aspects : Une intersection en biais : les deux tracés sont reliés par une ligne droite
Une extrémité droite : perpendiculaire à la bissectrice de l'angle entre les deux tracés -
– l'outil s'arrête exactement au dernier point tracé - BasicStroke.CAP_BUTT. BasicStroke.JOIN_BEVEL.
Une extrémité arrondie : Une intersection arrondie : les deux tracés sont prolongés par une extrémité
– un demi-cercle est ajouté à la fin du tracé - BasicStroke.CAP_ROUND. arrondie - BasicStroke.JOIN_ROUND.
Une extrémité carré : Une intersection angulaire : les deux tracés sont prolongés par un angle -
BasicStroke.JOIN_MITER.
– un demi carré est ajouté à la fin du tracé - BasicStroke.CAP_SQUARE.
– Remarque
L'intersection angulaire n'est pas adaptée aux lignes qui se croisent selon un angle
aigu.
Si deux lignes se croisent selon un angle inférieur à la limite angulaire, une
intersection en biais est choisie automatiquement en remplacement.
Cela permet d'éviter les prolongements angulaires trop longs.
– Par défaut, cette limite angulaire est fixée à dix degrés.
Graphisme 2D et les images: outils de dessin Graphisme 2D et les images: outils de dessin
Exemple des extrémités et des intersections des traits
– Pour spécifier les extrémités et les intersections, on doit utiliser la méthode setStroke(). Motif de remplissage pour des lignes
– On passe en argument à cette méthode un objet de type BasicStroke, ceci en construisant – On peut choisir des lignes pointillées en définissant un motif de remplissage.
cet objet à l'aide de trois ou quatre arguments :
– Un motif de remplissage est composé d'un tableau float[ ] de nombres contenant la
surface.setStroke(new BasicStroke(épaisseur, extrémité, intersection, limite longueur des tirets blancs et noirs.
angulaire (en option)));
– En reprenant l’exemple précédant avec un tracé plutôt arrondi:
class Zone extends JComponent {
– Remarque
– protected void paintComponent(Graphics g) {
Graphics2D surface = (Graphics2D) g; Le motif de remplissage et la phase de remplissage peuvent être spécifiés lors de la
GeneralPath dessin = new GeneralPath( ); construction du BasicStroke.
dessin.moveTo(180, 30); Toutefois, cette partie s'intègre à la suite de la définition des extrémités et des
dessin.lineTo(105, 105); intersections de traits.
dessin.moveTo(30, 30); La phase de remplissage indique la position de démarrage de chaque ligne par
dessin.lineTo(30, 180); rapport au motif de remplissage.
dessin.lineTo(180, 180); – Normalement cette valeur vaut 0 (point de commencement de la ligne).
dessin.closePath();
surface.setStroke(new BasicStroke(20, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
surface.draw(dessin);
– }
}

Graphisme 2D et les images: outils de dessin Graphisme 2D et les images: remplissage

Exemple (ok)
– class Zone extends JComponent { Lorsqu'une forme est remplie, son intérieur est recouvert d'une couleur de remplissage.
protected void paintComponent(Graphics g) {
– Graphics2D surface = (Graphics2D) g; La méthode setPaint( ) permet de choisir un style de remplissage parmi plusieurs objets
– GeneralPath dessin = new GeneralPath(); «Color, GradientPaint, TexturePaint , …» qui implémentent l'interface Paint.
– dessin.moveTo(180, 30); dessin.lineTo(105, 105);
– dessin.moveTo(30, 30); dessin.lineTo(30, 180); Dans l'API Java 2D, il existe trois classes adéquates suivant le type de remplissage voulu :
– dessin.lineTo(180, 180); – Couleurs unies : la classe Color implémente l'interface Paint pour remplir des formes
– dessin.closePath(); avec une couleur unie.
– float[ ] motif = {5, 10, 5, 10, 20, 10};
– Dégradés : la classe GradientPaint fait varier la couleur de remplissage en l'interpolant
– surface.setStroke( new BasicStroke(5, // épaisseur
entre deux valeurs spécifiées.
– BasicStroke.CAP_ROUND, // extrémité
– Textures : La classe TexturePaint remplit une zone en répétant une image.
– BasicStroke.JOIN_MITER, // intersection
– 15, // limite angulaire
– motif, // motif de remplissage Remarque
– 0 // phase de remplissage – Ces types de remplissages peuvent être utilisés pour:
– )); l'apparence du trait (du contour) ,
– surface.draw(dessin); l'apparence du fond de la forme choisie.
}
– Suivant le cas, il suffit alors de faire appel,
– }
soit à la méthode draw( ),
– Remarque: valeurs par défauts sont BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0F,
motif, 0 soit à la méthode fill( ) de la classe Graphics2D.
Graphisme 2D et les images: remplissage Graphisme 2D et les images: remplissage
Couleurs unies Remarque
– La méthode setPaint( ) de la classe Graphics2D permet de sélectionner une couleur qui – On peut spécifier une couleur personnalisée en créant un objet Color à l'aide de ses
sera employée par toutes les opérations de dessin ultérieures pour le contexte graphique. composantes rouge, verte et bleue.
surface.setPaint(Color.red); En utilisant une échelle de 0 à 255 (chaque composante est codée sur un octet) pour
– Pour dessiner avec plusieurs couleurs, effectuer une opération, puis sélectionner une les proportions de rouge, de vert et de bleu, appelez le constructeur de Color de la
autre couleur avant de procéder à l'opération suivante. façon suivante :
– Les couleurs sont définies à l'aide de la classe Color. – surface.setPaint(new Color(0, 128, 128)); // un bleu-vert foncé
– La classe java.awt.Color propose des constantes prédéfinies pour les treize couleurs
standard indiqué dans le tableau ci-dessous. – Les méthodes brighter( ) et darker( ) de la classe Color produisent des versions plus
vives ou plus foncées de la couleur actuelle.
Constantes prédéfinies Couleur standard correspondante
La méthode brighter() permet de mettre un élément en surbrillance, mais avive en
Color.black noir
Color.blue bleu
réalité à peine la couleur.
Color.cyan cyan (bleu clair) Pour obtenir une couleur plus visible, appelez plusieurs fois la méthode.
Color.darkGray gris foncé – ObjetCouleur.brighter().brighter().brighter();
Color.gray gris
Color.green vert
Color.lightGray gris clair
Color.magenta magenta
Color.orange orange
Color.pink rose
Color.red rouge
Color.white blanc
Color.yellow jaune

Graphisme 2D et les images: remplissage Graphisme 2D et les images: remplissage


Exemple
– Exemple de définition de couleur à la fois sur le trait et sur le fond des formes
constituant le tracé de l'application : Dégradés
– class Zone extends JComponent { – Un dégradé de couleurs est un passage graduel d'une couleur à une autre.
protected void paintComponent(Graphics g) {
– Graphics2D surface = (Graphics2D) g; – La classe GradientPaint encapsule ce concept dans une implémentation d'interface Paint.
– CubicCurve2D pétale = new CubicCurve2D.Double(150, 170, 10, 10, 290,
10, 150, 170); Il suffit d'indiquer deux points et la couleur associée à chaque point.
– surface.setPaint(Color.YELLOW); – GradientPaint s'occupe de tous les détails pour faire fondre progressivement la
– surface.fill(pétale); couleur d'un point à l'autre.
– Ellipse2D cercle = new Ellipse2D.Double(120, 140, 60, 60);
– surface.setStroke(new BasicStroke(10)); – Les objets GradientPaint peuvent être construits en fournissant deux points et les
– surface.draw(cercle); couleurs de ces deux points:
– surface.setPaint(new Color(255, 128, 0));
– surface.fill(cercle); surface.setPaint(new GradientPaint(premierPoint, couleurPremierPoint,
} deuxièmePoint, couleurDeuxièmePoint));
– }
Graphisme 2D et les images: remplissage Graphisme 2D et les images: remplissage
Exemple (ok)
– class Zone extends JComponent {
Dégradés protected void paintComponent(Graphics g) {
– Dans ce cas, les couleurs sont interpolées le long de la ligne joignant ces deux points et – Graphics2D surface = (Graphics2D) g;
elles sont constantes le long des lignes perpendiculaires à cette ligne. – CubicCurve2D pétale = new CubicCurve2D.Double(150, 170, 10, 10, 290, 10, 150, 170);
– surface.setPaint(new GradientPaint( 10, 10,
// ou alors un seul paramètre new Point2D.Double(10, 10),
Les points situés après le dernier point de cette ligne prennent la couleur du dernier
– Color.red, 30, 30,
point de cette ligne.
// ou alors un seul paramètre new Point2D.Double(30, 30),
– Color.yellow, true ) );
Sinon, on peut appeler le constructeur GradientPaint (avec un paramètre – surface.fill(pétale);
supplémentaire) en choisissant true pour le paramètre cyclique : – Ellipse2D cercle = new Ellipse2D.Double(120, 140, 60, 60);
– surface.setPaint(new GradientPaint(premierPoint, couleurPremierPoint, – surface.setStroke(new BasicStroke(10));
deuxièmePoint, couleurDeuxièmePoint, cyclique)); – surface.draw(cercle);
– surface.setPaint(new GradientPaint( 120, 140,
– Le dernier paramètre, de GradientPaint définit si le gradient est cyclique. // ou alors un seul paramètre new Point2D.Double(120, 140),
– Color.blue, 180, 200,
Dans un gradient cyclique, les couleurs continuent de fluctuer au-delà des deux // ou alors un seul paramètre new Point2D.Double(180, 200),
points indiqués. Sinon (si la valeur est false), le gradient dessine un mélange unique – Color.cyan ));
d'un point à l'autre. – surface.fill(cercle);
– Au-delà de chaque point extrême, la couleur est unie. }
– }

Graphisme 2D et les images: remplissage Graphisme 2D et les images: remplissage


Textures
– Une texture est une image continuellement répétée, comme un carrelage, jusqu'à remplir On peut créer un objet BufferedImage en fournissant la taille de l'image et son typeImage.
la forme. Les types d’images indiquent comment les couleurs des pixels sont codées.
Le typeImage le plus répandu est BufferedImage.TYPE_INT_ARGB,
– Ce concept est représenté dans l'API 2D par la classe TexturePaint. – dans lequel chaque pixel est spécifié par un entier décrivant les valeurs de
Pour créer une texture, il suffit de choisir (les champs du constructeur de la classe rouge, de vert, de bleu et de transparence (ou alpha)
TexturePaint) – Sachant que BufferedImage correspond à un tableau rectangulaire de pixels.
– l'image à utiliser – BufferedImage image = new BufferedImage(largeur, hauteur,
– le rectangle qui servira à la reproduire. BufferedImage.TYPE_INT_ARGB);
– Pour construire un objet TexturePaint, il faut spécifier
une BufferedImage et un rectangle d'origine: Ensuite, on obtient un contexte graphique associé à l’image « image » permettant de
dessiner dans notre image :
– Graphics2D zoneImage = image.createGraphics( );
– Le rectangle est un Rectangle2D qui indique la taille de l’image et la position
de départ du motif (point de commencement de calquage du motif sur le
rectangle). On peut alors tracer dans zoneImage.
– Ce rectangle est prolongé indéfiniment sur les deux axes (x et y) pour remplir – C.a.d
entièrement le plan. Toutes les opérations de dessin dans zoneImage ont maintenant pour effet de
remplir l'image intermédiaire avec des pixels (exemple d’opération: setPaint, fill
L'image est agrandie ou rétrécie pour remplir exactement le rectangle d'origine, ou autres opérations).
puis reproduite dans tous les autres rectangles. On peut créer alors notre objet TexturePaint :
Donc, on trace l'image dans le rectangle en fonction de la taille du rectangle – surface.setPaint(new TexturePaint (image, rectangle));
Graphisme 2D et les images: remplissage Graphisme 2D et les images: remplissage
Remarque
Exemple avec fabrication complète de la texture. (ok)
– class Zone extends JComponent { – Dans cet exemple, on prend un rectangle dont la dimension correspond à la taille de
protected void paintComponent(Graphics g) {
l'image.
– Graphics2D surface = (Graphics2D) g; Rien n'empêche de choisir des dimensions du rectangle qui soit un multiple de la
– CubicCurve2D petale = new CubicCurve2D.Double(150, 170, 10, 10, 290, 10, 150, 170); taille de l'image,
– BufferedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); – par exemple, deux fois la largeur sur trois fois la hauteur (ce qui permet
– Graphics2D zoneImage = image.createGraphics( ); d’agrandir l’image).
– zoneImage.setPaint(Color.red); – Il est possible également de récupérer une texture depuis un fichier image sur le disque
– zoneImage.draw(new Ellipse2D.Double(0, 0, 10, 20)); // Moitié d’une ellipse à cause taille image dur.
– Rectangle2D rectangle = new Rectangle2D.Double(0, 0, image.getWidth(), image.getHeight()); La classe ImageIO est spécialement conçue pour cela et simplifie la lecture d'un
– surface.setPaint(new TexturePaint(image, rectangle)); fichier graphique dans une image intermédiaire.
– surface.fill(petale); – BufferedImage image = ImageIO.read(new File("fichier image.gif"));
– Ellipse2D ellipse = new Ellipse2D.Double(120, 140, 60, 60);
– surface.setStroke(new BasicStroke(30));
Exemple « texture avec une image »
– surface.draw(ellipse);
– CubicCurve2D petale = new CubicCurve2D.Double(150, 170, 10, 10, 290, 10, 150,
– surface.setPaint(new GradientPaint( 120, 140,
170);
– Color.blue, 180, 200, Color.cyan ));
– surface.fill(ellipse); – BufferedImage image_2 = ImageIO.read(new File("C:/Images/papillon.gif"));
} – Rectangle2D rectangle2 = new Rectangle2D.Double(0, 0, 0.5 * image_2.getWidth(),
– } 0.5 * image_2.getHeight( ) );
– surface.setPaint(new TexturePaint(image_2, rectangle2));
– surface.fill(petale);

Graphisme 2D et les images: tracé de texte Graphisme 2D et les images: tracé de texte
Changement de police
Tout comme le tracé du contour d'une forme, celui du texte n'est qu'une variante du remplissage – Il est possible d'écrire un texte avec une police différente que celle prévue par défaut.
d'une forme.
Les polices de caractères, appelées aussi fontes, sont représentées par des objets de la
classe java.awt.Font.
Lorsqu’on demande à un Graphics2D de dessiner un texte, – Un objet Font est construit à partir d'un nom de police, un identificateur de style et d'une
– il détermine les formes nécessaires au dessin et les remplit. taille.
Les formes représentant des caractères sont baptisées Glyphes. Font police = new Font( nom de police, style, taille );
Une police est une collection (un ensemble) de glyphes. – Il existe trois sortes de noms de polices :
les familles, les caractères (également baptisés noms de polices) et les noms
Pour dessiner un texte, on fait appel à la méthode drawString( ) de Graphics2D qui existait déjà logiques.
dans la classe mère Graphics, – Les noms de famille et les noms de police sont étroitement liés.
– Elle est redéfinie afin de permettre les différents traitements Par exemple, "Garamond Italic" est une police de la famille "Garamond".
remplissage, tranformations, clipping, etc.... Police logique
– surface.drawString("Bienvenue ! ", 50, 150)); – Un nom logique est un nom générique attribué à la famille.
– Lors de l’appel de drawString( ), – Les noms des polices logiques ci-dessous sont en général disponibles sur toutes les
Graphics2D utilise la police courante pour récupérer les glyphes correspondant aux plateformes :
caractères de la chaîne. Serif (nom générique de TimesRoman) ;
– Puis les glyphes (qui ne sont pas des Shape) sont remplis à l'aide du Paint en SansSerif (nom générique de Helvetica) ;
cours. Monospaced (nom générique de Courier) ;
Graphics2D.drawString(texte à afficher, x, y)); Dialog ;
DialogInput.
Graphisme 2D et les images: tracé de texte Graphisme 2D et les images: tracé de texte

Paramètres de la classe Font Exemple


– Pour écrire des caractères d'une fonte, on doit: – class Zone extends JComponent {
créer un objet de la classe Font, protected void paintComponent(Graphics g) {
spécifier la chaîne de caractère correspondant au nom de la fonte, – Graphics2D surface = (Graphics2D) g;
donner le style de la fonte ainsi que sa taille. – surface.setPaint(new GradientPaint( 10, 10, Color.red, 30, 30, Color.yellow,
– Font verdana = new Font("Verdana", Font.BOLD, 14); true ));
– surface.setFont(new Font("SansSerif", Font.ITALIC+Font.BOLD, 48));
– Le nom logique de fonte peut être employé dans le constructeur de Font. – surface.drawString("Bienvenue !", 10, 50);
}
– Il faut ensuite spécifier le style (normal, gras, italique ou gras italique) dans le second – }
paramètre du constructeur, en lui donnant une des valeurs suivantes :
Font.PLAIN : normal,
Font.BOLD : gras,
Font.ITALIC : italique,
Font.BOLD+Font.ITALIC : gras et italique.

Graphisme 2D et les images: tracé de texte Graphisme 2D et les images: tracé de texte
Taille d'une police de caractères
Changement d'un seul paramètre de la police par défaut
– Afin de centrer une chaîne de caractère dans la fenêtre au lieu de l'écrire à une
– Parfois, plutôt que de créer une nouvelle police, il peut être judicieux de changer un seul des
paramètres de la police déjà utilisée. position arbitraire,
– C'est le cas notamment pour changer uniquement le style ou la taille de la police par défaut (ou une On doit connaître la largeur et la hauteur de la chaîne en pixels.
autre déjà existante). – Ces dimensions dépendent de trois facteurs :
– Pour cela, utilisez la méthode deriveFont( ) de la classe Font. La police ou la fonte utilisée.
– Cette méthode est surchargée pour permettre le changement,
La chaîne (ici "Bienvenue").
soit du style de la fonte (le paramètre est de type int),
L'unité sur laquelle la chaîne est écrite (ici, l'écran de l'ordinateur).
soit la taille de la fonte (le paramètre est de type float) :
– fonte.deriveFont(Font.BOLD); // change la style de la police
– fonte.deriveFont(48F); // change la taille de la police – Pour obtenir un objet qui représente les caractéristiques de la fonte utilisée à l'écran,
– Remarque On appelle la méthode getFontRenderContext( ) de la classe Graphics2D.
si on veut changer le nom de la police alors on doit créer une nouvelle fonte.
Exemple (**) Elle renvoie un objet de la classe FontRenderContext.
– class Zone extends JComponent {
– Ce qui permet de récupérer le rectangle qui englobe la chaîne au moyen de la
protected void paintComponent(Graphics g) {
méthode getStringBounds( ) de la classe Font :
– Graphics2D surface = (Graphics2D) g;
– surface.setPaint(new GradientPaint( 10, 10, Color.red, 30, 30, Color.yellow, true ));
– surface.setFont(this.getFont().deriveFont(48F)); FontRenderContext contexte = surface.getFontRenderContext();
// surface.setFont(new Font("SansSerif", Font.ITALIC+Font.BOLD, 48)); Surface est le contexte graphique dans lequel on dessine le texte
– surface.drawString("Bienvenue !", 10, 50); contexte : le contexte d'affichage de police « il gère les métriques du
} texte »
– } Rectangle2D rectangle = fonte.getStringBounds("Bienvenue !", contexte);
Graphisme 2D et les images: tracé de texte Graphisme 2D et les images: tracé de texte

Pour expliquer les dimensions de ce rectangle, On doit connaître certains termes de la


typographie «Manière dont un texte est imprimé» Pour interpréter les dimensions de ce rectangle, il est utile de connaître la position de certaines
constantes
– La ligne de base (baseline) :
est la ligne imaginaire sur laquelle s'aligne la base des caractères comme "e".

– Le jambage ascendant (ascent) :


représente la distance entre la ligne de base et la partie supérieure d'une lettre "longue
du haut", comme "b" ou "k" ou un caractère majuscule.

– Le jambage descendant (descent) :


représente la distance entre la ligne de base et la partie inférieure d'une lettre "longue
du bas", comme "p" ou "g".

– L'interligne (leading) :
est l'intervalle entre la partie inférieure d'une ligne et la partie supérieure de la ligne
suivante.

– La hauteur (height) :
est la distance verticale entre deux lignes de base successives et équivaut à (jambage
descendant + interligne + jambage ascendant).

Graphisme 2D et les images: tracé de texte Graphisme 2D et les images: tracé de texte
Exemple qui centre le texte "Bienvenue!" dans le sens de la hauteur et dans le sens de la largeur
– class Zone extends JComponent {
Remarque
protected void paintComponent(Graphics g) {
– La largeur (width) du rectangle que renvoie la méthode getStringBounds() est l'étendue – Graphics2D surface = (Graphics2D) g;
horizontale de la chaîne. – surface.setPaint(new GradientPaint(10, 10, Color.red, 30, 30, Color.yellow, true));
– String message = "Bienvenue !";
– La hauteur du rectangle est la somme des jambages ascendant, descendant et de – Font police = getFont().deriveFont(48F);
l'interligne. – surface.setFont(police);
– FontRenderContext contexte = surface.getFontRenderContext();
– L'origine du rectangle se trouve à la ligne de base de la chaîne. – Rectangle2D rectangle = police.getStringBounds(message, contexte);
// coin supérieur gauche du texte
– double x = (this.getWidth( ) - rectangle.getWidth( )) / 2;
– La coordonnée y supérieure du rectangle est négative.
– double y = (this.getHeight( ) - rectangle.getHeight( )) / 2;
// ajouter jambage ascendant à y pour atteindre la ligne de base
On peut obtenir les valeurs de largeur, de hauteur et des jambages ascendant d'une chaîne de la – double ascendant = - rectangle.getY( );
façon suivante : – surface.drawString(message, (int) x, (int) (y+ascendant));
– FontRenderContext contexte = surface.getFontRenderContext(); – // surface.drawString(message, (float) x, (float) (y+ascendant));
– Rectangle2D rectangle = fonte.getStringBounds("Bienvenue !", contexte); } // ainsi j‘affiche à la base du rectangle englobant
– double largeur = rectangle.getWidth( ); – }
– double hauteur = rectangle.getHeight( );
Remarque:
– double ascendant = - rectangle.getY( ); – Y + ascendant: car l’écriture de l’image du String doit commencer à la ligne principale du rectangle
englobant, donc on doit descendre de y par la valeur de ascendant qui est négative.
Graphisme 2D et les images: tracé de texte Diagramme UML récapitulant les différentes classes utilisées

Remarque
– Si on veut connaitre le jambage descendant ou l'interligne, on doit appeler la méthode
getLineMetrics() de la classe Font.
– Cette fonction renvoie un objet de la classe LineMetrics,
possédant les méthodes permettant d'obtenir les dimensions intrinsèques d'une police
de caractères, comme :
– getAscent( ) : jambage ascendant.
– getLeading( ) : interligne.
– getDescent( ) : jambage descendant.
– getHeight( ) : hauteur.
– Exemple
FontRenderContext contexte = surface.getFontRenderContext();
LineMetrics dimensions = fonte.getLineMetrics("Bienvenue !", contexte);
float ascendant = dimensions.getAscent();
float hauteur = dimensions.getHeight();
float descendant = dimensions.getDescent();
float interligne = dimensions.getLeading();

Graphisme 2D et les images: dessin d'images Graphisme 2D et les images: dessin d'images
Les images sont traitées un peu différemment des formes.
– En particulier, on ne fait pas appel au Paint courant pour dessiner une image car celle-ci Remarque
contient ses propres informations de couleurs. – La classe java.awt.Image représente une vue d'une image.
– La vue est créée à partir d'une image source fournissant des données sous la forme de
Comme pour le texte, la classe Graphics2D dispose de la méthode spécifique drawImage( ) qui pixels.
permet de placer l'image à l'emplacement indiqué. – Les images peuvent provenir
d'une source statique, comme des fichiers GIF, JPAG, PNG
– Cette méthode a pour premier argument l'objet de type Image qui représente l'image réelle d'une source dynamique comme un stream d'animation ou un moteur graphique.
récupérée à partir du disque dur. – La classe Image de Java_2D gère également l'animation GIF89a (gifs animés), de sorte
On sait que la classe ImageIO permet de récupérer un tel fichier. qu'il est aussi facile de travailler avec des animations simples qu'avec des images statiques.
– Image image = ImageIO.read(new File("fichier image.gif"));
– surface.drawImage(image, 50, 150, null));
Aussi, la méthode read( ) de la classe ImageIO peut récupérer une image à partir
d'une URL :
– Image image = ImageIO.read(new URL("http://site/répertoire/fichier_image.gif"));;
– surface.drawImage(image, 50, 150, null));
Remarque
– La variable image contient une référence à un objet qui encapsule les données images.
On peut afficher l'image grâce à la méthode drawImage( ) de la classe Graphics.
– surface.drawImage(image, positionX, positionY, spectateur));
Graphisme 2D et les images: dessin d'images Graphisme 2D et les images: dessin d'images

Exemple d’affichage d’une image à partir du coin supérieur gauche Exemple d’affichage d’une image en tenant compte de la dimension de l'image afin qu'elle soit
toujours centrée par rapport à l’écran:
– class Zone extends JPanel{
– class Zone extends JComponent {
protected void paintComponent(Graphics g) {
protected void paintComponent(Graphics g) {
– Graphics2D surface = (Graphics2D) g;
– Graphics2D surface = (Graphics2D) g;
– try {
– try {
Image image = ImageIO.read(new File("chouette.jpg"));
Image image = ImageIO.read(new File("chouette.jpg"));
surface.drawImage(image, 0, 0, null);
double positionX = (this.getWidth()-image.getWidth(null)) / 2;
– }
double positionY = (this.getHeight()-image.getHeight(null)) / 2;
– catch (IOException e) {
surface.drawImage(image, (int)positionX, (int)positionY, null);
surface.drawString("Image inexistante", 10, 10);
– }
– }
– catch (IOException e) {
}
surface.drawString("Image inexistante", 10, 10);
– }
– }
}
– }

Graphisme 2D et les images: dessin d'images Graphisme 2D et les images: dessin d'images..
Imposer une taille à l'image Spectateurs
– La méthode drawImage( ) permet de changer l'échelle d'une image. – Problèmes:
Cette version de la fonction drawImage( ) utilise deux arguments supplémentaires Lorsque Java récupère une image, comment savoir si elle est entièrement chargée ?
pour désigner Que se passe-t-il si nous avons besoin de connaître les propriétés de l'image (comme ses
– la largeur et la hauteur voulues quelle que soit les dimensions originales de dimensions) avant de pouvoir commencer à travailler avec?
l'image. Que se passe-t-il si une erreur se produit lors du chargement de l'image ?
– Ces problèmes sont traités par les spectateurs:
surface.drawImage(image, positionX, positionY, largeur, hauteur,
spectateur)); se sont des objets des classes qui implémentent l'interface ImageObserver.
– Toutes les opérations qui dessinent ou traitent des objets Image reviennent immédiatement,
Cette méthode permet donc de réaliser un reéchantillonage : mais prennent un objet spectateur en paramètre.
– Exemple qui adapte la taille de l'image à la dimension de la fenêtre (Attention, le ratio ??? – Les objets ImageObserver surveillent l'état de l'image et mettent ces informations à la
n'est pas respecté: ratio = rapport de la hauteur et de largeur de l’image) : disposition du reste de l'application.
class Zone extends JComponent { – Lorsque les données de l'image sont chargées, un spectateur est informé de la progression.
– protected void paintComponent(Graphics g) { – Le spectateur est averti de la disponibilité de nouveaux pixels, de l'achèvement d'une zone
de l'image et de la survenue d'une erreur en cours de chargement.
Graphics2D surface = (Graphics2D) g;
– Le spectateur reçoit dès que possible certains attributs sur l'image tels que la dimensions et
try { d'autres propriétés.
Image image = ImageIO.read(new File("chouette.jpg")); – Remarque
surface.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), La méthode drawImage() prend une référence sur un objet ImageObserver en paramètre.
null); Cette méthode renvoie une valeur booléenne qui indique si l'image a été (ou pas) affichée
} entièrement.
catch (IOException e) { surface.drawString("Image inexistante", 10, 10); } – Si l'image n'a pas encore été téléchargée ou si elle n'est qu'en partie disponible, la méthode
drawImage() ne dessine qu'une fraction de l'image et se termine.
– }
}
Graphisme 2D et les images: dessin d'images Graphisme 2D et les images: dessin d'images
Remarque**
Remarque
– Le spectateur peut faire ce qu'il veut des informations qu’il reçoit. – Dans l’exemple, notre composant Zone est utilisé comme spectateur et appelle la méthode
Le plus souvent, il appelle la méthode repaint( ) (qui fait appelle à paintComponent()) pour ordonner au
repaint( ) pour redessiner l'image si nécessaire.
composant graphique contenant l'image de dessiner la zone de l'image fraîchement arrivée. Si l'image arrive lentement, le composant Zone est averti régulièrement à l'arrivée de
– Il existe des spectateurs préfabriqués. Il se trouve précisément dans tous les composants graphiques, comme nouvelles données.
JComponent. L'image est donc affichée progressivement.
JComponent implémente l'interface ImageObserver et fournit une fonction de rafraîchissement simple. – Les propriétés awt.image.incrementaldraw et awt.image.redrawrate contrôle le
Cela signifie que tout composant graphique peut être utilisé comme son propre spectateur. comportement de l’affichage progresseive.
Exemple **: en faisant passer une référence à notre propre objet graphique à l'aide de this.
redrawrate limite le nombre d'appels à la méthode repaint( ) : la valeur par défaut est
– class Zone extends JComponent {
une fois toutes les 100 millisecondes.
protected void paintComponent(Graphics g) {
– incrementaldraw est la valeur par défaut.
– Graphics2D surface = (Graphics2D) g;
– try { – Elle est initialisée à true.
Image image = ImageIO.read(new File("chouette.jpg")); – Il suffit de la positionner à false pour différer le tracé de l'image.
double positionX = (this.getWidth() - image.getWidth(this)) / 2; – Dans les exemples précédant, le spectateur = null, puisque l'image est petite et de plus
double positionY = (this.getHeight() - image.getHeight(this)) / 2; elle est présente sur le disque dur.
surface.drawImage(image, (int)positionX, (int)positionY, this); L'interface ImageObserver déclare une unique méthode imageUpdate
– } Il est possible de redéfinir la méthode imageUpdate pour suivre le chargement de l'image. Cette
– catch (IOException e) { méthode reçoit plusieurs paramètres dont le premier est l'image et le second infoflags est un
– surface.drawString("Image inexistante", 10, 10); entier qui indique (par certains bits) quelles informations sont maintenant disponibles.
– } – public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height) {
} System.out.println("imageUpdate() : x = " + x + ", y = " + y + ", width = " + width + ", height = " + height);
– // Affichage de l'image lorsque l'image est totalement chargée
– }
– if ((flags & ALLBITS) != 0) repaint(); return true; }

Graphisme 2D et les images: dessin d'images Graphisme 2D et les images: dessin d'images
Toutes les méthodes drawImage sont données par:
Remarque** – public boolean drawImage(Image img, int x, int y, ImageObserver spectateur)
– System.setProperty("awt.image.incrementalDraw", "true"); Affiche l'image à l'endroit indiqué
– boolean incrementalDraw = Boolean.getBoolean ("awt.image.incrementalDraw"); – public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver
– Long redrawRate = Long.getLong ("awt.image.redrawrate"); spectateur)
– if (incrementalDraw) { Les parties transparentes sont rendues dans la couleur de fond «bgcolor ».
if (redrawRate != null) { – public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver
– long tm = redrawRate.longValue( ); spectateur)
– if (tm < 0) tm = 0; L’image est déformée pour remplir le rectangle donné.
– repaint (tm); – public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor,
ImageObserver spectateur)
}
L’image est déformée pour remplir le rectangle donné. Variante avec couleur de fond.
else repaint (100);
– public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int
– }
sx2, int sy2, ImageObserver spectateur)
La partie de l'image délimitée par le rectangle donné par les points s1 et s2 est
affichée dans le rectangle donné par les points d1 et d2. Une déformation peut avoir
lieu.
– public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int
sx2, int sy2, Color bgcolor, ImageObserver spectateur)
La partie de l'image délimitée par le rectangle donné par les points s1 et s2 est
affichée dans le rectangle donné par les points d1 et d2. Une déformation peut avoir
lieu. Variante avec couleur de fond.
Graphisme 2D et les images: dessin d'images Graphisme 2D et les images: Transformation de coordonnées
La classe Image permet de faire quelques traitements élémentaires sur les images en voici les On sait que quatre parties du pipeline d'affichage affectent chaque opération graphique.
principaux : – En particulier, tout le rendu est transformé, composé et masqué.
– int getHeight(ImageObserver ) retourne la hauteur de l’image en pixels. Supposons qu’on doit dessiner un objet, par exemple une voiture.
– int getWidth(ImageObserver ) retourne la largeur de l’image en pixels. – On connait, grâce aux indications du constructeur, la hauteur, l'empattement et la longueur
– Image getScaledInstance(int, int, int) retourne une copie redimensionnée de l’image. totale de cette voiture.
les deux premiers paramètres précisent la hauteur et la largeur désirées pour la copie. – On peut naturellement déterminer la position de tous les pixels, en choisissant une échelle
Si l’un d’entre eux vaut –1 il sera adapté pour conserver l’aspect initial de l’image. en nombre de pixels par mètre.
Le dernier paramètre indique l’algorithme à utiliser pour créer cette copie. Il peut – Cependant, il existe une technique plus simple, on peut demander au contexte graphique
prendre les valeurs : d'effectuer une conversion à notre place « changement d’échelle »:
– Image.SCALE_DEFAULT surface.scale(pixels par mètre, pixels par mètre);
algorithme standard surface.draw(new Rectangle2D.Double(coordonnées et dimensions en mètres)));
– Image.SCALE_FAST La méthode scale() (étirement) de la classe Graphics2D définit la transformation de
algorithme rapide coordonnées du contexte graphique et choisit une transformation d'échelle.
– Image.SCALE_SMOOTH – Cette transformation permet de passer de coordonnées de l'utilisateur (c'est-à-dire les
algorithme qui préserve la qualité de l’image unités spécifiées par l'utilisateur) à des coordonnées machine (c'est-à-dire des pixels).
– Image.SCALE_REPLICATE
algorithme simple qui se contente de dupliquer ou d’enlever des pixels
– Image.SCALE_AVERAGING
algorithme qui utilise une méthode basée sur la moyenne entre pixels
voisins.

Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées
Types de transformation pour les transformations affines
Remarque – Il existe quatre transformations fondamentales pour les transformations de coordonnées:
– Les transformations de coordonnées sont très utiles dans la pratique. Changement d'échelle - scale( ): Réduit ou augmente toutes les distances à un point
Elles permettent de travailler avec des valeurs de coordonnées plus simple et plus spécifié (on fait c’est un étirement ou homothéties).
significatives. Rotation - rotate( ) : Tourne tous les points autour d'un point fixe.
Elles correspondent à notre logique métier, avec celles dont on a l'habitude de Translation - translate( ): Déplace tous les points d'une quantité donnée.
travailler.
Déformation linéaire - shear( ) : Une ligne reste fixe et toutes les lignes parallèles sont
Le contexte graphique s'occupe ensuite de les transformer en pixels ( à notre place). décalées d'une quantité proportionnelle à la distance entre cette ligne et la ligne fixe.
(cisaillement).
– Remarque
Les méthodes scale( ), rotate( ), translate( ) et shear( ) de la classe Graphics2D
choisissent
– une transformation de coordonnées pour le contexte graphique, en fonction de
l'une de ces quatre transformations fondamentales (avec possibilité de
concaténation d’une transformation avec la transformation courante).
Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées
En général une transformation affine est donnée par le système:
Le cisaillement ( shear( ) ) est une transformation qui fait penser à un étirement selon un axe.
– X’ = a X + b Y + e
Le cisaillement selon l'axe des x ne modifie pas la coordonnée en y
– Y’ = d X + c Y + f
tandis que le cisaillement selon l'axe des y ne modifie par la coordonnée en x.
Types de transformation
On peut également combiner ces deux cisaillements
– avec un Graphics2D, on peut effectuer:
On a
des rotations:
– Cisaillement selon les abscisses : x' = x + shx * y; y' = y
– rotate(double theta)
– Cisaillement selon les ordonnées : x' = x; y' = y + shy * x
theta = angle en radians dans le sens des aiguilles d'une montre
des homothéties:
– scale(double sx, double sy)
x' = x + shx . y sx et sy = coefficients de l'homothétie
y' = y
des cisaillements: (étirement suivant l’axe x et/ou axe y)
– shear(double shx, double shy)
– shx et shy = coefficients du cisaillement
Les translations: il existe 2 méthodes documentées un peu différemment:
– translate(int x, int y)
le point (x, y) est la nouvelle origine du repère
– translate(double tx, double ty)
tx et ty sont les longueurs des déplacements horizontaux et verticaux
– Remarque: En pratique, ça revient au même

Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées

Rotation autour du point d'origine:

Rectification: la matrice dans la translation est donnée par 1 0 Tx


0 1 Ty

Transformation.shear(shx, shy); où 0 0 1

– shx et shy sont les coefficients de cisaillement


– Les nouveaux points (a, b) sont transformés des anciens points (x, y) avec la
transformation suivante
a = x + (shx)*y;
b = y + (shy)*x
– Remarque: On a un changement de l’orientation du repère utilisé (un étirement
suivant l’axe X et/ ou l’axe Y, les repère ne sont plus orthogonaux et pas de
respect des surfaces).
Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées

Cas d’une homethésie: Cas d’une translation


– coefficients de l'homothétie:
1 = identité
>1 = agrandissement
entre 0 et 1 = rétrécissement
< 0 pour un paramètre, pas de changement.

Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées

Cisaillement = déformation du repère (les axes ne sont plus orthogonaux)


Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées
Transformations temporaires - description de la classe AffineTransform (voir classe testeImage_4)
Composer les transformations – Pour créer une transformation affine (correspondant à l’identité)
– Il est tout à fait possible de composer ces transformations. AffineTransform transforme = new AffineTransform( );
Par exemple, on peut faire tourner les formes et doubler leur taille. Il faut alors passer – Si on veut appliquer une transformation de manière temporaire (par rapport au contexte
à la fois par une rotation et un changement d'échelle : graphique), il convient de récupérer l'ancienne transformation, de la composer avec la nouvelle
– surface.rotate(angle); transformation et de restaurer l'ancienne transformation lorsque on a fini.
– surface.scale(2, 2);
– Pour stocker une transformation temporaire, on doit passer par la classe AffineTransform qui
– surface.draw(...);
représente n'importe quel type de transformation.
– Remarque
La classe AffineTransform: permet de créer et de composer des transformations affines
Dans ce cas, l'ordre des transformations n'a aucune importance (à cause de l’égalité
de l’homothétie par rapport aux axes X et Y).
– Ensuite, pour récupérer une transformation affine existante associée au contexte graphique,
Cependant, avec d'autres transformations, l'ordre des compositions peut avoir de utilisez la méthode getTransform() de la classe Graphics2D.
l'importance.
– Par exemple, si on souhaite appliquer une rotation et une déformation, on doit – Enfin, pour redonner l'ancienne transformation, on prend la méthode setTransform() de
savoir quelle transformation appliquer en premier. Graphics2D. Exemple:
Le contexte graphique appliquera ces transformations dans l'ordre inverse AffineTransform ancienneTransformation = surface.getTransform();
dans on les lui avait fournit – // enregistre l'ancienne transformation
On peut fournir autant de transformations que l’on désire. surface.rotate(angle, x, y);
On peut faire une opération de rotation autour d'un autre point que l'origine : – // application d’une transformation temporaire concate à ancienneTransformation
– surface.rotate(angle, x, y); surface.setTransform(ancienneTransformation);
– // restitue l'ancienne transformation

Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées

Remarque
– Les méthodes: getRotateInstance( ), geScaletInstance( ), getTranlateInstance( ) et Exemple de synthèse
getShearInstance( ), de la classe AffineTransform, construisent des objets de – Cet exemple regroupe: le tracé de formes quelconques, de texte et d'image
transformation affine relatif aux transformations requises.
Ceci permet de montrer que les transformations s'appliquent quelque soit le type de
AffineTransform Transformation =AffineTransform.getTranslateInstance(10, 250); tracé.
– On commence par le code sans aucune transformation:
– Une fois qu'un objet AffineTransform est construit,

il est possible de rajouter d'autres transformations à l'aide de ses méthodes internes :


rotate( ), scale( ), translate( ), et shear(): c’est la composition des transformations.
– Transformation.rotate(Math.PI/2);
– Transformation.shear(shx, shy);
– Les méthodes scale, rotate, translate et shear font de la concaténation
La dernière transformation citée est la première appliquée.
On peut aussi l’affecter au contexte graphique
– surface.setTransform(Transformation);
On peut aussi, créer des formes Transformées via la transformation affine sans touché
au contexte graphique.
– C.a.d: les Formes utilisées subissent les caractéristiques de la transformation
avant de les dessiner via le contexte graphique.(voir exemple du slide 374)
Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées
class Zone extends JComponent {
– protected void paintComponent(Graphics g) {
Dans l’exemple suivant, on applique des transformations différentes sur chacun des tracés :
Graphics2D surface = (Graphics2D) g;
try { // tracé de l'image
– Pour le tracé de l'image, on utilise une inclinaison vers la droite.
– Image image = ImageIO.read(new File("chouette.jpg")); Pour cela, on utilise la méthode shear( ).
– surface.drawImage(image, 10, 10, this.getWidth(), this.getHeight(), null); – On va faire un étirement para rapport à l’axe x
// tracé du pétale de fleur
– CubicCurve2D pétale = new CubicCurve2D.Double(150, 170, 10, 10, 290, 10, 150, 170); – Pour le tracé des formes quelconques, on utilise une rotation du pétale de -45°.
– surface.setPaint(Color.BLUE); Attention, il faut faire une rotation par rapport au centre du cercle.
– surface.fill(pétale);
Par ailleurs, l'angle s'exprime en radian.
– Ellipse2D cercle = new Ellipse2D.Double(120, 140, 60, 60);
– surface.setStroke(new BasicStroke(10));
De plus, le sens de rotation correspond au sens horaire.
– surface.draw(cercle); – Pour cela, on utilise la méthode rotate( ) avec les trois paramètres.
– surface.setPaint(new Color(255, 128, 0));
– surface.fill(cercle); – Pour le tracé du texte, on utilise une inclinaison à la fois en x et en y.
// tracé du message de bienvenue On réutilise de nouveau la méthode shear() où cette fois-ci on règle, à la fois
– surface.setPaint(new GradientPaint(10, 10, – la valeur de transformation dans l'axe des x
– Color.red, 30, 30, Color.yellow, true));
– et la valeur de transformation dans l'axe des y.
– String message = "Bienvenue !";
– Font police = this.getFont().deriveFont(48F);
– Puis une translation du vecteur (-20, -20)
– surface.setFont(police);
– surface.drawString(message, 20, 80);
} catch (IOException e) { surface.drawString("Image inexistante", 10, 10); } } }

Graphisme 2D et les images: Transformation de coordonnées Graphisme 2D et les images: Transformation de coordonnées
class Zone extends JComponent {
– protected void paintComponent(Graphics g) { Utilisation d’un objet AffineTransfom pour le dessin d’une forme: (voir classe TesteImage _8)
Graphics2D surface = (Graphics2D) g; – AffineTransform at = new AffineTransform( );
AffineTransform transformation = surface.getTransform(); // transformation initiale= identité
try {
– at.translate(150,200);
surface.shear(-0.5, 0); – at.rotate(-Math.PI/3);
surface.translate(70, 0);
– at.scale(2, 0.5);
Image image = ImageIO.read(new File("chouette.jpg"));
surface.drawImage(image, 10, 10, getWidth( ) / 2, getHeight( ) / 2, null); – Ellipse2D.Float cercle = new Ellipse2D.Float(-50, -50, 100, 100);
surface.setTransform(transformation); // recup trans formation_initiale
– Shape shape = at.createTransformedShape(cercle); // création d’une forme via AffineTransform
// tracé du pétale de fleur
surface.rotate(-Math.PI/4, 150, 170); – surface.draw(shape);
CubicCurve2D pétale = new CubicCurve2D.Double(150, 170, 10, 10, 290, 10, 150, 170); Remarque:
surface.setPaint(Color.BLUE);
surface.fill(pétale); – Le dessin du cercle passe par un scale, une rotate et enfin par une translate.
Ellipse2D cercle = new Ellipse2D.Double(120, 140, 60, 60); – Ceci est fait via l’tilisation d’un objet de type AffineTransform sans toucher à la
surface.setStroke(new BasicStroke(10));
transformation affine par défaut associée au contexte graphique
surface.draw(cercle);
surface.setPaint(new Color(255, 128, 0));
surface.fill(cercle); surface.setTransform(transformation);
// tracé du message de bienvenue
surface.shear(0.5, 0.4);
surface.translate(-20, - 20);
surface.setPaint(new GradientPaint(10, 10, Color.red, 30, 30, Color.yellow, true));
String message = "Bienvenue !";
Font police = this.getFont().deriveFont(48F);
surface.setFont(police);
surface.drawString(message, 20, 80); }
catch (IOException e) { surface.drawString("Image inexistante", 10, 10); } } }
Graphisme 2D et les images: clipping Graphisme 2D et les images: clipping
En définissant une forme de clipping dans le contexte graphique, on restreint toutes les
opérations graphiques à l'intérieur de cette forme. Exemple
Le clipping = limiter l'affichage à une région définie par une forme géométrique // créer un arc de type corde de 240°
– Cela correspond à la mise en œuvre d'un masque dans les logiciels de traitement d'images. – Arc2D.Double arc = new Arc2D.Double(0, 0, getWidth(), getHeight(), 0, 240, Arc2D.CHORD);
surface.setClip(forme du masque); // ************ définir la zone de dessin de clippage.
surface.draw(forme quelconque); – surface.setClip(arc);
– Cela permet d’afficher uniquement la partie de la forme qui se trouve à – Image image = ImageIO.read(new File("Images/lapin3.jpg"));
l'intérieur du masque. – surface.drawImage(image, 0, 0, getWidth( )/2, getHeight( )/2, this);
// ************ créer un rectangle
Dans la pratique, on n'appelle pas explicitement la méthode setClip( ) puisqu'elle remplace le – Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, getWidth( ) / 2, getHeight()/2);
masque que le contexte graphique possède éventuellement.
// **** modifier la zone de dessin = intersection de l’ancienne avec le rectangle
– Par exemple,
– surface.clip(rect);
un contexte graphique d'impression contient un masque sous forme de rectangle qui
– Image image2 = ImageIO.read(new File("Images/papillon.jpg"));
empêche d'imprimer sur les marges de la feuille.
– surface.drawImage(image2, 0, 0, getWidth()/4, getHeight()/2, this);
Il vaut mieux, à la place, appeler la méthode clip( ) du contexte graphique
– Dans ce cas là, c'est juste la forme qui est passée en paramètre qui sert de masque
temporaire.
surface.clip(forme du masque);
– La méthode clip() réalise l'intersection du masque existant avec la nouvelle
forme de masque qu’on passe en paramètre.

Graphisme 2D et les images: clipping Graphisme 2D et les images: clipping via un Texte
Fabriquer un masque (ou un clipping) à partir d'un texte
– On va utiliser un exemple qui utilise un texte comme masque.
– Puis, on va afficher une image au travers de ce masque.

– Ceci consiste à utiliser un gestionnaire de disposition particulier qui permet de placer des
éléments quelconques (des formes, des images ..) dans la surface délimité par un texte.
Ce gestionnaire est représenté par la classe TextLayout.

– Pour créer un objet correspondant à ce gestionnaire de disposition TextLayout, on doit


fournir :
La chaîne de caractère.
La police utilisée.
Le contexte d'affichage de police « de type FontRenderContext ».
– TextLayout calque = new TextLayout("Notre texte" , police, contexte);
– Remarque *
Ce layout ou cet objet, de mise en page texte, enregistre la disposition d'une séquence
de caractères avec toutes leurs caractéristiques, en utilisant le contexte d'affichage de
police particulier qui est «FontRenderContext ».
La mise en page ou la présentation des caractères dépend du contexte d'affichage de
police « il gère les métriques du texte » :
– les mêmes caractères apparaîtrons différemment sur un écran ou sur une
imprimante.
Graphisme 2D et les images: clipping Graphisme 2D et les images: clipping

Remarque Remarque
– la classe TextLayout (package java.awt.font) enregistre la représentation graphique d'un – La classe TextLayout possède une méthode importante:
texte avec ses différents attributs (police, style, ...). ** getOutLine( )
– qui retourne un objet Shape qui décrit la forme du contour des caractères,
– Cette classe utilise un objet de type ** utilisés dans le texte , dans la mise en page de texte retenu.
FontRenderContext qui gère les métriques du texte (les dimensions d’un texte) dans – Cette méthode prend comme paramètre un objet de type AffineTransform.
le contexte graphique donné. « elle représente les caractéristiques de fonte à l'écran »
La forme du contour commence à l'origine (0, 0) qui va correspondre à la ligne de
– Les dimensions d'un texte ( c.à.d.: le rectangle englobant) peuvent être récupéré en base (donc la partie au-dessus de la ligne de base ne sera pas visible), ce qui n'est pas
invoquant la méthode getBounds( ) sur l'objet de type TextLayout. très pratique pour la plupart des opérations de dessin.
– Aussi, on peut utiliser via la classe TextLayout, les méthode suivantes: – Par conséquent,
getAdvance: longueur du texte, il faut fournir une transformation affine comme paramètre, au moyen de la
classe AffineTransform, pour la méthode getOutLine( ), afin de spécifier
getAscent: hauteur de lettre de base à haut total,
la nouvelle position du contour.
getDescent: base à queue des lettres,
getLeading : interligne
On prendra ce contour comme zone de clliping afin d’afficher une image à travers ce
masque

Graphisme 2D et les images: clipping Graphisme 2D et les images: Transparence et composition


Exemple Fin
– class Zone extends JComponent {
protected void paintComponent(Graphics g) { Avec le modèle de couleurs standard RVB, chaque couleur est décrite par ses composantes de
– Graphics2D surface = (Graphics2D) g; rouge, de vert et de bleu.
// mise en oeuvre du masque
– AffineTransform transformation = AffineTransform.getTranslateInstance(10, 170);
Cependant, il peut être également intéressant de décrire des zones de l'image qui sont
– transformation.scale(1, 3);
transparentes, ou partiellement transparentes.
– Font police = new Font("SansSerif", Font.ITALIC+Font.BOLD, 60);
– FontRenderContext contexte = surface.getFontRenderContext();
– TextLayout calque = new TextLayout("Bonjour !", police, contexte); Lorsqu’on superpose une image et un dessin existant,
– Shape masque = calque.getOutline(transformation); – les pixels transparents ne modifient pas les pixels existants,
– surface.clip(masque); – alors que des pixels partiellement transparents sont mélangés avec des pixels existants.
– try {
// tracer une image dans le masque
L'exemple suivant montre le résultat d'une superposition d'un texte partiellement transparent sur
Image image = ImageIO.read(new File("chouette.jpg")); une image.
surface.drawImage(image, 0, 0, null);
– Les détails de l'image restent visibles sous le texte
– } catch (IOException e) {
surface.drawString("Image inexistante", 10, 10);
– }
}
– }
Graphisme 2D et les images: Transparence et composition Graphisme 2D et les images: Transparence et composition

class Zone extends JComponent { Remarque


– protected void paintComponent(Graphics g) { – Avec L'API Java 2D, la transparence est décrite par une couche alpha qui spécifie le degré
Graphics2D surface = (Graphics2D) g; de transparence
try { Chaque pixel possède, en plus de ses composantes de rouge, de vert et de bleu, une
– // tracer l'image de fond valeur alpha variant entre 0 - transparence parfaite - et 255 - opacité (dans le cas où
les paramètres sont de type int).
Image image = ImageIO.read(new File("chouette.jpg"));
Pour cela, il suffit d'utiliser le constructeur de la classe Color qui intègre la couche
surface.drawImage(image, 0, 0, null); alpha :
} catch (IOException e) { – Color(int rouge, int vert, int bleu, int alpha);
– surface.drawString("Image inexistante", 10, 10); // valeur des paramètres varie de 0 à 255
} – En reprenant l'exemple, le réglage de la couleur du texte se fait de la façon suivante :
– // tracé du texte en transparence sur l'image surface.setPaint(new Color(0, 0, 255, 64));
surface.setPaint(new Color(0, 0, 255, 64));
surface.setFont(new Font("SansSerif", Font.ITALIC+Font.BOLD, 60)); – Il existe un deuxième constructeur, dont le fonctionnement est plus intuitif, qui prend cette
surface.drawString("Chouette", 10, 130); fois-ci des floats en paramètres. Dans ce cas là, il faut spécifier la valeur de chacune des
– } couches par un pourcentage :
} Color(float rouge, float vert, float bleu, float alpha);
– // valeur des paramètres varie de 0F à 1F
– En reprenant l'exemple, le réglage de la couleur du texte en intégrant plutôt la notion de
pourcentage est la suivante :
surface.setPaint(new Color(0F, 0F, 1F, 0.25F))

Graphisme 2D et les images: Transparence et composition Graphisme 2D et les images: Transparence et composition
Règles de composition pour la transparence
– Lorsqu’on superpose deux formes alors Mise en oeuvre de la composition
on mélange ou on compose les couleurs et les valeurs alpha des pixels sources et destinations. – La méthode setComposite() de la classe Graphics2D sert à installer un objet d'une classe
– Il existe douze règles de composition possibles pour ce traitement. qui implémente l'interface Composite.
Elles permettent de spécifier une préférence plus marquée soit sur la source ou soit sur la L'API Java2D fournit une classe de ce type, AlphaComposite, qui implémente les
destination (voir tableau) : règles (les douze règles précédentes).
– Remarque: œufs
– La méthode getInstance( ) de la classe AlphaComposite fournit un objet AlphaComposite.
Si les règles sont obscures et mystérieuses, il suffit de choisir la règle SRC_OVER.
On doit fournir la règle et la valeur alpha à utiliser pour les pixels source :
– Il s'agit de la règle par défaut pour les objets Graphics2D et c'est celle qui fournit les
résultats les plus intuitifs. – int règle = AlphaComposite.SRC_OVER;
CLEAR La source efface la destination. – float alpha = 0.25F ;
SRC La source remplace la destination et les pixels vides.
– surface.setComposite(AlphaComposite.getInstance(règle, alpha));
DST La source n'affecte pas la destination. – surface.setPaint(Color.blue);
SRC_OVER La source est mélangée à la destination et remplace les pixels vides. – surface.drawString("Chouette", 10, 130);
DST_OVER La source n'affecte pas la destination et remplace les pixels vides. – Voici quelques exemples de l'exemple précédent suivant la règle de composition :
SRC_IN La source remplace la destination. AlphaComposite.CLEAR
SRC_OUT La source efface la destination et remplace les pixels vides. AlphaComposite.SRC_OUT
DST_IN L'alpha de la source modifie la destination. AlphaComposite.DST
DST_OUT Le complément de l'alpha de la source modifie la destination. AlphaComposite.DST_OVER
SRC_ATOP La source se mélange à la destination. AlphaComposite.DST_IN
AlphaComposite.DST_OUT
DST_ATOP L'alpha de la source modifie la destination. La source remplace les pixels vides. AlphaComposite.DST_ATOP
XOR Le complément de l'alpha de la source modifie la destination. La source remplace les pixels vides. AlphaComposite.XOR
Graphisme 2D et les images: Transparence et composition Graphisme 2D et les images: Conseil d'affichage

AlphaComposite.SRC On a l'API Java 2D est en général très rapide,


AlphaComposite.SRC_IN – il se peut qu’on désire dans certain cas un contrôle plus précis sur les compromis entre
la vitesse d'affichage et la qualité d'affichage.
AlphaComposite.SRC_OVER
AlphaComposite.SRC_ATOP La méthode setRenderingHint( ) de la classe Graphics2D permet d’améliorer la qualité du
rendu en utilisant un seul conseil via une clé et une valeur.
– Les clés et les valeurs des conseils sont déclarées dans la classe RenderingHints.
surface.setRenderingHint(clé, valeur);

Graphisme 2D et les images: Conseil d'affichage Graphisme 2D et les images: Conseil d'affichage
Clé Valeurs Explications
VALUE_ANTIALIAS_ON,
Active ou désactive l'anticrénelage des
KEY_ANTIALIASING VALUE_ANTIALIAS_OFF,
formes. L'anticrénelage : anti-aliasing
VALUE_ANTIALIAS_DEFAULT
Lorsque c'est possible, sélectionne des – L'aspect le plus intéressant est la gestion de l'anticrénelage (en anglais : anti-aliasing). Il
VALUE_RENDER_QUALITY,
KEY_RENDERING VALUE_RENDER_SPEED,
algorithmes d'affichage pour un meilleur s'agit d'une technique permettant de réduire l'effet d'escalier des lignes inclinées et des
résultat en termes de qualités ou de
VALUE_RENDER_DEFAULT courbes.
vitesse.

VALUE_DITHER_ENABLE,
Active ou désactive la diffusion (dithering) – Dans la première image, on constate que
des couleurs. La diffusion simule un nombre
KEY_DITHERING VALUE_DITHER_DISABLE, les formes et les textes sont affichés avec un effet de marches d'escalier . Cet effet est
plus important de couleurs en créant des
VALUE_DITHER_DEFAULT
motifs de couleurs proches. très peu esthétique
VALUE_TEXT_ANTIALIAS_ON,
KEY_TEXT_ANTIALIASING VALUE_TEXT_ANTIALIAS_OFF,
Active ou désactive l'anticrénelage des – Dans la deuxième image
polices.
VALUE_TEXT_ANTIALIAS_DEFAULT
Le résultat est plus précis, l’image et le texte sont plus lisses aux bords qu’avant.
Active ou désactive le calcul des dimensions
VALUE_FRACTIONALMETRICS_ON, fractionnelles des caractères. Les
KEY_FRACTIONALMETRICS VALUE_FRACTIONALMETRICS_OFF, dimensions fractionnelles des caractères
VALUE_FRACTIONALMETRICS_DEFAULT permettent de les positionner plus
précisément.

VALUE_ALPHA_INTERPOLATION_QUALITY,
Active ou désactive le calcul précis des
KEY_ALPHA_INTERPOLATION VALUE_ALPHA_INTERPOLATION_SPEED,
composantes alpha.
VALUE_ALPHA_INTERPOLATION_DEFAULT

VALUE_COLOR_RENDER_QUALITY,
Choisit la qualité ou la vitesse pour
KEY_COLOR_RENDERING VALUE_COLOR_RENDER_SPEED,
l'affichage des couleurs.
VALUE_COLOR_RENDER_DEFAULT
VALUE_INTERPOLATION_NEAREST_NEIGHB
OR, Sélectionne une règle pour interpoler les
KEY_INTERPOLATION
VALUE_INTERPOLATION_BILINEAR, pixels lorsque les images sont déformées.
VALUE_INTERPOLATION_BICUBIC Sans anticrénelage Avec anticrénelage
VALUE_STROKE_NORMALYZE,
Sélectionne une règle pour la combinaison
KEY_STROKE_CONTROL VALUE_STROKE_PURE,
des traits.
VALUE_STROKE_DEFAULT
Graphisme 2D et les images: Conseil d'affichage Graphisme 2D et les images: Conseil d'affichage
Exemple de programme sur l'anticrénelage global
Remarque
– class Zone extends JComponent {
– Voici comment demander un filtre anticrénelage pour les formes :
protected void paintComponent(Graphics g) {
surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); – Graphics2D surface = (Graphics2D) g;
– Il peut également être utile d'appliquer un anticrénelage aux polices de caractères // règles de rendu
uniquement : – surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
surface.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints.VALUE_TEXT_ANTIALIAS_ON); // dessin du pétale
– CubicCurve2D pétale = new CubicCurve2D.Double(150, 130, 10, -20, 290, -
20, 150, 130);
– surface.setStroke(new BasicStroke(5));
– surface.draw(pétale);
// dessin du texte
– surface.setFont(new Font("SansSerif", Font.BOLD+Font.ITALIC, 60));
– surface.drawString("Bonjour !", 10, 190);
}
– }

Les Threads: Définition et propriétés Les Threads: Définition et propriétés


Remarque:
– Un thread est en quelque sorte un processus à l’intérieur d’un processus.
Définition – On peut avoir plusieurs threads au sein d’un même processus.
– Un programme est multitâche quand il lance (ou peut lancer) l’exécution de plusieurs
parties de son code en même temps (en parallèle). – Les ressources allouées à un processus (temps processeur, des variables d’environnement,
des données, la mémoire, espace d’adressage, etc…) sont partagées entre tous les threads
qui le composent.
Le multitâche s’appuie sur les processus ou les threads (fil d’exécution)
– Un processus (dans une application java) possède au moins un thread qui exécute le
programme principal habituellement contenu dans la fonction main ( ).
Définition
– Un thread (appelé aussi processus léger ou activité ou tâche) est un fil d'instructions (un
– Contrairement aux processus, les threads partagent la même zone mémoire (espace
chemin d’exécution) à l'intérieur d'un processus.
d’adressage), ce qui rend très facile mais aussi périlleux, car plein de risque d’erreur, la
communication entre threads.
Les threads Java sont des processus légers : – Par contre les threads ont leur propre
– ils partagent un ensemble de codes, de données et de ressources au sein d'un processus compteur ordinal ou pointeur d’instructions (registre qui contient l'adresse mémoire
lourd qui les héberge (ce processus est la JVM). de l'instruction en cours d'exécution ), leur propre pile.

– L’utilisation des threads et le détail de leur comportement est différent dans chaque
système d’exploitation (Windows NT, Unix)
– Certains langages, comme Java, C# ou Ada, définissent leur propre mécanisme de threads,
afin de permettre l’utilisation facile et portable des threads sur tous les systèmes.
Les Threads Les Threads
Intérêts d'une application multi-threads :
En java, il est possible de simuler l'exécution de plusieurs programmes en même temps: – gérer l'exécution de traitements longs sans bloquer les autres
– c'est le multithreading. exemples :
– calculs, affichage et gestion des interactions dans une interface graphique
Les programmes qui utilisent plusieurs threads sont dits multithreadés. – lancer des programmes démons qui tournent en tâche de fond
Le calcul de la multiplication de 2 matrice (m, p) et (p, n) peut être
Le multithreading est intégré au langage Java. effectuée en parallèle par m*n threads
– La création de threads est très simple : Dans une interface de traitement de texte,
Thread t = new Thread ( ); // créer un nouveau thread un thread s'occupe de ce que tape l'utilisateur,
t. start ( ); // lancer ou activer le thread un autre est en charge de souligner les erreurs,
– En créant un nouvel objet Thread, on crée un fil d'exécution qui possède sa propre pile. un 3ème se charge de faire parler le trombone
– lancer plusieurs exécutions du même code simultanément sur différentes données
exemples :
– répondre à plusieurs requêtes en même temps dans un serveur,
– traiter deux flux sonores en parallèle.
– exploiter la structure multi-processeurs des ordinateurs modernes ?

Inconvénients d'une application multi-threads :


– il faut gérer les problèmes de synchonisation entre threads
– une telle application est difficile à écrire et à débogger

Les Threads: Syntaxe Les Threads: Syntaxe


Exemple 1: ok
Syntaxe – class DeuxThreadAsynchrones {
– Les threads peuvent être créés comme instance d'une classe dérivée de la classe Thread. public static void main(String args[ ]) {
Cette classe est appelée classe threadée – new UnThread("Thread _1").start( );
– Les threads sont lancés par la méthode start( ), – new UnThread("Thread_2").start( );
Cette méthode demande à l'ordonnanceur de thread (l'ordonnanceur de la machine }
virtuelle Java) de lancer en concurrence la méthode run( ) du thread. – }
– La méthode run() doit être implantée dans le programme. – class UnThread extends Thread {
public UnThread(String str) {
Une classe "threadée" : C'est une classe qui implémente un thread. – super(str);
}
– Syntaxiquement, on la construit, ou bien comme : public void run( ) {
Une classe dérivée de la classe Thread. Par exemple – for (int i = 0; i < 10; i++) {
– class MaClasseThread extends Thread { ••• } System.out.println(i+" "+getName());
Une implémentation de l'interface Runnable. try {sleep( (int) (Math.random()*100) ) ; }
– class MaClasseThread implements Runnable { ••• } catch (InterruptedException e){// ….. }
– }
}
System.out.println(getName( ) + " est fini. ");
– }
Les Threads: Syntaxe Les Threads: Syntaxe
Une exécution
Exemple 2: – % java DeuxThreadAsynchrones
– class DeuxThreadAsynchrones { – 0 le thread 1
public static void main(String args[ ]) { – 0 le second thread
– new Thread(new ThreadRunnable ("la thread 1")).start( ); – 1 le thread 1
– new Thread(new ThreadRunnable ("le second thread")).start( ); – 2 le thread 1
– 1 le second thread
– /*Si la méthode start() est appelée alors que le thread est déjà en cours
d'exécution, une exception de type IllegalThreadStateException est levée.*/ – 3 le thread 1
– 4 le thread 1
}
– 5 le thread 1
– } – 6 le thread 1
– class ThreadRunnable implements Runnable { – 7 le thread 1
public UnThread(String str) { this.str = str; } – 2 le second thread
public void run( ) { – 3 le second thread
– for (int i = 0; i < 10; i++) { – 4 le second thread
System.out.println(i+" "+str); – 8 le thread 1
– 5 le second thread
try {sleep( (int) (Math.random()*100) ) ; }
– 9 le thread 1
catch (InterruptedException e){// ….. } – 6 le second thread
– } – le thread 1 est fini
– System.out.println(getName( ) + " est fini. "); – 7 l second thread
} – 8 l second thread
– } – 9 le second thread
– le second thread est fini

Les Threads: Syntaxe Les Threads: Constructeur


Il y a plusieurs constructeurs associés à un objet de type Thread.
Autre Exemple: – Quand on écrit
– courbe qui vibre indéfiniment (Exemple2) t1 = new MaClasseThread( );
– le code lancé par t1.start(); est le code run() de la classe threadée
MaClasseThread.
– Si on écrit :
t1 = new Thread(monRunnable);
– le code lancé par t1.start(); est le code run( ) de l'objet référencé par
monRunnable.
– monRunnable est un objet d’une classe implémentant l’interface Runnable
Runnable monRunnable = new MonRunnable ( );
– L'interface Runnable ne définit qu'une seule méthode : la méthode run( ).
Exemple :
– class Appli extends Thread {
public static void main(String args[ ]) {
– Thread t1 = new Appli();
– t1.start( );
}
public void run() { // ce code est lancé }
– }
Les Threads: Constructeur Les Threads: Cycle de vie

Un thread est dans l’état initial tant que la méthode start n’a pas été appelée sur lui.
Constructeurs de la classe Thread
– Thread( )
Crée un nouveau thread

– Thread(Runnable target)
Crée un nouveau Thread relatif à l'objet implémentant l'interface Runnable

– Thread(Runnable target, String name)


Crée un nouveau Thread relatif à l'objet implémentant l'interface Runnable avec un
nom
L’appel à la méthode start lance l'exécution d'un thread, donc on arrive dans l’état “en marche”
– Thread(String name) où le fonctionnement du thread est alors soumis à l’ordonnanceur de la machine virtuelle Java.
Crée un nouveau Thread avec un nom
Une première façon de quitter l’état “en marche” est la terminaison de la méthode run qui
achève également l’instance d’exécution ou soit à l'exécution de stop( ).
Un thread à l'état mort ne peut plus redémarrer.

Les Threads: Cycle de vie Les Threads: Quelques méthodes

currentThread( )
L’appel à la méthode start lance l'exécution d'un thread, donc on arrive dans l’état “en marche” – Donne le thread actuellement en cours d'exécution
où le fonctionnement du thread est alors soumis à l’ordonnanceur de la machine virtuelle Java. Thread.currentThread() renvoie la référence au thread courant c'est-à-dire celui qui
exécute currentThread( ).La méthode
Une première façon de quitter l’état “en marche” est la terminaison de la méthode run qui setName( )
achève également l’instance d’exécution ou soit à l'exécution de stop( ). – Fixe le nom du thread
Un thread à l'état mort ne peut plus redémarrer. getName( )
– Nom du thread
Il existe en revanche deux méthodes pour suspendre temporairement le fonctionnement d’un isAlive( )
thread : – Indique si le thread est actif (true) ou non (false)
l’appel à la méthode sleep où l’exécution du thread est suspendue pour un temps donné. start( )
l’appel à la méthode wait permet d’attendre certains évènements. – Lance l'exécution d'un thread
run( )
L’appel à la méthode interrupt( ) permet d‘interrompre ou de tuer proprement un thread ceci en – Méthode exécutée automatiquement après que la méthode start() précédente ait été
déclenchant l'exception InterruptedException dans l'objet auquel on applique la méthode. exécutée
sleep(n)
la méthode interrupt( ) sur un thread n’est détectée que si dans la méthode run du thread – Arrête l'exécution d'un thread pendant n millisecondes (throws InterruptedException )
un appel à interrupted( ) est effectué dans le bloc du catch de l’exception – Remarque
InterruptedException pour voir si il y a une interruption.
Si cette méthode est appelée dans du code synchronisé (synchronized) le thread ne
perd pas le moniteur (voir plus loin).
Les Threads: Quelques méthodes Les Threads: Changement d’état
join( )
– Opération bloquante - attend la fin du thread pour passer à l'instruction suivante Un thread passe d'un état exécutable à un état non exécutable (état bloquant) sous l'invocation
– T.join( ) : de l'une de ces méthodes :
bloque le thread courant (qui n'est pas le thread T ), jusqu'à ce que le thread T soit Thread.sleep(long durée), qui suspend le thread durant quelques instants,
terminé. (exemple identification de tous les joueurs avant commencement d’un jeu) – où
throws InterruptedException les méthodes wait de la classe java.lang.Object (utiliser dans la synchronisation)
yield( )
– Provoque une pause de l'exécution du thread et autorise les autres threads à s'exécuter Le passage à un état exécutable à nouveau, à partir d'un état non exécutable se fait soit :
ceux qui ont même priorité ou de priorité supérieure. – après l'écoulement du temps indiqué dans la méthode sleep
– soit après la réception d'une notification d'un autre thread via l’une des méthodes:
interrupt( ) notify( ) ou notifyAll( ).
– permet au thread A de demander l’interruption d’un thread B.
– permet d'interrompre un thread en cours d'exécution d'une méthode
sleep(), join() ou wait()
– Elle provoque la levée de l’exception InterruptedException suite à des appels d’opération
bloquante ( wait( ), join( ) ou .sleep( )).

boolean isInterrupted(): renvoi l’état d’interruption d’un thread;


static boolean interrupted(): renvoi l’état d’interruption d’un thread.
Remarque
– La documentation java dit que l’effet de yield n’est pas garantie.

Les Threads: Démarrage, suspension, reprise et arrêt d'un thread Les Threads: Démarrage, suspension, reprise et arrêt d'un thread

Pour gérer l'exécution des threads, on dispose de différentes méthodes dont celles ci-dessous. – public void stop() :
– public void start( ) : cette dernière méthode permet de stopper, de manière définitive, l'exécution du
cette méthode permet de démarrer un thread. thread.
Remarque, Cette méthode est dépréciée:
– si on invoque la méthode run (au lieu de start), – lorsqu'un thread est tué, il n'est pas possible de savoir ce qu'il était en train de
le code s'exécute bien, mais aucun nouveau thread n'est lancé dans le faire, il est donc possible qu'il soit arrêté au milieu d'une modification d'un
système. objet;
– Inversement, la méthode start, lance un nouveau thread dans le système dont le cet objet est donc laissé dans un état incohérent.
code à exécuter démarre par le run. – Remarque:
Des problèmes similaires peuvent se produire avec les méthodes suspend() et
– public void suspend( ) : resume( ) de plus elles sont sources de nombreux deadlock.
cette méthode permet d'arrêter temporairement un thread en cours d'exécution
(dépréciée). Une solution pour gérer proprement le stop d’un thread, consiste à
– Sortir par un teste dans la boucle de la méthode run.
– public void resume( ) : – Via la levée d’une exception InterrupedException en utilisant la méthode interrupt().
celle-ci permet de relancer l'exécution d'un thread, au préalable mis en pause via
suspend.
Remarque:
– Attention, le thread ne reprend pas au début du run, mais continue bien là où il
s'était arrêté.
Les Threads: Démarrage, suspension, reprise et arrêt d'un thread Les Threads: Démarrage, suspension, reprise et arrêt d'un thread
Exemple (utilisation d’une variable volatile partagée par plusieurs threads)
Remarque: – public class TesteRunnable implements Runnable {
– On peut simuler stop en utilisant une variable. protected volatile int x, j;
public TesteRunnable(int x, int j) { this.x = x; this.j = j; }
Pour rendre l’arrêt d’un thread T, on peut utiliser une variable arretThread visible public void run( ) {
depuis T et les threads qui peuvent stopper T: – for (int i = 0; i < 4; i++) {
– T initialise arretThread à false lorsqu’il démarre. System.out.println("avant sleep " + x++ +", j= "+ j++);
– Pour stopper T, un autre thread met arretThread à true try { Thread.sleep(500);
} catch (InterruptedException e) { System.out.println(e); }
– T inspecte à intervalles réguliers la valeur de arretThread et s'arrête quand
System.out.println("après sleep " + x +", j= "+ j);
arretThread à la valeur true.
– }
}
De plus, arretThread doit être déclarée volatile si elle n’est pas accédée dans du code – }
synchronisé. – public static void main(String args[]) {
– Donc, tous les threads partagent une zone mémoire commune pour ranger la TesteRunnable r = new TesteRunnable(0, 0);
valeur de la variable arretThread (concurrence d’accée arretThread ) Thread tr1 = new Thread(r), tr2 = new Thread(r);
– De plus, si une variable de type long et double est déclarée volatile, sa lecture tr1.start(); tr2.start();
et son écriture est garantie atomique (voir slide 424). – }
Le mot-clé volatile posé sur un paramètre, permet de garantir qu’une valeur écrite ou modifiée
par un thread sera lue correctement par un autre thread. Remarque
Volatile : permet d'assurer un accès ordonné à un champ par deux threads différents – Le mot-clé volatile est relativement peu utilisé et toutes les JVM ne le prennent pas en compte.
Volatile est utilisé pour des types primitifs java seulement. – Le plus souvent, les champs volatiles on les utilises pour terminer ou arrêter un thread

Les Threads: Démarrage, suspension, reprise et arrêt d'un thread Les Threads: Démarrage, suspension, reprise et arrêt d'un thread
Exemple (arrêt d’un Thread utilisant une variable volatile): ok
Exemple (arrêt d’un Thread utilisant un boolean dans la boucle du run)
class ExampleThread extends Thread {
class MonThread extends Thread {
– private volatile int testValue;
– protected volatile boolean running = true;
– public ExampleThread(int testValue) { this.testValue = testValue; }
– public Monthread () {
public void run() {
// Mon constructeur
– for (int i = 0; i < 10; i++) {
– }
if (testValue > 25) { return; }
– public arret( ) { // Méthode 2
testValue += i * i;
running = false;
System.out.println("boucle numero " + i + " avec testValue = " +
– }
testValue);
– public void run() {
try {Thread.sleep(500);
// Initialisation
} catch (InterruptedException exception) { }
while(running == true) { // Boucle infinie pour effectuer des traitements.
– }
– // Traitement à faire
}
– if (/*Condition d'arret*/) // Méthode 1
– public static void main(String args[]) {
– running = false;
ExampleThread ve = new ExampleThread(0);
}
new Thread(ve).start();
– }
new Thread(ve).start();
}
– }
}
Les Threads: Démarrage, suspension, reprise et arrêt d'un thread Les Threads: Démarrage, suspension, reprise et arrêt d'un thread
Exemple (arrêt d’un Thread utilisant la méthode interrupt( )) Exemple (arrêt d’un Thread utilisant la méthode interrupt( ))
– public class ThreadInter extends Thread{ Ou bien; dans le cas
public void run() { – D’attente du retour d'une opération bloquante (wait) dans un état d'attente (sleep), Dans
– try{ ce cas, on peut interrompre le thread
System.out.println("running...");
for(long l = 0; l < 30000000; l++); – public class MonThread extends Thread {
Thread.sleep(1000L); public void run() {
System.out.println("faire..."); – try{
– }catch(InterruptedException e) { while ( ! Thread.currentThread().isInterrupted( ) ) {
System.err.println(e); // autre code…..
– } } catch (InterruptedException e) {
} /* nous avons été interrompu , on remet interrupted à false par
static public void main(String [] args) throws Exception{ l'appel à cette méthode */
– Thread t = new ThreadInter(); Thread.currentThread().interrupted() ; }
– t.start(); – }
– t.interrupt( ); }
– System.err.println("interrupted!"); public void cancel() {
} – // interruption du thread courant, c'est-à-dire le nôtre
– } – Thread.currentThread().interrupt() ;
}
– }

Les Threads: Gestion de la priorité d'un thread. Les Threads: Gestion de la priorité d'un thread.
Ou Ordonnancement des threads Ou Ordonnancement des threads

On peut, en Java, agir sur la priorité des threads:


– A tout thread est associé une priorité. Remarque
Sur une durée déterminée, un thread ayant une priorité plus haute recevra plus fréquemment – L'ordonnancement est dépendant des plateformes:
le processeur qu'un autre thread. Il exécutera donc, globalement, plus de code. Sous Windows 95(JDK1.1.4), et MacOS (JDK 1.0.2), un tourniquet pour les threads
de même priorité est installé i.e. une thread peut être interrompu par l'ordonnanceur
La priorité d'un thread varie entre 0 et 10. en faveur d’un autre thread de même priorité.
– Mais attention, il n'est en aucun cas garanti que le système hôte saura gérer autant de Par contre, sous Solaris on a pas ce type de gestion des threads.
niveaux de priorités.
Des constantes existent et permettent d'accéder à certains niveaux de priorités : – La sémantique de la méthode yield( ) n’est pas définie, certaines plate-formes peuvent
– MIN_PRIORITY (0) l’ignorer.
– NORM_PRIORITY (5: par défaut)
– MAX_PRIORITY (10). – A priorité égale, la spécification du langage n'impose rien
pas de contrainte d'équité (concernant le temps d’exécution).
La fonction setPriority(int p): permet de changer le niveau de priorité. après un notify( ) c'est l'une des threads en attente dans l'état éligible qui est choisie.

Remarque:
– Il n'est pas interdit à la machine virtuelle Java d'augmenter la priorité d'un thread trop
longtemps oublié.
Il est donc fortement conseillé de ne pas utiliser la priorité comme ingrédient de
solutions à des problèmes de synchronisation
Les Threads: Gestion de la priorité d'un thread. Les Threads: Gestion de la priorité d'un thread.
Ou Ordonnancement des threads Ou Ordonnancement des threads
Exemple Résultat
– On lance trois threads. Chacun d'eux avec une priorité différente. Au bout d'un certain
temps, le thread initial (celui du main) stoppe les trois autres et l'on regarde le nombre
d'incrémentations réalisé par chacun d'entre eux.
public class ThreadPriority extends Thread { Thread 1 Thread 2 Thread 3
– int counter = 0; Thread.NORM_PRIORITY Thread.NORM_PRIORITY Thread.NORM_PRIORITY
– public void run() {while(true) counter++; // incrémente indéfiniment un compteur}
250 752 409 256 697 243 251 964 807
– public static void main(String args[]) throws Exception {
Thread.MIN_PRIORITY Thread.NORM_PRIORITY Thread.MAX_PRIORITY
ThreadPriority thread1 = new ThreadPriority(), thread2 = new ThreadPriority(),
thread3 = new ThreadPriority();
11 104 663 20 673 290 1 164 460 398
thread1.setPriority(Thread.MAX_PRIORITY);
thread2.setPriority(Thread.NORM_PRIORITY);
thread3.setPriority(Thread.MIN_PRIORITY);
thread1.start(); thread2.start(); thread3.start();
Thread.sleep(5000);
thread1.stop(); thread2.stop(); thread3.stop();
System.out.println("Thread 1 : counter == " + thread1. counter);
System.out.println("Thread 2 : counter == " + thread2. counter);
System.out.println("Thread 3 : counter == " + thread3. counter); }
}

Les Threads: Gestion d'un groupe de threads Les Threads: Gestion d'un groupe de threads

Une autre possibilité intéressante consiste à regrouper différents threads. Remarque


– Dans un tel cas, on peut invoquer un ordre sur l'ensemble des threads du groupe, ce qui – Par défaut un thread appartient au groupe “system”
peut dans certains cas sérieusement simplifier le code.
– Permet de grouper des threads pour les traiter globalement et simultanément
– Si on ajoute un thread à un groupe, on ne pourra en aucun cas le changer vers un autre
gestion des priorités groupe.
interruption
– Permet de regrouper ensembles des threads qui travaillent ensemble – Une fois tous les threads attachés à un groupe, on peut alors invoquer les méthodes de
contrôle d'exécution des threads sur l'objet de groupe.
Pour inscrire un thread dans un groupe, il faut que le groupe soit initialement créé. Les noms des méthodes sont identiques à la classe Thread: suspend(), resume(), stop()
– Pour ce faire,
il faut instancier un objet de la classe ThreadGroup. – On obtient le groupe d’un thread par l’appel à la méthode getThreadGroup().
Un fois le groupe créé, on peut attacher des threads à ce groupe, via un constructeur
de la classe Thread. – Un groupe de threads peut contenir autre groupe de Threads
– public Thread(ThreadGroup group, String name); ou
– public Thread(Runnable target, String name)
Les Threads: Gestion d'un groupe de threads Les Threads: Gestion d'un groupe de threads
public class Palindrome extends Thread {
Exemple d’utilisation – public static void main (String[] args) {
– On suppose que via un navigateur on télécharge des images. ThreadGroup tg = new ThreadGroup("Groupe");
– Où chaque image se télécharge par l'intermédiaire d'un thread, new Th1(tg).start();
Tous les threads font partie d'un groupe, new Th2(tg).start();
– nous disposons d'un moyen efficace de les stopper tous par un simple clique sur – }
un bouton arrêt. }
– Pour construire un groupe de threads, on utilise le constructeur suivant : class Th1 extends Thread {
ThreadGroup g = new ThreadGroup("WebImages0047654"); – private static volatile boolean arret = false;
– Pour ajouter des threads à ce groupe : – Th1(ThreadGroup tg) {
Thread th = new Thread (g, "Image1"); super(tg, "Th1"); // un objet thread de nom "Th1" de la classe Th1 est ajouté au groupe tg
– Pour interrompre tous les threads d'un groupe, il suffit d'appeler la méthode interrupt du – }
groupe :
– public void run() {
g.interrupt( );
int i = 0;
while (!arret) {
Remarque
System.out.println(i++);
– Un groupe peut posséder des groupes enfants. Le fait d'interrompre les threads du groupe
}
parent, interromps également les membres des groupes enfants.
– }
– public static void arrete() { arret = true; }
}

Les Threads: Gestion d'un groupe de threads Synchronisation de threads et accès aux ressources partagées.
class Th2 extends Thread {

Dans de nombreux cas,


– Th2(ThreadGroup tg) { super(tg, "Th2"); }
– les threads partagent les mêmes objets et par conséquent,
la modification de ces objets par un thread pourrait avoir
– public void run() {
– des conséquences fâcheuses pour les autres threads notamment si ceux-ci
String s1 = ""; modifient un objet en même temps.
BufferedReader r = new BufferedReader( new InputStreamReader(System.in)); – Donc, il faut synchroniser les accès concurrents aux objets partagés.
while (!s1.equals("s")) {
– try {
while (s1 == "") { s1 = r.readLine(); }
if (!s1.equals("s")) { s1 = ""; }
– } catch (IOException e) {}
}
Th1.arrete();
– }
}

Remarque: (gestion globale d’un groupe de threads)


– On détruit th2, en sortant de la fonction run, ceci entraine la destruction de tous les objets
de type th1 car le champ arret est static.
Synchronisation de threads et accès aux ressources partagées Synchronisation de threads et accès aux ressources partagées
La synchronisation
Section critique
– Lorsque 2 threads ou plus ont besoin d’une même ressource au même moment, il y a
– C’est une partie du code (Méthode, bloc de code) qui ne peut être exécutée en même temps
besoin de s’assurer que la ressource ne sera utilisée que par une thread en même temps:
par plusieurs threads sans risquer de provoquer des anomalies de fonctionnement.
on utilise alors un procédé de synchronisation.
– Une partie du code qui ne doit jamais être exécuté simultanément par plusieurs threads.
– Un moyen d’obtenir une synchronisation sur les sections ou les ressources critiques:
les moniteurs.
Une ressource critique
– C’est une ressource qui ne doit être accédée que par un seul thread à la fois.
– Un moniteur est un verrou qui permet l'accès en exclusion mutuelle aux sections critiques
Exemples: variable globale, périphérique de l'ordinateur.
du code
– Un moniteur est un verrou qui ne laisse qu'un seul thread à la fois accéder à la ressource
Exemple 1
– Si obj.x = 2, le code obj.x = obj.x + 1; // instruction dans la méthode run
– Chaque objet java et chaque classe java possède un moniteur.
exécuté par 2 threads T1 et T2 (de même type), peut donner en fin d’exécution 3 ou 4
Chaque fois qu'un objet est crée, on crée également un moniteur qui lui est associé
suivant l’ordre d’exécution :
– À un moment donné, un seul thread peut posséder le moniteur d’un objet
– 1. T1 : lit la valeur de x (2); puis le système lui hôte le cpu avant de faire x+1
Si d’autres threads veulent acquérir le même moniteur, ils sont mis en attente, en
– 2. T2 : lit la valeur de x (2)
attendant que le premier thread rende le moniteur
– 3. T1 : calcul de x + 1 (3) // évaluation de cette expression
– 4. T2 : calcul de x + 1 (3) // T2 récupère la main de T1
– Lorsqu’un thread acquiert un verrou, on dit qu’elle entre dans le moniteur.
– 5. T1 : range la valeur calculée dans x (3)
Les autres threads sont dites en attente du moniteur
– 6. T2 : range la valeur calculée dans x (3)
– Analogie avec des cabines d’essayage de vêtements
– x contient 3 au lieu de 4 !

Synchronisation de threads et accès aux ressources partagées Synchronisation de threads et accès aux ressources partagées
– Méthodes synchronisées:
Lorsqu'un thread rentre dans la section critique, il acquiert le moniteur et si d'autres Threads
tentent de rentrer dans cette section critique, ils se mettent en attente jusqu'à ce que le thread On peut déclarer qu'une méthode est en section critique sur le moniteur this ou en
détenteur du moniteur le libère utilisant le mot clé synchronized devant la déclaration de la méthode, les deux
syntaxes ci-dessous sont équivalentes
– synchronized void methode( ) { //section critique}
Deux moyens de synchronisation: les méthodes synchronisées et les blocs synchronisés
– void methode() { synchronized(this) { //section critique} }
– Bloc synchronisé sur un objet (un objet quelconque)
Remarque
En Java, tout objet peut jouer le rôle de moniteur.
– objetXX. nomMethode(…) : on synchronise le message methode( ) sur l’objet objetXX

On pose un moniteur sur un bloc de code à l'aide du mot clé synchronized


– un thread n'accède à la section critique que si le moniteur est disponible
Afin de Contrôler l’accès à l’objet synchronisé (l’accès à l’objet passé en
paramètre de synchronized(Object) est réservé à un unique thread.)
– un thread qui entre en section critique bloque l'accès au moniteur
– Synchronized ( objetMonitor ) { ...
// Instructions de manipulation d'une ressource partagée. – un thread qui sort de section critique libère l'accès au moniteur
//zone de code protégée sur l'objet: objetMonitor
//contenant certaine appel de méthode de l’objet objetMonitor – sleep ne fait pas perdre le moniteur (contrairement à wait)
/ /code en section critique
– } – le moniteur ne doit pas être modifié dans la section critique! Si le verrou change, la
synchronisation n'est plus garantie.
synchronized(maListe) { maListe = new ArrayList<String>(); }
Synchronisation de threads et accès aux ressources partagées Synchronisation de threads et accès aux ressources partagées
Exemple 2: Dans l’exemple 2:
– accès à un compte par plusieurs threads sans verrouillage des données : – pour permettre au Thread 1 de ne pas être interrompu,
on utilise le mot clé synchronised comme modificateur de la méthode qui ne doit pas
Remarque sur l’exemple: être interrompue :
– Pendant un certain laps de temps, la somme des 2 comptes sera inexacte !!! – public synchronized void transfert( Compte source, Compte dest, int montant) {
Par exemple: affichage du solde des deux comptes: if (source.getSolde() < montant) return;
– Thread_2 fait appel à la méthode getSolde() de compte_1 source.solde = source.solde – montant; // ***** ligne 3
– Il faut donc que le Thread_1 soit certain de ne pas être interrompu pendant le transfert afin dest.solde = dest.solde + montant; // ***** ligne 4
que l’affichage soit correcte. – }
– Dans ce cas, la méthode ne sera pas interrompue entre les lignes 3 et 4.
Remarque
– Pour repérer plus facilement les problèmes de multitâche, on peut ajouter des appels à la
méthode yield qui forcent le thread à rendre la main, et permettre ainsi à un autre thread
de pouvoir s’exécuter.
– public synchronized void transfert( Compte source, Compte dest, int montant) {
if (source.getSolde() < montant) return;
source.solde = source.solde – montant;
Thread.yield( );
dest.solde = dest.solde + montant;
– }

Synchronisation de threads et accès aux ressources partagées Synchronisation de threads et accès aux ressources partagées
Acquisition et restitution d’un moniteur
– Un thread W acquiert automatiquement le moniteur d’un objet F en exécutant du code Résumé
synchronisé sur cet objet F.
– Tant que Thread_1 exécute du code synchronisé sur un objet objetXXX, les autres threads
– W rend le moniteur en quittant le code synchronisé (ou se met en attente en appelant F.wait()
dans le code synchronisé).
ne peuvent exécuter du code synchronisé sur ce même objet objetXXX
– W peut quitter le code synchronisé normalement, ou si une exception est lancée et qui n’est pas Ceci concerne le même code excécuter par thread_1, ou n’importe quel autre code
saisie dans le code synchronisé. synchronisé sur objetXXX , ils sont mis en attente
– Lorsque Thread_1 quitte le code synchronisé ou se met en attente par la méthode wait( ),
Remarque un des threads en attente peut commencer à exécuter le code synchronisé
– utilisation du mot clé synchronized Les autres threads en attente auront la main à tour de rôle (si tout se passe bien…)
synchronized void f( ) { /*...*/ }
synchronized void g( ) { /*...*/ } Exemple : ok
– si f( ) est appelée par l’objet ObjetXXX , donc exécutée par un thread – public class Compte {
g( ) ne peut être appelée sur l’instance ObjetXXX , par un autre thread, tant que f( ) n’est private double solde;
pas terminée dans son exécution.
public void deposer(double somme) {
– il y a mise en place automatique d’un principe de verrouillage (lock) des données de l’instance
concernée. – solde = solde + somme;
– C.a.d « aucune autre méthode synchronisée de l’objetXXX ne peut être exécutée }
tout autre thread T2 qui tentera d’utiliser une opération synchronisée sur ce même objet public double getSolde() {
ObjetXXX (en appelant une autre méthode synchronized de cette même classe) sera bloque – return solde;
jusqu'a ce que ce verrou soit enlevé. }
Mais T2 peut appeler la méthode g avec une autre instance de la même classe que
– }
ObjetXXX
Synchronisation de threads et accès aux ressources partagées Synchronisation de threads et accès aux ressources partagées
Autre exemple
On lance 3 threads du type suivant : – class ListeTab {
private String[ ] tab = new String[50];
– Thread t1 = new Thread() {
private int index = 0; void ajoute(String s) { tab[index] = s; index++; }
public void run( ) {
– }
– for (int i = 0; i < 100; i++) {
Soient deux threads T1 et T2 qui exécutent en parallèle la fonction ajoute(String) sur la même
compte.deposer(1000); ListTab. Donc , on aura les deux exécutions.
– } void ajoute(String s) { void ajoute(String s) {
} tab[index] = s; //(a1) tab[index] = s; //(b1)
– }; index++; //(a2) } index++; //(b2) }
Plusieurs exécutions sont possibles.Par exemple :
A la fin de l’exécution, des trois thread qui attaque le même compte, on n’obtient pas – (a1) (a2) (b1) (b2), est une exécution possible, cohérente ;
nécessairement 300.000 – (a1) (b1) (b2) (a2), est une exécution possible, mais incohérente : le tableau ne contient pas la
chaîne de caractères ajoutée par T1, et une case de la liste est vide.
Remarque: (voir la classe Compte_Cours) Pour éviter cela, on synchronise la section critique
– void ajoute(String s) {
– Pour éviter ce problème, il faut rendre la méthode deposer synchronisée :
Synchronized (tab)){
public synchronized void deposer(double somme)
– tab[index] = s;
– index++;
}
– }

Synchronisation de threads et accès aux ressources partagées Synchronisation de threads et accès aux ressources partagées
Remarque
– Il faut synchroniser le moins de code possible pour faciliter les accès multiples
Remarque
les performances seront meilleures s’il y a moins de threads en attente d’un moniteur.
– Méthodes de classe synchronisées
Si on synchronise une méthode static, on bloque le moniteur de la classe.
– Synchronisation et performances
– On bloque ainsi tous les appels à des méthodes synchronisées de la classe (c.a.d
L’exécution de code synchronisé peut nuire aux performances (un mécanisme toutes les fonctions déclarées static)
couteux)
mais pas les appels synchronisés sur une instance de la classe.
Si un thread entre dans le moniteur de classe alors les autres restent en
Il peut provoquer des blocages entre threads (blocage temporaire de traitement) ; attente.

Du code synchronisé peut empêcher des optimisations (inlining) au moment de la – Méthode synchronisée et héritage
compilation.
La redéfinition d’une méthode synchronisée dans une classe fille peut ne pas être
– appel de méthode « synchronized » est 4 fois + long qu'appel méthode "normal" synchronisée.
De même, la redéfinition d’une méthode non synchronisée peut être synchronisée.
Il faut faire des fonctions synchronisées petites pour que le blocage soit le plus court
possible.

Attention au DeadLock (Thread_1 attend la réponse de Thread_2 et inversement)

– Les méthodes non synchronized sont tjrs exécutables concurremment


Communication entre threads: wait et notify Communication entre threads: wait et notify

Exécution conditionnelle
– Lorsqu’un programme est multi-tâche, la situation suivante peut se rencontrer : Schéma d’utilisation de wait-notify
Un thread T1 ne peut continuer son exécution que si une condition est remplie – Cette utilisation demande un travail coopératif entre les threads T1 et T2 :

Le fait que la condition soit remplie ou non cela dépend d’un autre thread T2 Ils se mettent d’accord sur un objet commun: Obj
– Par exemple, T1 a besoin du résultat d’un calcul effectué par T2 – Obj est un objet quelconque qui sera l’objet source du blocage
– Obj sera aussi l’objet de verrouillage (-- synchronized (Obj) --).
– Une solution coûteuse serait que T1 teste la condition à intervalles réguliers.
Arrivé à l’endroit où il ne peut continuer que si la condition est remplie,
– Les méthodes wait( ), notify( ) et notifyAll( ) de la classe Object permettent de – T1 se met en attente : Obj.wait( );
programmer plus efficacement ce genre de situation (collaboration entre threads) – wait doit être invoquée sur l’objet verrouillée (le moniteur)
Ces méthodes permettent la communication entre Threads
Quand T2 a effectué le travail pour que la condition soit remplie, il le notifie en
On a un blocage avec wait et déblocage avec notify / notifyAll utilisant l’objet source de blocage de la fonction wait:
Obj.notify( );
Ces méthodes sont implémentées comme final dans la classe Object, de sorte que ce qui débloque T1
toutes les classes en disposent.

Communication entre threads: wait et notify Communication entre threads : wait et notify
Besoin de synchronisation
– Le mécanisme d’attente-notification (Bloquage-débloquage) lié à un objet met en jeu l’état wait(long timeout)
interne de l’objet ; – suspend le processus courant jusqu'à ce que la méthode notify() ou notifyAll() de cet objet
pour éviter des accès concurrent à cet état interne, une synchronisation est nécessaire soit appelée, ou bien que le temps indiqué soit écoulé

– Les appels objet.wait() et objet.notify() (wait(long timeout) et objet.notifyAll()) ne Utilisation de wait


peuvent donc être effectués que dans – Mauvaise utilisation :
du code synchronisé sur objet if (!condition) objet.wait();
– Car pour appeler l'une de ces quatre méthodes, il faut posséder l'objet. Ce qui – si on quitte l’attente avec le wait( ), cela signifie qu’un autre thread a notifié que
signifie utiliser l'instruction synchronized. Dans le cas contraire, une exception la condition était remplie
est levée. – mais, après la notification, et avant le redémarrage de ce thread, un autre thread
– Autrement dit, a pu prendre la main et modifier la condition
wait et notify ne peuvent être lancés que dans une section critique
– Le bon code (dans du code synchronisé sur le même objet que le code qui modifie la
Méthode wait() condition) :
– public final void wait( ) throws InterruptedException while (!condition) {
– objet.wait( ) – objet.wait();
nécessite que le thread en cours possède le moniteur de objet. }
bloque le thread qui l’appelle, jusqu’à ce qu’un autre thread entre dans le même
moniteur et appelle la méthode objet.notify( ) ou objet.notifyAll( )
libère le moniteur de l’objet afin qu’il soit utilisé par d’autres threads
Communication entre threads: wait et notify Communication entre threads: wait et notify

Méthode notifyAll( ) Schéma général de synchronisation


– public final void notifyAll( ) – bloquer (éventuellement) lors de l'entrée
– objet.notifyAll( ) – réveil des bloqués en fin
nécessite que le thread en cours possède le moniteur de objet synchronized(this) {
débloque tous les threads qui s’étaient bloqués sur l’objet avec objet.wait() – while (!condition) {
try {
– Un seul des threads débloqués va récupérer le moniteur, on ne peut prévoir lequel this.wait( );
– Les autres devront attendre qu’il relâche le moniteur pour être débloqués à tour de rôle, } catch(InterruptedException ie) { …..}
mais ils ne sont plus bloqués par un wait. – }
}
– En fait, souvent ils se bloqueront à nouveau eux-mêmes (s’ils sont dans une boucle while // ... Autres codes
avec wait) synchronized(this) {
– L'endroit le plus approprié pour appeler cette méthode est dans l'objet qui est susceptible – ……
de changer la donné pour les threads mis dans la file d'attente avec wait. – try {
this.notifyAll( );
Méthode notify( ) – } catch(InterruptedException ie) { …… }
– objet.notify( ) }
idem notifyAll( ) mais ne débloque qu’un seul thread
– On ne peut prévoir quel sera le thread débloqué et, le plus souvent, il vaut donc mieux
utiliser notifyAll( )

Communication entre threads: wait et notify Communication entre threads: wait et notify

Remarque Déblocage des threads


– Pour endormir un thread sur le moniteur, il faut utiliser la méthode wait. – Le thread débloqué (et élu) ne pourra reprendre son exécution que lorsque le thread qui l’a
Plusieurs prototypes sont fournis afin de pouvoir attendre indéfiniment soit durant un notifié rendra le moniteur de l’objet en quittant sa portion de code synchronisé
délai maximal.
– Le redémarrage et l’acquisition se fait dans une opération atomique
– La méthode Thread.sleep permet aussi de faire patienter un thread, mais il sera
alors impossible de faire reprendre l'activité du thread avant la fin du timeout. Exemple: producteur- consommateur (voir fichier TesteDepot.java) ok
– Les instances d’une classe Depot contiennent des jetons
– Par contre, la méthode Object.wait le permet. – Ces jetons sont
déposés par un producteur
– Pour réveiller des threads endormis, on peut utiliser les méthodes notify et notifyAll. consommés par des consommateurs
Ceci si, on choisis de réveiller un unique thread endormi sur un objet sur lequel il faut – Les producteurs et consommateurs sont des threads.
se synchroniser, soit on décide de tous les réveiller.

– wait( ) pour bloquer un thread si une condition n’est pas vérifiée.

– notify( ) ou notifyAll( ) pour réveiller un thread bloquée lorsque la condition


devient vraie.
Communication entre threads: wait et notify Communication entre threads: wait et notify

public class Depot {


/* Classe gérant les mouvements des jettons des consommateurs et des producteurs d'un dépôt
– private int nbJetons = 0; de jetons */
– public synchronized void donneJeton() { public class TesteDepot implements Runnable {
try { – private String nom; //nom du depot
– while (nbJetons == 0) { – private Depot depotClient;
this.wait( ); – Thread tr; // qui sera soit producteur soit consommateur
– } – int cpProduit = 0, cpConsommer = 0;
– nbJetons--;
}catch (InterruptedException e) { – public TesteDepot(String nom, Depot depotClient) {
} this.nom = nom;
– } this.depotClient = depotClient;
– public synchronized void recois(int n) { tr = new Thread(this, nom);
nbJetons += n; – }
this.notifyAll();
– }
}

Communication entre threads: wait et notify Communication entre threads: wait et notify
/*on arrête si le nombre de jetons présent dans le dépôt est > 100 avec le nombre de jetton
fabriqués est 2 jettons à la fois par le producteur */ Autre exemple
public void run() { – class ListeTab {
– while (true) { private List<String> tab = new ArrayList<>( );
System.out.println("nom thread actif = " + nom); void String supprimer(String s) {
if (depotClient.getNbJetons() > 100) { – synchronized(tab){
– System.out.println("nbJetons en course = " + depotClient.getNbJetons( ) while(tab.isEmpty()) { tab.wait( ) ;}
+ " lors de l'appel de " + nom); String element = tab.remove(0);
– return; – }
} }
if (nom.equalsIgnoreCase("consommateur")) { public void addElemnt(String element){
– depotClient.donneJeton(); cpConsommer++; synchronized(tab){
– System.out.println("le nombre de jetons consommer est: " + cpConsommer); – tab.add(element);
} else if (nom.equalsIgnoreCase("producteur")) { – tab.notifyAll();
– depotClient.recois(2); // fabrique 2 jetton à la fois – ……..
– cpProduit += 2; }
– System.out.println("le nombre de jetons Produit est: " + cpProduit); – }
} – Après pour le teste, on crée deux threads un qui exécute la fonction supprimer et l’autre
else { System.out.println("Ce depot ne fait aucune opération."); } qui fait addElement afin de débloquer celui qui est bloqué par wait
(voir class ObjectDemo)

– }
}
Communication entre threads: wait et notify Communication entre threads: wait et notify --Resumé

Variante de wait Java permet à des threads de "communiquer" entre eux sur leur état via (join, notify, wait).
– Si on ne veut pas attendre éternellement une notification, on peut utiliser une des variantes
suivantes de wait :
Les méthodes wait et notify permettent de gérer la programmation concurrentielle (accès
public void wait(long timeout) concurrent à un même objet par plusieurs threads).
public void wait(long timeout, int nanos)
– Dans ce cas, le thread doit gérer lui-même le fait de connaître la cause de son déblocage Elles permettent de construire un mécanisme explicite de blocage et déblocage (ou mécanisme
(notify ou temps écoulé) d’attente-notification) de Thread.
– Blocage avec wait et déblocage avec notify / notifyAll.
Moniteurs réentrants
– Un thread qui a acquis le moniteur d’un objet peut exécuter les autres méthodes wait() et notify() synchronisent des threads sur un moniteur (objet verrou)
synchronisées de cet objet ; il n’est pas bloqué en demandant à nouveau le moniteur.
– Elles ne peuvent être appelées que depuis un block synchronized ou fonction synchronized

Affectations atomiques
– Il est inutile de synchroniser une partie de code qui ne fait qu’affecter (ou lire) une valeur
à une variable de type primitif de longueur 32 bits ou moins (int, short, …).
En effet, la spécification du langage Java spécifie qu’une telle affectation ne peut être
interrompue pour donner la main à un autre thread.
Mais, cette spécification n’assure rien pour les affectations de double et de long !
– JVM garantit atomicité d'accès au byte, char, short, int, float, réf. d'objet
!! pas long, ni double !!

Communication entre threads: wait et notify --Resumé La classe Timer


La classe java.util.Timer permet de lancer un processus une ou plusieurs fois en précisant des
wait délais.
– le thread actuel est bloqué et rend le verrou, elle permet de suspendre le fonctionnement Un Timer gère les exécutions d'une instance de TimerTask, classe qui Implémente Runnable.
d’un thread Exemple
– le thread est placé dans une liste d’attente – public class ExempleTimer extends TimerTask{
– tant qu’il est dans la liste d’attente, il n’a aucune chance d’être exécuté public void run( ){
– try{
– tant qu’il n’a pas reçu une notification d'un autre thread via l’appel de notify() /
notifyAll() ), il reste bloqué. System.out.println("je m'execute");
Thread.sleep(500);
– }
notify / notifyAll – catch(InterruptedException e){
– notify : supprime un thread choisi au hasard dans la liste d’attente System.out.println(e.getMessage());
– notifyAll : supprime tous les threads dans la liste d’attente – }
– un thread supprimé de la liste d’attente redevient exécutable }
– lorsque le verrou est à nouveau disponible, l’un des threads le prend et continue son – } // fin classe.
exécution – Timer t = new Timer();
– débloque tous les threads qui s’étaient bloqués par wait( ) – // la tâche se répètera toutes les 2s et démarre dans 1s
– t.schedule(new ExempleTimer(),1000,2000);
– Date d = new Date();
– d.setTime(d.getTime()+10000);
– // la tâche démarre dans 10s
– t.schedule(new ExempleTimer(),d);
La classe Timer Les expressions régulières

Pragmatiquement
Définition:
– Un Thread pour repousser une action dans le temps
– Une expression régulière (en anglais regexp pour regular expression) est une chaine de
Dans 3 secondes, si l’utilisateur n’a pas bougé sa souris, afficher un popup disant « caractères «que l’on appelle parfois un motif» qui décrit un ensemble de chaines de
Ce bouton sert à quitter le document » caractères.
– Un Thread pour répéter une action régulièrement Tous les 0.5 secondes, redessine la barre – une expression régulière est une chaîne de caractères écrite dans une syntaxe particulière,
de progression. propre à la bibliothèque d'expressions régulières utilisée.
– Pour ces cas simple, pas besoin de faire des Threads compliqués : Utiliser un Timer !
Exemple:
– L’expression régulière [0-9][a-z] décrit l’ensemble des chaines de caractères composées
d’un chiffre et d’une lettre.

M.AFILAL 454

Utilité des expressions régulières Composantes d’une des expressions régulières: Les caractères
Les expressions régulières ont de nombreuses utilités en informatique, elles servent
principalement pour réaliser : Caractère Signification
– des recherches multiples ou des filtres :
X Le caractère X
ne conserver que certaines lignes d’un fichier texte, de la forme nom=valeur par
exemple \\ Le caractère \
– des contrôles :
\t Le caractère tabulation
vérifier qu’une donnée entrée par un utilisateur a bien le format d’une adresse IP ou
une adresse e-mail par exemple, comme « [email protected] ». \n Le caractère nouvelle ligne
– des substitutions :
\r Le caractère retour chariot
Remplacer un motif par une chaine de caractères précise.
Remplacer une date sous format américain (08-05-1985) pour la mettre au format en \f Le caractère saut de page
français (05/08/1985). Le caractère sonnette '\u0007'
\a
Remplacer automatiquement toutes les adresses « http:// » par des liens cliquables,
comme cela se fait sur certains forums. \e Le caractère d'échappement '\u001B'
– des découpages : Caractère de contrôle de tabulation
récupérer une partie d’une chaine de caractères par exemple découper une ligne par \v
verticale [\x0B]
rapport aux « ; » dans le cas d’un fichier .csv.
Remarque Exemple
– Les expressions régulières sont un moyen très puissant et très important pour faire des – AB\tCD représente la chaine de caractère AB suivit d’une tabulation suivi de CD
recherches dans des ensembles de documents textuels ou dans des chaînes de caractères
et leurs appliquer un traitement automatisé.
M.AFILAL 455 M.AFILAL 456
Composantes d’une des expressions régulières:
Composantes d’une des expressions régulières:
Les méta caractères
Classes de caractères
Méta Caractères Signification
Classe de caractères Signification
. Remplace n'importe quel caractère, sauf la fin de ligne.
[0-9] Un caractère numérique entre 0 et 9
? Opérateur portant sur l'expression précédente : 0 ou une fois l'expression précédente.
[abc] Un caractère de la classe des trois caractères a, b et c
* Opérateur de Kleene : 0, 1 ou plusieurs fois l'expression précédente.
[^abc] Un caractère de la classe de tous Les caractères sauf a, b et c
+ 1 ou plusieurs fois l'expression précédente.
[a-z] Un caractère de a à z
[] Intervalle de caractères.
[a-zA-Z] Un caractère de a à z minuscule ou majuscule
{} Quantificateur.
[a-gmn] Un caractère de a à g, ou m ou n.
\ Le caractère qui suit n'est plus considéré comme un méta-caractère.
On peut inclure des classes les unes dans les autres. Cette classe représente un unique
^ Négation ou début de ligne. [a-g[A-G]] caractère, compris entre a et g, en minuscule ou en majuscule. Elle est équivalente à
$ Fin de ligne. [a-gA-G].
Le signe && représente l'intersection. Le résultat est l'intersection entre la classe [a-g]
| Opérateur ou. [a-g&&[c-k]]
et la classe [c-k]. Il s'agit donc de la classe [c-g].
Le résultat est l'intersection entre la classe qui représente tous les caractères de a à g,
Les méta-caractères sont des caractères qui sont interprétés par l'analyseur. [a-g&&[^cd]] et celle qui représente tous les caractères, sauf c et d. Il reste donc a, b, e, f et g, que
Pour qu'un méta-caractère ne soit par interprété par l'analyseur, il faut le faire précéder du l'on peut aussi écrire [abefg] ou [abe-g].
caractère '\'.
Réalise l'intersection de tous les caractères compris entre a et z, et de tous les
Les caractères '-' et ']' ont un statut spécial : dans un intervalle ce sont des méta-caractères, et [a-z&&[^m-p]]
caractères sauf ceux compris entre m et p. Il nous reste donc [a-lq-z].
en dehors d'un intervalle ils se comportent comme des caractères normaux.
– [a\]b]*c est équivalent à (a|]|b)*c M.AFILAL 457 Exemple M.AFILAL 458

– [a\-b]*c est équivalent à (a|-|b)*c – [0-9][a-z] représente une chaine de caractères constitué d’un chiffre puis d’une lettre

Composantes d’une des expressions régulières: Composantes d’une des expressions régulières:
Classes de caractères prédéfinies Classes de caractères POSIX
Classe de caractères Signification
Classe signification
. Caractère quelconque
\p{Lower} Une minuscule:[a-z]
\d Un caractère numérique: [0-9]
\p{Upper} Une majuscule:[A-Z]
\D Un caractère non numérique: [^0-9]
\p{Alpha} Une caractère alphabétique: [\p{Lower} \p{Upper}]
\s N'importe quel caractère blanc (espace, tabulation, retour-chariot, etc...):
[ \t\n\x0B\f\r] \p{Digit} Un chiffre:[0-9]
\S Un caractère non blanc: [^\s] \p{Alnum} Un caractère alphanumérique:[\p{Alpha} \p{Digit}]
\w N'importe quel caractère utilisable dans un mot (w est utilisé pour word). \p{Punct} Ponctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
Cela représente les caractères alphabétiques minuscules et majuscules, les
chiffres et le caractère souligné (underscore). Notons que les caractères \p{Blank} Espace ou tabulation: [ \t]
accentués ne s'y trouvent pas : [a-zA-Z_0-9]
\p{XDigit} Caractère hexadécimale: [0-9a-fA-F]
\W Inverse de la classe précédente : [^\w]
\p{Space} Un caractère blanc: [ \t\n\x0B\f\r]
N'importe quel caractère minuscule. Les caractères accentués s'y trouvent !
\p{javaLowerCase}
Equivalent to java.lang.Character.isLowerCase() \p{ASCII} tout caractère ASCII :[\x00-\x7F]
N'importe quel caractère majuscule. Les majuscules accentuées s'y
\p{javaUpperCase} \p{Graph} Un caractère visible : [\p{Alnum}\p{Punct}]
trouvent. Equivalent to java.lang.Character.isUpperCase()
\p{javaWhitespace} N'importe quel espace. Equivalent to java.lang.Character.isWhitespace() \p{Print} Un caractère imprimable [\p{Graph}\x20]

N'importe quel M.AFILAL


caractère écrit en miroir au sens de Unicode. 459 M.AFILAL 460
\p{javaMirrored}
Equivalent to java.lang.Character.isMirrored()
Composantes d’une des expressions régulières: Composantes d’une des expressions régulières:
Caractères de répétition Caractères de répétition
Les quantificateurs déterminent le nombre de répétitions tolérées d'un caractère quelconque
ou d'une expression régulière complète ou partielle.
Action Expression régulière Cible
Le nombre de répétitions est indiqué par un symbole (?, *, +) ou par un intervalle via { } (ex.:
{1, 3}). Recherche des mots se \b\p{Lower}*e\b Elle est belle Isabelle lorsqu'elle se lève.
Dans ces tableaux, X représente une classe quelconque. terminant par la lettre e
Recherche deux caractères l l{2} Elle est belle Isabelle lorsqu'elle se lève.
Quantificateurs Quantificateurs Quantificateurs consécutifs
gloutons ou avides hésitants ou réticents possessifs Signification
(greedy) (reluctant) (Possessive) Recherche d'un ou plusieurs l+ Elle est belle Isabelle lorsqu'elle se lève.
caractères l consécutifs
X? X?? X?+ X une fois ou zéro fois
Recherche d'un caractère e el? Elle est belle Isabelle lorsqu'elle se lève.
X* X*? X*+ X zéro ou plusieurs fois suivi éventuellement par un l

X+ X+ X++ X une fois au moins (XX*) Recherche d'un caractère e el{1, 2} Elle est belle Isabelle lorsqu'elle se lève.
suivi par un (minimum) ou
X{n} X{n}? X{n}+ X n fois deux (maximum) l

X{n,} X{n,}? X{n,}+ X, au moins n fois Recherche d'un caractère e el{1,} Elle est belle Isabelle lorsqu'elle se lève.
suivi par un (minimum) ou
X{n, m} X{n, m}? X{n, m}+ X entre n et m fois plusieurs l

M.AFILAL 461 M.AFILAL 462

Composantes d’une des expressions régulières: Composantes d’une des expressions régulières:
Caractères de répétition Caractères de répétition
Remarque
Les quantificateurs gloutons consomment la chaîne de caractères cible entière lors d'une
première tentative de recherche. Puis si cette première tentative échoue, alors à partir de Remarque
la fin de la chaîne cible, ils reculent d'un caractère, essayent à nouveau une mise en Les quantificateurs hésitants fonctionnent différemment des quantificateurs gloutons. En
correspondance, puis répètent ce processus jusqu'à que ce la recherche réussisse ou qu'il effet, ils démarrent une recherche au début de la chaîne de caractères cible, consomment
n'y ait plus de caractères. un caractère à la fois pour une mise en correspondance. La dernière chose qu'ils essayent
Exemple: Recherche d’une expression régulière dans une chaîne cible est une mise en correspondance sur le chaîne de caractères cible entière.
Exemple: Recherche d’une expression régulière dans une chaîne cible
Cible regexp Résultat

xxxabcxxxdefxxxabcxxxdefxxxabcxxxdef .?def debut=8, fin=12, groupe(0) = xdef


debut=20, fin=24, groupe(0) = xdef Cible regexp Résultat
debut=32, fin=36, groupe(0) = xdef xxxabcxxxdefxxxabcxxxdefxxxabcxxxdef .??def debut=8, fin=12, groupe(0) = xdef
.*def debut=0, fin=36, debut=20, fin=24, groupe(0) = xdef
groupe(0) = xxxabcxxxdefxxxabcxxxdefxxxabcxxxdef debut=32, fin=36, groupe(0) = xdef
.+def debut=0, fin=36, .*?def debut=0, fin=12, groupe(0) = xxxabcxxxdef
groupe(0) = xxxabcxxxdefxxxabcxxxdefxxxabcxxxdef debut=12, fin=24, groupe(0) = xxxabcxxxdef
debut=24, fin=36, groupe(0) = xxxabcxxxdef
.+?def debut=0, fin=12, groupe(0) = xxxabcxxxdef
debut=12, fin=24, groupe(0) = xxxabcxxxdef
debut=24, fin=36, groupe(0) = xxxabcxxxdef

M.AFILAL 463 M.AFILAL 464


Composantes d’une des expressions régulières: Composantes d’une des expressions régulières:
Caractères de répétition Caractère de début ou de terminaison

Certains caractères spéciaux permettent de détecter des éléments particuliers d'un texte.
Remarque
Ils précisent les limites des occurrences correspondant à une expression régulière ou à
Les quantificateurs possessifs consomment toujours la chaîne de caractères cible entière, l'une de ses parties.
en essayant une seule fois une mise en correspondance. A la différence des
quantificateurs gloutons (greedy), ceux-ci ne reculent jamais, même si cela permettrait De cette manière, il est possible de délimiter une ligne (^Ligne...$)ou un mot (\bmot\b).
de réussir une recherche.
Exemple: Recherche d’une expression régulière dans une chaîne cible Caractère Signification
^ Un début de ligne.

$ Une fin de ligne.


Cible regexp Résultat
\b Le début ou la fin d'un mot: « \w»
xxxabcxxxdefxxxabcxxxdefxxxabcxxxdef .?+def aucun
\B Le début ou la fin d'un élément qui n'est pas un mot: « \W»
.*+def aucun
\A Le début d'une entrée.
.++def aucun
\G La fin du morceau de texte qui a été trouvé précédemment.

\Z La fin d'une entrée, sauf s'il s'agit de la fin du texte.

\z La fin d'une entrée

M.AFILAL 465 M.AFILAL 466

Composantes d’une des expressions régulières: Composantes d’une des expressions régulières:
Caractère de début ou de terminaison Opérateurs logiques
Exemple:
Expression signification

Action Expression régulière Cible XY X suivi de Y

Délimitation d'une entrée \A.*\Z Du sublime au ridicule il n'y a qu'un pas. X|Y X ou Y
(Napoléon 1er)
Groupe d'un caractère X. Sachant que ( ) représente délimiteurs de groupe (avec
(X)
Délimitation d'une entrée \b.*\b Du sublime au ridicule il n'y a qu'un pas. capture)
(Napoléon 1er)
Délimitation d'une entrée \bN\P{Lower}+\b Du sublime au ridicule il n'y a qu'un pas.
(Napoléon 1er)
Recherche du premier mot ^\w*\b Du sublime au ridicule il n'y a qu'un pas.
d'une ligne (Napoléon 1er)
Recherche d'une sous-chaîne \([\p{Lower}[0-9 ]]*\)$ Du sublime au ridicule il n'y a qu'un pas.
entre parenthèses et en fin de (Napoléon 1er)
ligne
Recherche d'une sous-chaîne ^<tr>.*</tr>$ <html>
entre parenthèses et en fin de <body>
ligne <table>
<tr><td>A</td></tr> <tr><td>B</td></tr>
<tr><td>C</td></tr> <tr><td>D</td></tr>
<tr><td>E</td></tr> <tr><td>F</td></tr>
M.AFILAL </table> 467 M.AFILAL 468
</body>
Composantes d’une des expressions régulières: Composantes d’une des expressions régulières:
Exemples Exemples
Expression régulière correspondant à une adresse IP
[0-9]{1,3}(?:.[0-9]{1,3}){3}
Exemple: 192.168.0.1
Si, on n’impose pas un nombre de chiffres compris entre un et trois Expression régulière correspondant à une date
\d+\.\d+\.\d+\.\d+ Une date au format 29 février 2012 suit le motif suivant : Un ou deux chiffres, un
Expression régulière correspondant à une adresse e-mail espace, un nombre indéfini de lettres, un espace puis quatre chiffres. L’expression
régulière associée est donc :
(\\w+)@(\\w+\\.)(\\w+)(\\.\\w+)*
[0-9]{1,2} [a-z]+ [0-9]{4}
Exemple: [email protected]
ou
Expression régulière correspondant à un numéro téléphonique
\d{1,2} \p{Lower}+ \d{4}
numéro de téléphone au format français: formé de 5 blocs de 2 chiffres avec n’importe
quel caractère entre chaque bloc
([0-9]{2}.){4}[0-9]{2}
Expression régulière correspondant à une URL
[a-z]{3,}://[a-z0-9-]+.[.a-z0-9-]+(?::[0-9]*)?
Exemple: http://5.freshminutes.it/index.php
Expression régulière correspondant à un nombre réel.
(\+|-)?([0-9]+\.?[0-9]*|\.[0-9]+)([eE](\+|-)?[0-9]+)?
Remarque
Le point s’écrit \. car « . » est un caractère qui signifie « un caractère quelconque ».
Le \ précédent le . sert à indiquer qu’il ne faut pas interpréter le point comme une
expression régulière mais comme le caractère point.
M.AFILAL 469 M.AFILAL 470
Idem pour \+

Les expressions régulières en java Les expressions régulières en java: exemple


Exemple: Utilisation de la méthode statique Pattern.matches
La façon la plus simple d'utiliser une expression régulière est d'utiliser la méthode
Java fournit dans son JDK depuis la version 1.4 une API standard permettant la manipulation statique matches de classe Pattern.
d'expressions régulières:
String texte = "Quand le ciel bas et lourd" ; // texte à tester
Package java.util.regex
boolean existe = Pattern.matches("a.*", texte) ;
Trois classes interviennent dans les utilisations d'expressions régulières en Java :
Le booléen existe sera vrai si texte contient une chaîne de caractères commençant par la
Pattern : lettre a. Dans cette analyse ou moteur d'expression régulière,
permet de compiler l'expression régulière fournie .* correspond à n'importe quel nombre de caractères quelconques.
C’est une représentation compilée d’une expreg avec un motif donné. ?? Exemple : Utilisation d'un Matcher
Matcher : Une façon plus complexe, mais équivalente au code de recherche donné par
permet d'analyser une chaîne en entrée à partir d'un Pattern et de faire différentes l’exemple_1, est de l'écrire en utilisant un objet Matcher.
opérations dessus. String texte = "Quand le ciel bas et lourd" ; // texte à tester
C’est un moteur de recherche d’un motif dans une chaîne de caractères. ?? Pattern p = Pattern.compile("a.*") ; //compilation de la regex avec le motif : "a.*"
PatternSyntaxException : Matcher m = p.matcher(texte) ; //création du moteur associé à la regex sur la chaîne texte
exception levée lorsque la syntaxe d'une expression régulière n'est pas correcte. boolean existe = m.matches( ) ; // lancement de moteur de recherche
try{ // lancement de la recherche de toutes les occurrences avec find
while(m.find( )) System.out.println(texte.substring(m.start(), m.end( )));
}catch(PatternSyntaxException pse){ }
Remarque
La première méthode, donnée dans l’exmple_1, est bien adaptée aux cas les plus
simples.
La seconde méthode, donnée dans l’exmple_2, permet de réutiliser l'objet matcher, et
M.AFILAL 471 M.AFILAL 472
de faire des opérations plus complexes qu'une simple recherche.
Affichage de toutes les chaines commençant par un a qui se trouve dans texte
Les expressions régulières en java: la classe Pattern Les expressions régulières en java: la classe Pattern
La classe Pattern permet la configuration et la création de modèles d'expression régulière avec
un motif donné.
Description des champs de la classe Pattern
Option ou Description
Option des regexp
Option ou Description Équivalent
Option des regexp ou Champ
Équivalent embarqué
ou Champ
embarqué L'expression est interprétée comme une séquence de caractères
Deux caractères correspondent si et seulement si leurs littérale, c'est à dire que les méta-caractères et les séquences
LITERAL Aucun
CANON_EQ Aucun décompositions canoniques correspondent (ex.: '\\u00E0' d'échappement ne seront pas interprétés (ex.: \"\b.*\b\"
correspond à 'à'). recherche les occurrences \"\b.*\b\").
Insensibilité à la casse. Autorise le mode multiligne.
Les caractères US-ASCII minuscules ou majuscules MULTILINE (?m)regexp Les caractères '^' et '$' indiquent respectivement un début et une
CASE_INSENSITIVE (?i)regexp
correspondent (ex.: 'A' correspond à 'a'). fin de ligne (ex.: \"^A.*$\" correspond à \"Alors...\\n\").
Notons que les caractères accentués ne s'y trouvent pas. Autorise la gestion des caractères Unicode.
Autorise les espaces et commentaires dans la regex. L'expression de recherche est insensible à la casse des
UNICODE_CASE (?u)regexp
Les espaces blancs et les commentaires commençant par le caractères, y compris pour tous les caractères Unicode (ex.:
COMMENTS (?x)regexp
signe dièse '#' sont ignorés (ex.: ' ' ou \"#Une ligne...\" sont \u00C0 correspond à \u00E0).
ignorés). Autorise le codage Unix des fins de ligne.
Autorise le mode « point à tout ». UNIX_LINES (?d)regexp Le terminateur de lignes correspond à un caractère de fin de
Le point '.' permet de remplacer tous les caractères, y ligne Unix (Line Feed : \\n).
DOTALL (?s)regexp
compris les caractères de fin de ligne (ex.: \".*\"
M.AFILAL 473 M.AFILAL 474
correspond à \"Une ligne\nUne autre ligne\n\").

Les expressions régulières en java : la classe Pattern La classe Pattern: les méthodes compile( )
Description des méthodes de la classe Pattern

Méthode Description
La création d'un objet Pattern nécessite l'appel de l'une des méthodes statiques compile( ). La
static Pattern compile(String regex) crée un modèle à partir de l'expression régulière fournie. classe Pattern ne possède pas de constructeurs.
static Pattern compile(String regex, crée un modèle à partir de l'expression régulière et d'un indicateur passés
int flags) en argument. L'indicateur est une combinaison des options précitées Ces méthodes acceptent soit une expression régulière exprimée sous la forme d'une chaîne de
caractères, soit une expression régulière avec un paramètre optionnel flags ‘un drapeau’.
int flags() retourne l'indicateur pour le modèle courant.
Syntaxe :
Matcher matcher(CharSequence crée un objet Matcher qui appliquera le modèle courant sur la chaîne de static Pattern compile(String regex)
input) caractères passée en argument. static Pattern compile(String regex, int flags)
static boolean matches(String regex, compile une expression régulière à appliquer sur la chaîne de caractères
CharSequence input) fournie.
L'attribut flags précise les options à utiliser par la classe Pattern.
String pattern() retourne l'expression régulière du modèle courant. Les valeurs possibles sont :
String[] split(CharSequence input) découpe la chaîne de caractères spécifiée en fonction du modèle courant. UNIX_LINES, CASE_INSENSITIVE, COMMENTS, MULTILINE, DOTALL,
UNICODE_CASE et CANON_EQ
découpe la chaîne de caractères spécifiée en fonction du modèle courant.
La limite indique le nombre de fois que le modèle courant devra
s'appliquer à la chaîne de caractères. Lorsqu'un pattern doit être configuré de telle sorte à satisfaire à plusieurs options, le flags
String[] split(CharSequence input, n < 0 : n-1 applications du modèle. doit être une combinaison d'options, ceci grâce à l'opérateur OU binaire : |..
int limit) n = 0 : autant d'applications que possible, mais les sous-chaînes de
caractères vides sont supprimées.
n > 0 : autant d'applications que possible en conservant toutes les sous-
chaînes y-compris les vides.
M.AFILAL 475 M.AFILAL 476
La classe Pattern: les méthodes compile( ) La classe Pattern: les méthodes compile( )

Exemple Remarque
Si on veut ne pas tenir compte des majuscules On vu comment passer les options en paramètre à la méthode compile( ). Il est
également possible d'écrire ces options directement dans le motif de la regex. Elles
Pattern p = Pattern.compile("^[abc]$", Pattern.CASE_INSENSITIVE); doivent être placées en tout début.

Si on veut autoriser la fin de ligne Unix et utilisation du mode multiligne Exemple


Pattern p = Pattern.compile("^[abc]$", Pattern.MULTILINE | Motif : String regExp = "(?i)foobar"
Pattern.UNIX_LINES);
Chaîne à traiter : String texte = "FooBar, foobar, FOOBAR"
try {
Si on veut ne pas tenir compte des majuscules et considérer que « . » peut correspondre
à une fin de ligne Pattern pattern = Pattern.compile(regExp)
Pattern modele = Pattern.compile("l.*e", Pattern.CASE_INSENSITIVE | Résultats trouvés : "FooBar", "foobar", "FOOBAR"
Pattern.DOTALL); Pattern pattern = Pattern.compile(regExp);
Matcher m = pattern.matcher(texte);
while (m.find( )) { System.out.println(texte.substring(m.start( ), m.end( ))); }
} catch (PatternSyntaxException pse) { }

Résultats trouvés : "FooBar", "foobar", "FOOBAR"

M.AFILAL 477 M.AFILAL 478

La classe Pattern: la méthode matches( ) La classe Pattern: la méthode matcher( )

La méthode matches( ) retourne vrai (true) si une chaîne de caractères cible contient une La méthode Matcher renvoie une instance de type Matcher associée au pattern courant afin de
occurrence correspondant à une expression régulière. rechercher l’expression régulière dans le texte cible (et éventuellement effectuer des
remplacements ou autres actions)
Syntaxe
static boolean matches(String regex, CharSequence input) Syntaxe
Matcher matcher(CharSequence input)
Exemple
Recherche de chaine commençant par le caractère U suivi par au moins un caractère de Exemple
mot suivi par la lettre Z à la fin de la chaine recherchée dans la chaine cible. Pattern patternCourant = Pattern.Compile("toto");
boolean b = Pattern.matches("\\bU\\w+a\\b", "UwghjanZ") Matcher recherche = patternCourant.matcher("TexteCible");
// lancement de la recherche de toutes les occurrences boolean trouve = recherche.find();
boolean b = Pattern.matches("(a((b)(c)))", "abc");

Remarque
La méthode matches ( ) permet de faire directement une recherche sans passer
explicitement par la classe Matcher
Elle fait la même chose que l’expression suivante:
Pattern.compile(exprReg).matcher(texte).matches( )

M.AFILAL 479 M.AFILAL 480


La classe Pattern: les méthodes split() Les expressions régulières en java: la classe Matcher
La classe Pattern possède deux méthodes split() qui permettent la décomposition d'un texte en
prenant le pattern courant comme délimiteur.
La classe Matcher permet de rechercher des chaînes qui correspondent à une expression
Elle retourne un tableau de String. régulière et l'obtention des positions des occurrences trouvées.
Contrairement à la méthode split() de la classe String, peut prendre un paramètre optionnel Un objet Matcher est un moteur de recherche d’une expression régulière associée à un motif
limit qui dans une chaîne de caractères.
permet de fixer le nombre maximum de sous-chaînes générées, http://roger.neel.free.fr/langages/cours_htm/courspdf/coursjava.html#regexp
peut favoriser la récupération de valeurs vides. http://cyberzoide.developpez.com/tutoriels/java/regex/
Syntaxe : Fichier: entree-sortie21bien.pdf
String[ ] split(CharSequence input) Fichier: ch2_miseEnOeuvre.pdf
String[ ] split(CharSequence input, int limit) Fichier: LesExpressionRegulieres.pdf
Fichier: cours_4_12.pdf
Exemple: Cet exemple permet de scinder une phrase en ses 10 premiers mots. Le motif \W On obtient une instance avec la méthode matcher de la classe Pattern en donnant le texte
signifie tout caractère de non-mot. dans lequel se fera la recherche
// compilation de la regex Les méthodes reset(CharSequence) et reset() réinitialisent un matcher pour effectuer une
Pattern p = Pattern.compile("\\W"); nouvelle recherche
// séparation en sous-chaînes
String[] items = p.split("J'aime le chocolat.", 10);
// parcours du tableau des sous-chaînes
for(int i = 0; i < items.length; i++) { System.out.println(items[i]); }
Le résultat est le suivant :
J
aime M.AFILAL 481 M.AFILAL 482
le
chocolat

Vous aimerez peut-être aussi