Cours de IA & POO Avancée: Pierre Chauvet

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 193

Cours de

IA & POO Avancée


Pierre Chauvet
[email protected]
Déroulement du cours

1. Projet ROBOT virtuel


2. POO et UML
- Conception orientée objet
- Principes de la POO
- UML
3. La programmation concurrente en JAVA
- Rappels sur les processus
- Gestion de la vie d’un thread
4. Graphismes et animations
5. Le traitement des Exceptions
- Le traitement des erreurs
- Lever une exception
- Propagation & récupération d’une exception
Déroulement du cours

Evaluation :
- Projet permettant de mettre en œuvre les principaux
points abordés en cours
- Réalisé par binôme en Java sous Eclipse
- Restitution du code et d’un rapport (modélisation,
validation, expériences)
ROBOT Virtuel
POO et UML
Objectifs pédagogiques

Pour un renforcement en POO sous Java


• Faire de la programmation orientée objet
• Apprendre à gérer les exceptions
• Séparer le modèle de la vue (on développera plusieurs vues), en
utilisant une approche Observateur-Observé
• Faire du multi-threading simple : un thread de calcul/affichage qui peut
être lancé, stoppé ou pausé reposant sur un framework « worker »
• Programmer un composant visuel pour afficher les mouvements du
robot : notion de fenêtre écran/fenêtre réelle et technique du double-
buffering
Pour une initiation à l’IA
• Avec un peu de systémique
• Découverte des réseaux de neurones artificiels
• Initiation aux algorithmes génétiques
• Initiation aux algorithmes de type colonie de fourmis
Résumé du projet
Réaliser un simulateur de robot doté de capteurs et d’actuateurs très simples (le
projet n’est pas là pour illustrer un cours de mécanique), et dont le contrôleur (le
« cerveau ») est utilise différents modèles de l’IA (réseaux de neurones artificiels).
L’apprentissage utilisera des algorithmes génétiques et/ou des règles classiques
(minimisation d’une erreur).

Le (ou les) but du robot seront développés pendant le projet, d’un but simple
(balayer un maximum de surface, i.e. parcourir un maximum de cellules) à un but
plus compliqué (survivre !). L’environnement sera constitué d’une grille à 2
dimensions.
Le Robot R1
R1 est doté:
• de capteurs (percevoir)
• d’actuateurs (agir)
• d’un contrôleur (le « cerveau », réfléchir)
• d’un état interne (et mémoire éventuellement)

Cf document “projet ROBOT.pdf”


Travail Pratique n°1
1. Proposer un diagramme de classes (détailler si nécessaire certaines méthodes, ex. algorithme de déplacement du robot en
fonction de la commande) permettant de simuler un seul robot dans son environnement, sans s’occuper à ce stade de la partie
représentation graphique et de l’IHM. Se placer à un niveau d’abstraction suffisant pour tester différents types de contrôleurs. On
considère que les bords du monde sont impassables.

2. Réaliser une application Java fonctionnelle à partir de ce diagramme de classe, pour simuler ce robot. On implémentera un
contrôleur très simplifié pour tester l’application. Il est possible, et même recommandé, de modifier/corriger le diagramme de
classe en fonction du code développé.
Conception
Orientée Objet
Approches fonctionnelle et objet

• Analyse et conception de systèmes complexes :


• décomposer pour comprendre,
• réunir pour construire

• La représentation du système utilise un modèle qui


permet de formaliser la démarche (=méthode d’analyse
et de conception)

• Dans l’approche fonctionnelle, on décrit le système par


une décomposition en sous-systèmes correspondant à
des fonctions plus ou moins élémentaires qui participent
à la fonction principale

• La hiérarchie doit être stable au sens où une évolution


fonctionnelle ne doit pas provoquer des modifications
structurelles lourdes.
Notion de Système

• Un système est une société d’objets qui


coopèrent en vue d’accomplir un objectif
(sa fonction)
Approche Objet

• Un logiciel est construit sous la forme d’objets


qui coopèrent
• La définition d’un objet est relativement
indépendante des autres objets ET du logiciel
en cours de construction
• Exemples: objets visuels dans les applications
fenêtrées (Windows, Linux, etc.)
Principes de la Programmation
Orientée Objet
Notion d’Objet

• Un objet rassemble des données et le


code travaillant sur ces données.
• La classe est un moule qui permet de
créer des objets.
• La classe décrit les objets, les objets
sont les instances de leur classe.
Classe et Objet

La classe décrit la structure interne de l’objet:


• Données qu’il regroupe
• Actions (procédures/fonctions) opérant sur ces données

Véhicule Nom de la classe


#NombreDeVéhicules : Entier
#Marque : Chaîne
#PuissanceFiscale : Entier Description des
#VitesseMaximale : Entier attributs
#VitesseCourante : Entier
+CréerVéhicule( )
+DétruireVéhicule( )
Description des
+Démarrer
méthodes = code
+Accélérer(Taux:Entier)
associé aux données
+Avancer( )
+Reculer( )
Attributs d’instance

• Les données Marque, PuissanceFiscale,


VitesseMaximale et VitesseCourante sont
représentatives d’un véhicule particulier.

• Chaque objet de type Véhicule aura sa propre


copie de ces données.

• Dans ce cas, une donnée est appelée attribut


d’instance.
Attributs d’instance

Classe Instances

Véhicule Marque=‘Lotus’
#NombreDeVéhicules : Entier PuissanceFiscale=11
#Marque : Chaîne VitesseMaximale=230
#PuissanceFiscale : Entier VitesseCourante=170
#VitesseMaximale : Entier
#VitesseCourante : Entier Instanciation
+CréerVéhicule( ) Marque=‘Peugeot’
+DétruireVéhicule( ) PuissanceFiscale=7
+Démarrer VitesseMaximale=160
+Accélérer(Taux:Entier) VitesseCourante=90
+Avancer( )
+Reculer( )
Attributs de classe

• L’attribut NombreDeVéhicules est chargé de


compter le nombre de véhicules présents à
un instant donné dans la classe.

• Il est incrémenté dans CréerVéhicule( ).

• Il est décrémenté dans DétruireVéhicule( ).

• Tous les objets partagent une copie unique


de cet attribut située au niveau de la classe.
Principes d’encapsulation

• Un objet rassemble en lui-même ses données


(les attributs) et le code agissant dessus (les
méthodes)
• Abstraction de données : la structure de
données de l’objet n’est pas visible de
l’extérieur. L’interface est constituée par les
méthodes.
• Abstraction procédurale : l’utilisateur de l’objet
n’a aucune information sur la mécanique interne
mise en œuvre dans cet objet (boîte noire)
Notion d’héritage

• Un constat : les systèmes réels se prêtent à


merveille à une classification hiérarchique des
éléments qui les composent.

• Le principe : un objet spécialisé hérite des


caractéristiques de l’objet le plus général auquel
il rajoute ses éléments propres.
Notion d’héritage

En terme de concepts objets :

• Une classe de base est associée au concept le


plus général (classe à la racine de l’arbre
hiérarchique des classes).

• Pour chaque concept spécialisé, on dérive une


classe du concept de base: toute classe dérive
d’une classe parent.
Exemple 1: les objets graphiques

ObjetGraphique
#Nombre : Entier
#Couleur : TypeCouleur
#X : Entier
#Y : Entier
#Epaisseur : Entier Classe de base :
+Créer( ) concept général
+Détruire( )
+getX( ) : Entier
+getY( ) : Entier
+setX(valeur : Entier )
+setY(valeur : Entier )
+DéplacerVers(dx,dy : Entier)
+Afficher( )
+Effacer( )
Exemple 1: les objets graphiques

ObjetGraphique

Classes dérivées :
Ligne concept spécialisé
Cercle
#Longueur : Entier
#Angle : Réel #Rayon : Entier

+Créer( ) +Créer( )
+Détruire( ) +Détruire( )
+getLongueur( ) : Entier +getRayon( ) : Entier
+setLongueur(valeur : Entier ) +setRayon(valeur : Entier )
+Afficher( ) +Afficher( )
+Effacer( ) +Effacer( )
Classes Abstraites

• Certains titres de classes sont en italique:


titre en italique = classe abstraite

• Classe abstraite = classe qui ne fournit pas


d’implémentation pour certaines de ses méthodes.

• Une classe abstraite ne peut pas être instanciée (pas


d’objets).

• Il appartient à ses classes dérivées de définir du code


pour chacune des méthodes abstraites.

→ C’est la base du polymorphisme.


Le polymorphisme

• Implémentations différentes, suivant la classe, de


méthodes déclarées dans une classe ancêtre.
Exemple: la méthode Afficher( ).

ObjetGraphique

+Afficher( )
Ligne
Méthode Ligne.Afficher( )
{ Tracer une droite;}
+Afficher( )

Cercle
Méthode Cercle.Afficher( )
{ Tracer un cercle;}
+Afficher( )
Le polymorphisme

• L’utilité du polymorphisme provient de la notion


sous-jacente de compatibilité descendante des
pointeurs:
un pointeur sur un objet d’une classe spécialisée
peut toujours être affecté à un pointeur d’une
classe ancêtre.

• Exemple: un dessin est une liste d’objets Ligne,


Cercle, … Comment afficher le dessin ?
Le polymorphisme (exemple)

Liste

Méthode Dessin.Afficher( ) {
déclarer objet : ObjetGraphique ;
Dessin pour i=0 à NombreObjets-1 faire {
lire objet en position i ;
+Afficher( ) appeler objet.Afficher( );
}
}

Sans le polymorphisme, il faudrait déclarer une variable locale


par type d’objet graphique, et avoir autant d’attribut liste que de
type d’objet graphique.
Exemple 2: modélisation d’un
parc de véhicules

• Une entreprise possède un parc de véhicules


regroupant des voitures, des camions, des
hélicoptères et des bateaux. Elle souhaite
disposer d’un modèle de fonctionnement de son
parc.

• Le but est de créer une hiérarchie permettant de


factoriser le plus possible les fonctionnalités de
chaque type de véhicule.
Exemple 2: modélisation
d’un parc de véhicules

Concepts communs :
Démarrer, Accélérer, Véhicule
Ralentir, Arrêter

VéhiculeRoulant Hélicoptère Bateau

Voiture Camion
Exemple 2: modélisation
d’un parc de véhicules
Une hiérarchie de classes
possible pour la modélisation
Véhicule d’un parc hétérogène de
véhicules après ajout des avions

VéhiculeRoulant VéhiculeAérien Bateau

Voiture Camion Hélicoptère Avion


Avantages de l’héritage

• Code de taille plus faible : factorisation des


comportements communs dans les classes les
plus générales.

• Seul le code spécifique reste à écrire dans les


classes dérivées.

• Modélisation naturelle – déboguage facilité.

• Code des classes les plus générales très utilisé


donc très fiabilisé.
Risques de l’héritage

• Une hiérarchie trop lourde: ex. dériver VoitureJaune


et VoitureRouge de la classe Voiture.

• L’héritage de construction:
Feuille2

Base Intermed1 Intermed2

Feuille1 Feuille3

• Les incohérences conceptuelles: hériter d’une


méthode qui n’a pas de sens dans la classe.
L’héritage multiple

• Extension du modèle d’héritage simple où l’on


autorise une classe à posséder plusieurs
classes mères.

• Tous les langages orientés objets ne l’autorise


pas.

• Le problème est contourné dans différents


langages par la notion d’interface. Ex. Objective
C, JAVA.
L’héritage multiple

ClasseA
VéhiculeRoulant Bateau

ClasseB ClasseC
Hovercraft

Héritage à répétition: ClasseD


ClasseD reçoit 2 copies de
sa classe ancêtre ClasseA
Interfaces

• Interface : « classe » sans attribut dont toutes


les méthodes sont abstraites.

• Une classe implémente une interface si elle


propose une implémentation pour chacune des
méthodes décrite dans l’interface.

• Chaque classe implémente autant d’interfaces


qu’elle le désire.
Interfaces

InterfaceA ClasseB

+MethodA1( ) +MethodB1( )
+MethodA2( ) +MethodB2( )
ClasseC possède en fin
de compte les méthodes:
MethodA1, MethodA2,
MethodB1, MethodB2
ClasseC

+MethodA1( )
+MethodA2( )
ClasseC implémente
MethodA1( ) et MethodA2( )
La relation d’association

• Si l’on peut dire que toute instance de la


classe A « communique avec » ou
« utilise » des instances de la classe B,
alors il y a association entre ces deux
classes.

ClasseA ClasseB
La relation d’agrégation

• L’agrégation est une association forte entre


deux classes.

• Si l’on peut dire que la classe A « est


composée de » ou « possède » la classe B,
alors on a agrégation.
ClasseA ClasseB
La relation d’agrégation

Moteur Chassis Roue

1 1 4

Voiture
1 1
Association et agrégation

Modèle 1:
Zoo

Cage 1 Gardien
*
nettoie
1 1 nourrit
contient

* Animal *
est contenu est nourri par
Association et agrégation

Modèle 2:
Zoo

Cage 1 Gardien
*
nettoie
1 1 nourrit
contient

* Animal *
est contenu est nourri par
Agrégation, héritage et interfaces

• Il est parfois possible de traduire en terme


d’agrégation des notions apparentées à
l’héritage multiple et à l’utilisation
d’interfaces.

• Exemple: on considère un système


militaire regroupant des avions et des
radars. Comment modéliser l’adjonction
d’un AWACS à ce système ?
Agrégation, héritage et interfaces
(exemple AWACS)

Solution 1: Héritage multiple


→ Dériver la classe AWACS d’avion et radar

Avion Radar

AWACS
Agrégation, héritage et interfaces
(exemple AWACS)

Solution 2: utilisation d’interfaces


→ 3 possibilités :
Avion Radar Machine Détecteur Machine
Détecteur Volante Volante

AWACS Radar AWACS Avion AWACS


Agrégation, héritage et interfaces
(exemple AWACS)

Solution 3: Utilisation d’agrégations


→ 3 possibilités
Avion Radar Radar Avion

AWACS Radar AWACS Avion AWACS


Logiciel orienté objet

• Classe: composant logiciel décrivant des objets.


• Objet: entité fondamentale rassemblant des données
ainsi que le code opérant sur ces données.
• Message: Moyen de communication entre objets. Dans
la plupart des langages orientés objets, l’invocation de
message se traduit par l’activation d’une méthode.
• Logiciel orienté objet: façon de concevoir le logiciel
orientée vers les données plutôt que sur les actions. Un
logiciel est alors vu comme une collection d’objets
communiquant par messages.
Les 3 principes fondamentaux
du paradigme objet

• Encapsulation :
1) Rassemblement des données et du code traitant ces
données dans une entité appelée objet.
2) Séparation nette entre partie publique (méthodes) et
partie privée (données, implémentation).

• Héritage : principe de généralisation/spécialisation dans


les arborescences de classe.

• Polymorphisme : Faculté présentée par certaines


méthodes dont le comportement est différent (malgré
une signature identique) selon l’objet auquel elles
s’appliquent.
UML
Unified Modeling Language
UML ?

• UML est un langage standard pour visualiser,


spécifier, construire et documenter un logiciel…
• … et plus généralement un système
• UML essaie de réunir plusieurs écoles:
- Concepts de modélisation de données
- Concepts de modélisation métier (workflow)
- Concepts de modélisation objet
• Il permet de transcender la notion de contraintes
d’implantation liées aux langages et aux
systèmes
Des modèles plutôt que du code !

• Un modèle est une simplification/abstraction de


la réalité
• Nous construisons donc des modèles afin de
mieux comprendre les systèmes que nous
développons
• Nous modélisons des systèmes complexes
parce que nous somme incapables de les
comprendre dans leur totalité
• Le code ne permet pas de simplifier/abstraire la
réalité
Comment modéliser ?
• Le choix d’un modèle influence fondamentalement la
manière d’aborder un problème et de définir une solution

• Tout modèle possède un « niveau de granularité » :


modèle très précis proche de la réalité / difficile à aborder
modèle simple éloigné de la réalité / simple à comprendre

• Un bon modèle doit rendre compte de la réalité (validité


de l’approximation)

• Un seul modèle est rarement suffisant. Les systèmes


d’une certaine complexité sont mieux approchés par
plusieurs modèles

⇒Diagrammes & Notations


Des méthodes de modélisation

• L’apparition du paradigme objet à permis


la naissance de plusieurs méthodes de
modélisation
– OMT, OOSE, Booch, Fusion, …
• Chacune de ces méthodes fournie une
notation graphique et des règles pour
élaborer les modèles
• Certaines méthodes sont outillées
Trop de méthodes…

• Entre 89 et 94 : le nombre de méthodes


orientées objet est passé de 10 à plus de 50
• Toutes les méthodes avaient pourtant
d’énormes points communs (objets, méthode,
paramètres, …)
• Au milieu des années 90, G. Booch, I. Jacobson
et J. Rumbaugh ont chacun commencé à
adopter les idées des autres. Les 3 auteurs ont
souhaité créer un langage de modélisation unifié
Historique
Définition en cours par une UML 2.0
commission de révision
Soumission à l’OMG UML 1.x 1999-2002

UML 1.2 Juin 1998


Standardisation par l’OMG
UML 1.1 Novembre 1997
Soumission à l’OMG
Septembre 1997
Soumission à l’OMG UML 1.0 Janvier 1997
Version bêta OOPSLA’96 UML 0.9 Juin 1996

OOPSLA’95 Méthode unifiée 0.8 Octobre 1995

Booch’93 OMT-2

Autres méthodes Booch’91 OMT-1 OOSE Partenaires


Aujourd’hui

• UML est le langage de modélisation orienté


objet le plus connu et le plus utilisé au monde

• UML s’applique à plusieurs domaines


– DOO, Deploiement, Spécifications, Organisations …

• UML n’est pas une méthode de gestion de projet


ou de développement
– RUP
Pourquoi utiliser UML ?

• Réfléchir
• Définir la structure « gros grain »
• Documenter
• Guider le développement
• Développer, Tester, Auditer
Une vue, Un diagramme

• Diagramme de classes / Class Diagram UML 1.x


– Classe, Opération, Attribut, Association, …
• Diagramme d’objet / Object Diagram
• Diagramme de cas d’utilisation / Use Case Diagram
– Cas d’utilisation, Acteur, ..
• Diagramme de séquence / Sequence Diagram
– Instance, message, relation
• Diagramme de collaboration / Collaboration Diagram
• Diagramme d’état / Statechart Diagram
• Diagramme d’activité / Activity Diagram
• Diagramme de composant / Component Diagram
• Diagramme de déploiement / Deployment Diagram
Les 9 diagrammes
Vues statiques et dynamiques

• Ces diagrammes sont subdivisés en des vues statiques


(qui représentent le système à modéliser au moyen de
diagrammes d'objets, de classes, de cas d'utilisation, de
composants, de déploiement) et des vues dynamiques
(qui montrent le fonctionnement du système au moyen
de diagrammes de séquence, de collaboration, d'états
transitions et d'activités).
Diagramme de Classe

• Un diagramme de classes exprime la structure


statique d’un système en terme de classes et de
relations (interactions).
Comp any

Pe rson

members
Company Employe e
0..1 *
Exemple de diagramme
de classe
Diagramme d’objets

• Représentation d’un contexte à l’aide


d’instances de classes et de relations
Diagramme de collaboration

• Mise en évidence des interactions entre


objets, numérotées selon leur ordre
d’occurrence.
Diagramme de cas d’utilisation

• Représentation sous forme d’actions et de


réactions le comportement du système du
point de vue de l’utilisateur
Exemple de cas d’utilisation
Scenarii et Cas d’utilisation

• Les scénarios sont des instances de cas


d’utilisation (en pratique, un chemin
possible)
• Ils sont classés en
- scénarios principaux : trame normale
- scénarios secondaires: cas alternatifs
• Exemple: JPlanning
Diagramme de Séquence

• Mise en évidence des interactions entre


objets et acteurs selon une logique
temporelle
Diagramme de Séquence
Diagramme d’état-transition

• Permet de décrire le comportement d’un


objet en terme d’états et d’évènements.
Visualisation selon la logique des
automates à états finis (transition =
évènement).
Diagramme d’état-transition

• Représentation des états:

• Représentation des transitions

• Exemple:
Diagramme d’Activité

• Variante des diagrammes d’état-transition,


destinée à représenter le comportement
interne d’une méthode.
Diagramme d’Activité

• Raccourci pour la visualisation des activités dans un


diagramme d’état-transition

• Représentation des flots


d’exécution et de leur
synchronisation
Diagramme de Composants

• Description de l’architecture physique d’une application en


terme de modules (fichiers sources, exécutables, librairies,
BdD, …).
Ils montrent la mise
en œuvre physique
des modèles de la
vue logique avec
l’environnement de
développement.
Diagramme de Dépoiement

• Disposition physique des différents matériels


(les nœuds) qui entrent dans la composition
d’un système et la répartition des exécutables
sur ces matériels.
Diagramme de Déploiement

Représentation basée sur :


Des nœuds (les dispositifs matériels)
Des composants (composants du logiciel)
Des artefacts (librairies, programmes externes)
Des relations entre ces éléments
Exemple de Diagramme
de Déploiement
Le standard UML

• définit précisément tous les éléments UML


et leurs relations : sémantique
• définit précisément une notation graphique
pour chaque éléments : notation

⇒ Qu’est ce qu’un outil UML standard ?


Le méta-modèle UML

• Le standard UML définit de manière


pseudo formelle la sémantique des
concepts UML en fournissant le méta-
modèle UML
– Plus de 50 concepts (méta-classes)
– Structuration en package (core, common
behavior, …)
– Aucune présence des diagrammes
Modéliser la sémantique

• Pourquoi ne pas faire un modèle représentant


les éléments UML : Un méta-modèle
generalization

Package Class Opération


0..1 * 0..1 *
0..1

Attribut

⇒ Moins d’Interprétation
Méta-modèle

• Le méta-modèle UML est censé définir la façon


dont sont stockés les modèles en mémoire

Bibliothèque 5 objets
• Bilbiothèque:Class
0..1 • Undefined:AssociationEnd
• Undefined:Association
• Undefined:AssociationEnd
* • Exemplaire:Class

Exemplaire
Notation

• La notation est la partie visible du


standard
• La sémantique des utilisateurs se base sur
la notation
• Le standard n’établit pas un lien précis
entre la notation et la sémantique
Outil UML standard

• Il est communément établi qu’un outil UML


standard est un outil qui
– Respecte intégralement la notation UML
• Même si tous les diagrammes ne sont pas
supportés
– Dispose d’un format de représentation interne
compatible avec le méta-modèle UML
standard
Du contemplatif au productif…

• Les modèles sont souvent utilisés pour


– Réfléchir, Définir la structure gros grain,
documenter
• Ils sont alors contemplatifs
– Ils ne permettent aucun gain significatif
• Il faut alors qu’ils deviennent productifs
– Permettre la génération automatique de
code, de déploiement, …
Eclipse avec
plug-in UML Omondo
• Respecte les standards UML (1.0 et 2.0)
• Synchronisation avec le code Java (à partir des
diagrammes de classes/objets)
• Gratuit !
ArgoUML
http://argouml.tigris.org

• Respecte les standards UML (1.0 et 2.0)


• Simplicité de l’utilisation
• Génération de code
• Léger et gratuit
Autres outils standards

• Rational Rose
– Outil historique, forte implantation, RUP
– http://www.rational.com
– Racheté par IBM

• Together
– Synchronisation multi-langages, extension vers produits Borland
– http://www.togethersoft.com
– Racheté par Borland

• Objecteering
– Synchronisation multi-langages, français
– http://www.objecteering.com

• Visio
– Outil non complet de microsoft
– http://www.microsoft.com/office/visio
La Programmation
Concurrente en Java
- Rappels sur les processus
- Définition des threads
- Création d’un thread
- Gestion de la vie d’un thread
- Synchronisation et Accès concurrent
- La communication entre threads
- Les « Démons »
Rappels sur les processus

De manière générale, un processus est un « programme en cours


d'exécution ».

Le système alloue de la mémoire pour chaque processus :


- segment de code (instructions)
- segment de données (allocation dynamique)
- segment de pile (variables locales et adresses de retour des
fonctions).

Le système associe à chaque processus des informations de


gestion : identifieur, descripteurs (fichiers ouverts, connexions
réseau), priorités, droits d'accès, etc.
Rappels sur les processus
Un processus peut être dans différents états :
• En exécution : le processus s'exécute sur un processeur du
système ;
• Prêt : le processus est prêt à s'exécuter, mais n'a pas le
processeur (occupé par un autre processus en exécution) ;
• Bloqué : il manque une ressource (en plus du processeur) au
processus pour qu'il puisse s'exécuter.
Rappels sur les processus

Un processus a besoin de ressources pour s'exécuter : mémoire,


processeur, mais aussi entrées/sorties (fichiers, communications
avec d'autres processus s'exécutant ou non sur la même machine).

Cas des ressources partagées :


- Le nombre de points d'accès d'une ressource est le nombre de
processus qui peuvent simultanément l'utiliser. Par exemple, une
imprimante ou un processeur sont des ressources à un seul point
d'accès.
- Pour chaque ressource partagée, il faut mettre en place une
politique de synchronisation.
Rappels sur les processus

On dit que des processus sont en exclusion mutuelle s'ils utilisent


la même ressource (dite ressource critique).

La section critique d'un programme est la suite d'instructions


pendant laquelle la ressource critique est utilisée.
Notion de Démon

Un démon est un processus qui s'exécute en tâche de fond.

Un démon est en général lancé lors de l'initialisation du système,


et s'exécute jusqu'à l'arrêt de celui-ci.

Il permet d'implémenter un service : service d'impression (prise


en compte des demandes d'impressions, gestion d'une file
d'attente pour partager l'imprimante), services Internet (ftp, mail,
http), gestion de certaines tâches du système d'exploitation etc.
Définition des threads
Un thread, parfois appelé ``processus léger'', est en quelque sorte
un processus à l'intérieur d'un processus. Les ressources allouées
à un processus (temps processeur, mémoire) vont être partagées
entre les threads qui le composent.

Un processus possède au moins un thread, qui exécute le


programme principal.

Contrairement aux processus, les threads partagent la même zone


mémoire (espace d'adressage), ce qui rend très facile (mais
dangereux) la communication entre threads.

Chaque thread possède son propre environnement d'exécution


(valeurs des registres du processeur) ainsi qu'une pile (variables
locales).
Définition des threads

L'utilisation des threads et le détail de leur comportement est


différente dans chaque système d'exploitation (Windows, Linux,
UNIX).

Certains langages, comme JAVA, 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 font partie intégrante du langage JAVA. Elles sont


gérées grâce à la classe Thread.
Quelques exemples
Applications réseau
- un thread s'occupe de l'accès réseau ;
- un thread s'occupe des interactions avec l'utilisateur ;
- un thread attend les connexions ;
- d’autres threads traitent des connexions déjà établies...

Logiciels de simulation
- un thread s'occupe de l’interface utilisateur (main) ;
- un ou plusieurs threads effectuent les calculs (simulations) ;
- un thread affiche les résultats (courbes, animations,…) ;

Traitements de texte
- un thread s'occupe de ce que tape l'utilisateur ;
- un thread est en charge de souligner les erreurs ;
- un thread se charge de l’aide contextuelle…
Quelques exemples

Cas des programmes JAVA

Quand on lance un programme Java (par l'intermédiaire de la


JVM), c’est en fait un processus qui est lancé.

Ce processus contient plusieurs threads :


- le thread principal (celui qui exécute le code à partir du
main),
- la gestion du garbage collector,
- les threads créés par votre code, etc.
Création d’un thread

Il existe 2 méthodes pour créer un thread :

- créer une classe qui hérite de la classe Thread

- déclarer une classe qui implémente l'interface Runnable

Thread AutreClasse Runnable Thread

MonThread MonThread
Création d’un thread

Méthode 1: créer une classe qui hérite de la classe Thread.

Cette classe doit surcharger la méthode run() de la classe Thread.

class MonThread extends Thread {


MonThread() {
... code du constructeur ...
}
public void run() {
... code à exécuter dans le thread ...
}
}
Création d’un thread
Méthode 1: créer une classe qui hérite de la classe Thread.

Une instance de Thread est lancée en appelant sa méthode start() :

MonThread p = new MonThread();


p.start();

L'appel de la méthode start() passe le thread à l'état « prêt », ce


qui lui permet de démarrer dès que possible.

La machine virtuelle JAVA décide du moment auquel le thread va


s'exécuter. Lorsque le thread démarre, JAVA appelle sa méthode
run() .

Un thread se termine lorsque sa méthode run() se termine.


Création d’un thread
Méthode 1: Exemple.
public class FirstThread extends Thread {
/** Un attribut propre à chaque thread */
private String threadName;
/** Création et démarrage automatique du thread */
public FirstThread(String threadName) {
this.threadName = threadName; this.start();
}
/** Le but d'un tel thread est d'afficher 500 fois
* son attribut threadName. Notons que la méthode
* sleep() peut déclancher des exceptions. */
public void run() {
try {
for(int
for i=0;i<500;i++) {
System.out.println( "Thread : " + this.threadName + " - itér : " + i );
Thread.sleep(30);
}
}
catch (InterruptedException exc) {
exc.printStackTrace(); }
}
Création d’un thread

Méthode 1: Exemple.


/** Le point de démarrage du programme.
* Notez bien que nous lançons deux threads et que
* chacun d'eux possède une donnée qui lui est propre. */
static public void main(String argv[]) {
FirstThread thr1 = new FirstThread("thr1");
FirstThread thr2 = new FirstThread("thr2");
}
}
Création d’un thread
Méthode 2: implémenter l'interface Runnable.

Cette interface déclare seulement une méthode : run(). La classe


Thread a un constructeur qui prend en argument une instance
implémentant Runnable.

class MonThread2 implements Runnable {


MonThread2() {
... code du constructeur ...
}
public void run() {
... code à exécuter dans le thread ...
}
}
Création d’un thread

Méthode 2: implémenter l'interface Runnable.

Pour créer et lancer un thread, on crée d'abord une instance de la


classe MonThread2, puis une instance de Thread sur laquelle on
appelle la méthode start() :

MonThread2 p = new MonThread2() ;


Thread th = new Thread(p) ;
th.start() ;
Création d’un thread

Méthode 2: exemple.

/** Second test de la classe Thread en utilisant la


* technique qui consiste à implémenter l'interface
* java.lang.Runnable */
public class SecondThread implements Runnable {
/** Un attribut partagé par tous les threads */
private int counter;
/** Démarrage de cinq threads basés sur un même objet */
public SecondThread (int counter) {
this.counter = counter;
// On démarre cinq threads sur le même objet
for (int i=0;i<5;i++) {
(new Thread(this)).start();
}
}
Création d’un thread
Méthode 2: exemple.

/** Chaque thread affiche 500 fois un message. Un
* unique compteur est partagé pour tous les threads.
* Il y a cinq threads. Le dernier affichage devrait
* donc être "Valeur du compteur == 2499". */
public void run( ) {
try {
for(int
for i=0;i<500;i++) {
System.out.println( "Valeur du compteur == "
+ counter++ );
Thread.sleep(30);
}
}
catch (InterruptedException exception) {
exception.printStackTrace();
}
}
Création d’un thread

Méthode 2: exemple.


/** Le main crée un unique objet sur lequel vont se
* baser cinq threads. Il vont donc tous les cinq se
* partager le même attribut. */
static public void main(String argv[]) {
SecondThread p1 = new SecondThread(0);
}
}
Un exemple: L’Horloge

Objectif :
créer une classe "Horloge" permettant d'afficher l'heure en
cours.

Méthode:
• Dériver la classe java.awt.Label.
Notre objet est donc un label que l'on peut ajouter à une interface graphique
et sur lequel nous pouvons notamment changer la couleur d'arrière plan.

• Fournir la méthode run en implémentant l'interface Runnable.


Il ne reste plus qu'à instancier et lancer un objet de type Thread sur notre
label pour pouvoir changer son contenu à chaque seconde.
Un exemple: L’Horloge
import java.util.*;
import java.text.*;
import java.awt.*;
public class Horloge extends Label implements Runnable {
private DateFormat timeFormat = DateFormat.getTimeInstance();
public void run() {
try {
while(true)
while {
this.setText(timeFormat.format(new
this new Date()));
Thread.sleep(1000);
}
}
catch(Exception
catch exception) { exception.printStackTrace(); }
}

public Horloge( ) {
this.setText(timeFormat.format(new
this new Date()));
this.setAlignment(
this Label.CENTER );
(new
new Thread(this
this)).start();
this
}
}
Un exemple: L’Horloge
Utilisation de la classe Horloge avec une Applet :

import java.awt.*;
import java.applet.*;
public class Start extends Applet {
public void init() {
this.setLayout(new
this new FlowLayout());
Label clock1 = new Horloge();
clock1.setBackground(newnew Color(200,200,255));
Label clock2 = new Horloge();
clock2.setBackground(newnew Color(200,255,200));
Label clock3 = new Horloge();
clock3.setBackground(newnew Color(255,200,200));
this.add(clock1);
this
this.add(clock2);
this
this.add(clock3);
this
}
}
Cycle de vie d’un thread
Un thread peut passer par différents états, tout au long de son
cycle de vie. Le diagramme suivant illustre ces différents stades
ainsi que les différentes transitions possibles :
Nouveau Mort

Stop( )

Start( ) Stop( ), fin run( )

Exécutable

Wait(), sleep() Notify(), notifyAll(),


fin sleep( )

Non Exécutable
Cycle de vie d’un thread

Etat « nouveau » :
C'est l'état initial après l'instanciation du thread. A ce stade, le
thread est opérationnel mais celui-ci n'est pas encore actif.

Etat « exécutable » :
Un thread est dans un état exécutable à partir du moment où il a
été lancé par la méthode start() et le reste tant qu'il n'est pas sorti
de la méthode run(). Dès que le système le pourra, il donnera du
temps d'exécution au thread.
Cycle de vie d’un thread
Etat « en attente » :
Un thread en attente n'exécute aucun traitement et ne consomme
aucune ressource CPU. Il existe plusieurs manières de mettre un
thread en attente. Par exemple :
♦ Appeler la méthode Thread.sleep(temps en millisecondes).
♦ Appeler la méthode Object.wait() ;
♦ Accéder à une ressource bloquante (Flux, accès en base de
données, etc…)
♦ Accéder à une instance sur laquelle un verrou a été posé.

Etat « terminé » :
Un thread dans un état terminé est un thread qui est sorti de sa
méthode run() soit de manière naturelle, soit de manière subite
(Exception non interceptée).
Gestion de la vie d’un thread
La classe Thread offre un certain nombre de méthodes pour
contrôler le comportement des threads :

• sleep( long ms ) : le thread courant cesse de s’exécuter pendant


ms millisecondes (d'autres threads à l'état prêt peuvent alors
s'exécuter). Attention: ce thread ne perd pas ses verrous…

• isAlive() : retourne vrai si le thread auquel on applique la


méthode est vivant (c'est à dire à été démarré par start() et que sa
méthode run() n'est pas encore terminée). Le thread vivant est
donc prêt, bloqué ou en cours d'exécution.

• getPriority() et setPriority( int prio ) permettent de manipuler


la priorité du thread.
Gestion de la vie d’un thread
La notion de priorité :
Il est possible de changer la priorité d'un thread afin qu‘il ait une
priorité particulière pour accéder au processeur. Le thread de plus
forte priorité accède plus souvent au processeur. Pour changer la
priorité d'un thread :
public void setPriority (int prio);
pour visualiser la priorité :
public int getPriority ();
Il existe des constantes prédéfinies pour les valeurs des priorités :
• public final static int MAX_PRIORITY;
• public final static int MIN_PRIORITY;
• public final static int NORM_PRIORITY;
Il n'est pas possible de sortir de ces bornes sous peine de recevoir
une exception IllegalArgumentException.
Gestion de la vie d’un thread
La notion de priorité : Exemple.
public class ThreadPriority extends Thread {
private int counter = 0;
public void run() { while(true)
while counter++; }
public int getCounter() { return this.counter;
this }
public static void main(String args[]) throws Exception {
ThreadPriority thread1 = new ThreadPriority();
ThreadPriority thread2 = new ThreadPriority();
ThreadPriority thread3 = new ThreadPriority();
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.getCounter());
System.out.println("Thread 2 : counter == " + thread2.getCounter());
System.out.println("Thread 3 : counter == " + thread3.getCounter());
}
}
Les groupes de threads
Objectif: « Enregistrer » différents threads dans un groupe.

Avantage: pouvoir invoquer un ordre sur l'ensemble des threads


du groupe, ce qui peut sérieusement simplifier le code.

Méthode:
• Instancier un objet de la classe ThreadGroup
• Attacher un thread à ce groupe via un constructeur de la classe
Thread :
Thread(ThreadGroup group, Runnable target)

Remarques:
- Par la suite, un thread ne pourra en aucun cas changer de
groupe.
- Les noms des méthodes de contrôle d’un ThreadGroup sont
identiques à ceux d’un Thread (start( ), sleep( ), …).
La synchronisation
Chaque instance de la classe thread possède ses propres attributs.
Pour partager une variable entre plusieurs threads, on peut
recourir à une variable de classe. Exemple :
public class exemplePartage extends Thread {
private static String chaineCommune = "";
private String nom;
exemplePartage ( String s ) { nom = s; }
public void run() {
chaineCommune = chaineCommune + nom;
}
public static void main(String args[]) {
Thread T1 = new exemplePartage( "T1" );
Thread T2 = new exemplePartage( "T2" );
T1.start(); T2.start();
System.out.println( chaineCommune );
}
}
La synchronisation
Le programme crée trois threads : le thread principal (qui exécute
main() ), et deux threads T1 et T2 créés explicitement.
A l'exécution, on observe :

• Aucun affichage : le thread principal affiche chaineCommune


avant que le système n'ait donné la main aux threads T1 et T2.

• T1 : au moment de l'affichage, le thread T1 s'est exécuté, mais


pas T2.

• T2 : idem;

• T1T2 ou T2T1 : les deux threads se sont exécutés, dans un ordre


arbitraire.
La synchronisation
Les résultats précédents font apparaître le besoin de mécanismes
permettant de synchroniser les traitements effectués par des
threads.

Un problème majeur: l’accès concurrent

Exemple:
Deux threads T1 et T2 accèdent à une même variable partagée
compte, travaillent sur une copie locale tmp qui est incrémentée
avant d'être réécrite dans compte.
Un appel à sleep() simule un traitement long. Il augmente la
probabilité que le système bascule d'une tâche à l'autre durant
l'exécution de run() .
La synchronisation
public class exempleConcurrent extends Thread {
private static int compte = 0;
public void run() {
int tmp = compte;
try { Thread.sleep(1); // ms }
catch (InterruptedException e) {
System.out.println("Erreur!\n"); return;
}
tmp = tmp + 1;
compte = tmp;
}
public static void main(String args[]) throws InterruptedException {
Thread T1 = new exempleConcurrent();
Thread T2 = new exempleConcurrent();
T1.start();T2.start();
T1.join(); T2.join();
System.out.println( "compteur=" + compte );
}
}
La synchronisation
Résultats possibles:
La synchronisation
L'environnement Java offre un premier mécanisme de
synchronisation : les verrous (locks en anglais):

→ Chaque objet Java possède un verrou, et un seul thread à la


fois peut verrouiller un objet.

→ Si d'autres threads cherchent à verrouiller le même objet, ils


seront endormis jusqu'à que l'objet soit déverrouillé par le
thread qui l’a verrouillé.

Cela permet de mettre en place ce que l'on appelle une section


critique.
La synchronisation
Les problèmes d'accès concurrents se règlent en JAVA grâce à
cette méthode de verrouillage.

Le mot clé synchronized permet de déclarer qu'une méthode ou


un bloc d'instructions est critique : un seul thread à la fois peut se
trouver dans une partie synchronisée sur un objet.

Il y a deux façons de définir une section critique:


- Synchroniser un ensemble d'instructions dans un objet
synchronized(object)
synchronized {
// Instructions de manipulation d'une ressource partagée
}
- Synchroniser directement l'exécution d'une méthode pour une
classe donnée
public synchronized void meth(int param) {
// Le code de la méthode synchronizée
}
La synchronisation
Etude d’un exemple : plusieurs threads tracent des pixels en
parallèle de manière à remplir une surface rectangulaire.

Pour que le programme se déroule normalement, il faut que


l'appel à la méthode de tracé soit synchronisé. Sinon, certains
pixels peuvent être sautés.

Description:
L'interface graphique de l'applet présente deux boutons et une
zone de dessin. Les deux boutons permettent de lancer le tracé
dans la zone de dessin. Mais l'un des boutons lance le tracé sans
synchronisation alors que l'autre le fait en synchronisant les
threads.
La synchronisation

Code de l’exemple (1) :


import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Synchronized extends Applet
implements ActionListener, Runnable {
private Panel pnlButtonBar = new Panel();
private Button btnStartNormal = new Button("Non synchronisé");
private Button btnStartSynchronized = new Button("Synchronisé");
private Canvas cnvGraphic = new Canvas();
private Label lblStatus = new Label("Choisissez un mode d'exécution");

private boolean drawMode = false;


private int x, y;

La synchronisation
Code de l’exemple (2) :
public void init() {
pnlButtonBar.setLayout(new new FlowLayout());
pnlButtonBar.add(btnStartNormal);
pnlButtonBar.add(btnStartSynchronized);
btnStartNormal.addActionListener(this);
btnStartSynchronized.addActionListener(this);
this.setLayout(new
this new BorderLayout());
this.add(pnlButtonBar,
this BorderLayout.NORTH);
cnvGraphic.setBackground(Color.white);
this.add(cnvGraphic,
this BorderLayout.CENTER);
this.add(lblStatus,
this BorderLayout.SOUTH);
}

public void plot() {


this.cnvGraphic.getGraphics().drawLine(this
this this.x,this
this this.y,this
this this.x,this
this this.y);
this
if (++this
this.x
this >= 300) { this.x=0;
this this.y++;
this }
}

La synchronisation
Code de l’exemple (3) :
public void run() {
while(this
while this.y
this < 100) {
if (drawMode) {
synchronized(this
synchronized this)
this { this.plot();
this }
}
else { this.plot();
this }
}
}

public void actionPerformed(ActionEvent event) {


this.drawMode
this = (event.getSource() == btnStartSynchronized );
this.cnvGraphic.repaint();
this
this.x
this = this.y
this = 0;
(new
new Thread(this
this)).start();
this
(new
new Thread(this
this)).start();
this
(new
new Thread(this
this)).start();
this
}
}
Graphismes et
Animations
- Dessiner en Java
- Technique du double buffering
- Exemple: Horloge.java
Dessiner en Java
Il y a principalement deux cas où un programmeur est amené à
dessiner :
- la réalisation d'un composant graphique non-standard,
- la création d'une image à partir d’objets et de données.

Le dessin sera assuré par la classe java.awt.Graphics:


- pour dessiner dans un composant graphique, l'instance est donnée
en paramètre de la méthode paint() – package AWT ou
paintComponent() – package SWING;
- pour le dessin dans une image, on crée un objet java.awt.Image,
puis on utilise la méthode getGraphics() pour récupérer l'objet
Graphics et dessiner dans l'image.
Attention !
Vous ne devez en aucun cas essayer de construire
une instance de la classe Graphics.
Package java.awt et javax.swing

• awt : 1ère boite à outil de java


– Éléments de base
• Component (et Graphics)
• Container
• Layout (LayoutManager)
• swing : depuis jdk 1.2
– Enrichissement des comportements
– faire que tout fonctionne de manière identique
partout
Classe Component

• Définit un élément graphique


– Une Dimension
• getSize retourne une Dimension
• setSize avec une Dimension ou deux entiers
– Une position
• getLocation retourne un Point
• setLocation avec un Point ou deux entiers
– Coordonnées
• Origine au coin supérieur gauche
• x (width) vers la droite et y (height) vers le bas
– Méthode public void paint(Graphics g)
Classe Graphics

• Instance de Graphics = Contexte graphique d’un


composant

• On ne dessine par sur un composant mais sur


son contexte graphique
– Changer de crayon : setColor()
– drawRect(), drawOval(), drawPolygon(), drawString(),
fillRect(), fillOval()
– drawImage(img, x, y, ImageObserver)
Classe Graphics - Exemple
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import javax.swing.JPanel;

public class JCanvas extends JPanel {

/**
* This is the default constructor
*/
public JCanvas() {
super();
this.setSize(300, 200);
}

public void paint(Graphics g) {


Color c = g.getColor();
g.setColor(Color.RED);
g.fillRect(10,10,80,80);
g.setColor(Color.BLUE);
g.fillOval(150,50,80,80);
g.setColor(c);
}

}
Une animation simple

• Créer une classe héritant d’un composant


graphique et implémentant la classe Runnable

• Implémenter sa méthode paint() (tracé du


dessin)

• Implémenter sa méthode run(), qui appelle


régulièrement repaint()
Un premier exemple…
public class JAnimatedSimple extends JPanel implements Runnable {
private int it;
private Dimension dim;

public JAnimatedSimple() {
super();
this.setSize(300, 200);
}

public void run() {


it=0;
while (true) {
if(it==90) {it=0;} else {it++;}
repaint();
try { Thread.sleep(30); } catch (InterruptedException e) { }
} /**
} * This method initializes jAnimatedSimple1
*
public void paint(Graphics g) { * @return dessinModel.JAnimatedSimple
dim = getSize(); */
Color c = g.getColor(); private JAnimatedSimple getJAnimatedSimple1() {
//nettoie le canvas if (jAnimatedSimple1 == null) {
g.clearRect(0, 0, dim.width, dim.height); jAnimatedSimple1 = new JAnimatedSimple();
//passe la couleur active à rouge jAnimatedSimple1.setPreferredSize(new Dimension(10, 150));
g.setColor(Color.RED); if (tr == null) {
//trace le rectangle tr = new Thread(jAnimatedSimple1);
g.fillRect(Math.round(10+it),Math.round(10+it),20,20); tr.start();
g.setColor(c); }
} }
} return jAnimatedSimple1;
}
Le double buffering

• Permet d’éviter le scintillement pour des tracés


assez longs à réaliser (flicker free animation)

• La technique du double buffering consiste à:


– tracer l’image dans un composant non visuel
(Image ou BufferedImage par exemple)
– Copier cette image sur le contexte graphique du
composant affiché
Un second exemple…
public class JAnimatedCanvas extends JPanel implements Runnable {
private int it;
private Dimension dim;
private Image imgTmp; // double-buffer
private Graphics gTmp; // contexte graphique du double-buffer

public void run() {


it=0;
while (true) {
if(it==90) {it=0;} else {it++;}
repaint();
try { Thread.sleep(50); } catch (InterruptedException e) { }
}
}
public JAnimatedCanvas() {
super();
this.setSize(300, 200);
setBackground(Color.white);
}
public void buildBuffer() {
dim = getSize();
imgTmp = createImage(dim.width, dim.height);
gTmp = imgTmp.getGraphics();
}
public void paint(Graphics g) {
buildBuffer();
Color c = gTmp.getColor();
gTmp.clearRect(0, 0, dim.width, dim.height);
gTmp.setColor(Color.RED);
gTmp.fillRect(Math.round(10+it),Math.round(10+it),10,10);
gTmp.setColor(c);
g.drawImage(imgTmp,0,0,this);
}
}
Exemple: Horloge.java
Etude d’un exemple :
Construction d'une animation sous forme d'une applet java. Dans
l'exemple l'animation sera constituée par le mouvement des
aiguilles d'une horloge.
Utilisation des threads et de la technique du double buffering.

Allure de l’horloge:

Voir à l’adresse: http://chgi.developpez.com/java/applet/


Exemple: Horloge.java

Description:
la classe se nomme Horloge , elle hérite de la classe
java.applet.Applet et utilise l'interface Runnable pour le thread.

Utilisation d’un thread:


Le rafraîchissement du dessin est fait toutes les secondes par
l'appel de la méthode repaint(). Utilisation d’un thread pour éviter
de bloquer l'affichage de l'applet pendant la pause
"Thread.sleep(1000)".

Technique du double buffering:


Le dessin de l'horloge est fait sur une image en mémoire
imgTmp. Elle est dessinée en une seule passe sur la surface de
dessin de l'applet à l'aide de la méthode drawImage.
Horloge.java
import java.awt.*;
import java.util.*; //pour Calendar
import java.applet.*;
public class Horloge extends Applet implements Runnable{
Thread tr;
Image imgTmp; Déclaration
Graphics gTmp; du buffer
public Horloge() {
if (tr == null)
null {
tr = new Thread(this
this);
this
tr.start();
}
}

public void run() {


while (true
true)
true {
repaint();
try { Thread.sleep(1000); }
catch(InterruptedException
catch e){ }
}
}
Horloge.java

public void init() {
Dimension dim = getSize();
imgTmp = createImage(dim.width, dim.height);
Création
gTmp = imgTmp.getGraphics();
setBackground(Color.white); du buffer
}

public void update(Graphics g) { paint(g); }


Surface de dessin
public void paint(Graphics gsp) {
Calendar d = Calendar.getInstance(); qui sera affichée
int[]
int tPolygonX = new int[4];
int
int[]
int tPolygonY = new int[4];
int
Color cFond = new Color(232,232,232);
double n,z,u,x0,y0,x1,y1,x2,y2,x3,y3;
int h = d.get(Calendar.HOUR);
int m = d.get(Calendar.MINUTE);
int s = d.get(Calendar.SECOND);

Horloge.java

gTmp.setPaintMode();
gTmp.setColor(cFond); gTmp.fillOval(2,2,118,118);
gTmp.setColor(Color.black); gTmp.drawOval(2,2,118,118);
gTmp.drawString("12",55,16); gTmp.drawString("6",58,116);
gTmp.drawString("3",108,66); gTmp.drawString("9",8,66);
//Aiguille des secondes
n = s*200/60; z = n/100*Math.PI; u = (n+50)/100*Math.PI;
x0 = Math.sin(z)*50; y0 = -Math.cos(z)*50;
x1 = -Math.sin(z)*10; y1 = Math.cos(z)*10;
x2 = Math.sin(u)*2; y2 = -Math.cos(u)*2;
x3 = -Math.sin(u)*2; y3 = Math.cos(u)*2;
tPolygonX[0] = (int
int)x1+60;
int tPolygonX[1] = (int
int)x2+60;
int
tPolygonX[2] = (int
int)x0+60;
int tPolygonX[3] = (int
int)x3+60;
int
tPolygonY[0] = (int
int)y1+60;
int tPolygonY[1] = (int
int)y2+60;
int
tPolygonY[2] = (int
int)y0+60;
int tPolygonY[3] = (int
int)y3+60;
int
gTmp.setColor(Color.red);
gTmp.fillPolygon(tPolygonX, tPolygonY, 4);
gTmp.setColor(Color.black);
gTmp.drawPolygon(tPolygonX, tPolygonY, 4);

Horloge.java

//Aiguille des minutes
n = m*200/60;
z = n/100*Math.PI;
u = (n+50)/100*Math.PI;
x0 = Math.sin(z)*50; y0 = -Math.cos(z)*50;
x1 = -Math.sin(z)*10; y1 = Math.cos(z)*10;
x2 = Math.sin(u)*4; y2 = -Math.cos(u)*4;
x3 = -Math.sin(u)*4; y3 = Math.cos(u)*4;
tPolygonX[0] = (int
int)x1+60;
int tPolygonX[1] = (int
int)x2+60;
int
tPolygonX[2] = (int
int)x0+60;
int tPolygonX[3] = (int
int)x3+60;
int
tPolygonY[0] = (int
int)y1+60;
int tPolygonY[1] = (int
int)y2+60;
int
tPolygonY[2] = (int
int)y0+60;
int tPolygonY[3] = (int
int)y3+60;
int
gTmp.setColor(Color.yellow);
gTmp.fillPolygon(tPolygonX, tPolygonY, 4);
gTmp.setColor(Color.black);
gTmp.drawPolygon(tPolygonX, tPolygonY, 4);

Horloge.java

//Aiguille des heures
n = h*200/12 + m*200/60/12;
z = n/100*Math.PI;
u = (n+50)/100*Math.PI;
x0 = Math.sin(z)*35; y0 = -Math.cos(z)*35;
x1 = -Math.sin(z)*10; y1 = Math.cos(z)*10;
x2 = Math.sin(u)*4; y2 = -Math.cos(u)*4;
x3 = -Math.sin(u)*4; y3 = Math.cos(u)*4;
tPolygonX[0] = (int
int)x1+60;
int tPolygonX[1] = (int
int)x2+60;
int
tPolygonX[2] = (int
int)x0+60;
int tPolygonX[3] = (int
int)x3+60;
int
tPolygonY[0] = (int
int)y1+60;
int tPolygonY[1] = (int
int)y2+60;
int
tPolygonY[2] = (int
int)y0+60;
int tPolygonY[3] = (int
int)y3+60;
int
gTmp.setColor(Color.green);
gTmp.fillPolygon(tPolygonX, tPolygonY, 4);
gTmp.setColor(Color.black);
gTmp.drawPolygon(tPolygonX, tPolygonY, 4);

Horloge.java


gsp.drawImage(imgTmp,0,0,this
this);
this
Copie du buffer sur
} la surface affichée
}
Le Traitement
des Exceptions
Le mécanisme des exceptions permet de gérer les
erreurs en simplifiant l’écriture du code :
- il évite les cascades de « if » imbriqués
- il évite la transmission d’indicateurs de succès ou
d’échec des méthodes.

Ce mécanisme repose sur la classe « Exception »,


ancêtre de toutes les erreurs pouvant être récupérées
dans le programme.
Le traitement des erreurs
Un exemple: la classe « Cercle »
class Cercle {
private int rayon ;
private int x , y ;
public Cercle(int x , int y , int rayon) {
setRayon(rayon) ;
this.x=x ; this.y=y ;
}
public void setRayon(int rayon) {
if (valeur<=0) {
System.out.println(‘le rayon doit être >= à 0’);
System.exit(1);}
this.rayon=rayon;
}
}
La solution proposée est simple mais trop brutale : une simple
erreur de saisie déclenche l’arrêt du programme…
Le traitement des erreurs
Une autre possibilité :
class Cercle {

public boolean setRayon(int rayon) {
if (valeur<=0) return false
else {
this.rayon=rayon;
return true;
}
}

}
Cette solution permet au programme de savoir que quelque
chose ne va pas sans le stopper.
Mais cela ne permet pas de connaître « où et quoi » (d’autres
méthodes peuvent retourner false).
Le traitement des erreurs

Le mécanisme des exceptions tel qu’il est proposé par JAVA


est prévu pour traiter ce type de problème.

Il suppose l’utilisation de toute une hiérarchie de classes basée


sur la super-classe « Exception », et de mots-clefs spécifiques
(« throws », « throw »).

Il permet de distinguer clairement l’endroit du programme où


l’erreur peut arriver de l’endroit du programme où l’erreur est
traitée.
Le traitement des erreurs
Possibilités de traitement de l’erreur :
- soit ignorer l’erreur, cette dernière est alors traitée
automatiquement et peut conduire à une erreur fatale;
- soit traiter l’erreur.

Erreur !

Traitement Traitement
Auto.

Erreur Récupération Propagation


Fatale
Le traitement des erreurs

L’erreur est récupérée :


l’erreur est traitée directement par la méthode dans laquelle
elle s’est produite

L’erreur est propagée :


la méthode dans laquelle l’erreur s’est produite n’est pas
compétente pour la traiter; elle est transmise (=propagée) vers
le niveau supérieur (méthode appelante), et ainsi de suite.
Lever une exception
Création d’une nouvelle classe :
class RayonException extends Exception {
public RayonException (string message) {
super(message);
}
}
Modification de la classe « Cercle » :
class Cercle {

public void setRayon(int rayon) throws RayonException{
if (valeur<=0) {
throw new RayonException(‘Le rayon doit être >= à 0’);
}
this.rayon=rayon;
}

}
Lever une exception

♦ La classe « Exception »
Elle possède:
- un attribut de type string, qui spécifie le message d’erreur
associé – spécifié comme paramètre du constructeur ;
- un attribut qui représente une photo de la pile d’exécution au
moment où l’erreur a été signalée ;

Un objet de type Exception est un signal d’erreur: il est créé


avec un « new » lorsque l’erreur survient.
Propagation d’une exception
♦ « throws » : propagation de l’exception
précise que la méthode est susceptible de déclencher une
exception du type qui suit throws. Cette indication est
obligatoire en Java, à partir du moment où l'exception en
question n'est pas traitée par la méthode elle-même.

Dans l’exemple précédent:


La méthode setRayon( ) est susceptible de déclencher une
exception de type RayonException, qui sera alors propagée
vers la méthode appelante.
Récupération d’une exception
Pour gérer convenablement une exception il faut:
1. inclure dans un bloc try les instructions dans lesquelles on
risque de voir déclenchée une exception ;
2. faire suivre ce bloc des différents gestionnaires d'exception
possibles.
try {
… //instructions pouvant provoquer des erreurs à récupérer
}
catch( Exception1 e) {
… //instructions gérant les erreurs de type Exception1
}
catch( Exception2 e) {
… //instructions gérant les erreurs de type Exception2
}
Remarque: on dit aussi capture (catch=attraper) d’une
exception
Récupération d’une exception
Lorsqu’une Exception apparaît dans un bloc try:

- L’interpréteur abandonne l’exécution du code restant dans


try;

- l'interpréteur cherche un bloc catch correspondant au type


d’exception levée;

- s'il ne trouve aucun bloc catch correspondant, l'exception est


propagée dans le bloc de niveau supérieur, et ainsi de suite
jusqu'au bloc de la classe qui par défaut enverra l'exception à
l'interpréteur → message d'alerte standard;

- si un bloc catch est trouvé, celui-ci gèrera l'exception à votre


façon, et elle ne sera plus propagée.
Exemple: automates
Exemple simple: déclenchement et traitement d’une seule
exception

class ErrPos extends Exception {


public ErrPos () {
super (‘Abscisse ou ordonnée négative !’);
}
}

class Objet {
protected int posX , posY;
public Objet(int x, int y) throws ErrPos{
if ((x<0)||(y<0)) { throw new ErrPos(); }
posX=x; posY=y;
}
public void affiche() {
System.out.println(‘ posX=‘+posX+’ posY=‘+posY);}
}
Exemple: automates

public class Carte {


public static void main(String args []) {
try {
Objet o=new Objet(2,4);
o.affiche();
Objet o=new Objet(-2,4);
o.affiche();
}
catch(ErrPos e) {
System.out.println(‘Erreur de construction’);
System.exit(-1);
}
}

}
Exemple: automates
Exemple plus complet: déclenchement et traitement de 2
exceptions
class ErrPos extends Exception {
public ErrPos () {
super (‘Abscisse ou ordonnée négative !’);
}
}

class ErrDepl extends Exception {


public int x , y ;
public ErrDepl (int x, int y) {
super (‘Déplacement hors de la carte !’);
this.x = x;
this.y = y;
}
}
Exemple: automates

class Automate extends Objet {


public void deplace(int dx, int dy) throws ErrDepl {
int xtmp=posX+dx;
int ytmp=posY+dy;
if ((xtmp<0)||(ytmp<0)) {throw new ErrDepl(xtmp,ytmp);}
PosX=xtmp;
PosY=ytmp;
}
}

Remarque:
la classe Automate hérite de la classe Objet. Le constructeur
d’Automate est le constructeur d’Objet, et Automate dispose
des attributs posX et posY.
Exemple: automates
public class Carte {
public static void main(String args []) {
try {
Automate a=new Automate(2,4);
a.affiche();
a.deplace(-1,-5);
a.affiche();
}
catch(ErrPos e) {
System.out.println(‘Erreur de construction’);
System.exit(-1);
}
catch(ErrDepl e) {
System.out.println(‘Erreur de déplacement’);
System.out.println(‘Position souhaitée: (’+e.x+’,’+e.y+’)’);
System.exit(-1);
}
}
}
Poursuite de l’exécution

Dans tous les exemples précédents, le gestionnaire


d'exception met fin à l'exécution du programme en appelant
la méthode System.exit( ).

Cela n'est pas une obligation !

Après l'exécution des instructions du gestionnaire,


l'exécution se poursuit simplement avec les instructions
suivant le dernier bloc catch associé au bloc try.
Hiérarchie des classes Exception

Throwable
String (message d'erreur)

Exception Error

OutOfMemoryError

RunTimeException

VosExceptions... NullPointerException
ClassCastException
...
Hiérarchie des classes Exception

• Throwable est la classe de base, à partir de laquelle


vont dériver toutes les exceptions.

• Error gère les erreurs liées à la machine virtuelle


(LinkageError, ThreadDeath etc.).

• Exception contient l'ensemble des exceptions gérées par


le programmeur (ArithmeticException etc.).

• RunTimeException regroupe les erreurs de base


(ArithmeticException, ...)
Hiérarchie des classes Exception

• Error et RunTimeException appartiennent à la


catégorie uncheked : throws n'est pas nécessaire à coté de
la méthode qui lance une exception de cette catégorie là.

• Toutes les autres exceptions (y compris celles créées par


le programmeur) appartiennent à la catégorie checked où
l'utilisation de throws est exigée.
Les « RunTimeException »
La classe RuntimeException dérive de la classe Exception.
Quelques exceptions dérivant de RuntimeException :

• java.lang.ArithmeticException : Une exception est survenue sur une


opération arithmétique, comme une division d'un entier par zéro.
• java.lang.ArrayStoreException : Tentative de stocker dans un tableau un
élément qui n'est pas du type des éléments du tableau ou castable dans ce
type.
• java.lang.ClassCastException : Tentative de cast d'un objet dans un type
incorrect.
• java.lang.IllegalArgumentException : Une méthode a été appelée avec
un mauvais argument ou invoquée sur un mauvais objet. Les classes
suivantes dérivent de cette classe d'exception :
• java.lang.IllegalThreadStateException : Un thread était dans un état
inadéquat pour l'opération requise.
• java.lang.NumberFormatException : Tentative de convertir dans un type
numérique une chaîne de caractères mal formattée.
Les « RunTimeException »
Suite…
• java.lang.IllegalMonitorStateException : Le thread courant a tenté
d'attendre ou de prévenir d'autres threads, sur un objet non verrouillé par
ce thread. Cette exception est déclenchée par les méthodes wait () et
notify () de la classe Object.
• java.lang.IndexOutOfBoundsException : Un indice (sur un tableau, une
chaîne) ou un intervalle défini par deux indices ont dépassé les limites
inférieures ou supérieures. Les classes suivantes dérivent de cette classe
d'exception :
• java.lang.ArrayIndexOutOfBoundsException pour les tableaux (indice
négatif ou supérieur ou égal à la taille du tableau).
• java.lang.StringIndexOutOfBoundsException pour les chaînes de
caractères.
• java.lang.NegativeArraySizeException : Tentative de créer un tableau
ou une chaîne avec une taille négative.
• java.lang.NullPointerException : Tentative d'utiliser une référence null
alors qu'une référence sur un objet valide était attendue.
• java.lang.SecurityException : Tentative de violation de sécurité.
Les « RunTimeException »
Le package java.util définit les exceptions suivantes signalant
des opérations interdites :

• java.util.EmptyStackException : Tentative d'accéder à un élément dans


une pile vide (classe java.util.Stack).
• java.util.NoSuchElementException : Cette exception est déclenchée par
les implémentations de la méthode nextElement() de l'interface
java.util.Enumeration quand il n'y a plus d'éléments à énumérer.

Rappel:
Il n'est pas obligatoire de traiter les exceptions des
classes RuntimeException, Error et leur dérivées dans
un try ... catch, et ceci qu'elles soient citées ou non dans
la clause throws des méthodes invoquées.
Les « Exception »

La classe Exception dérive de la classe Throwable. Les


classes d'exceptions qui suivent sont déclenchées par
certaines méthodes de la bibliothèque Java :
• java.lang.ClassNotFoundException : Une classe ou une interface d'un
certain nom n'a pas été trouvée. Cette exception peut être déclenchée par
la méthode Class.forName() et les méthodes findSystemClass() et
loadClass() de la classe ClassLoader.
• java.lang.CloneNotSupportedException : La méthode Object.clone() a
été appelée sur un objet dont la classe n'implémente pas l'interface
Cloneable.
• java.lang.IllegalAccessException : Tentative de charger une classe dont
le nom est correct, mais qui n'est pas public, ou qui se trouve dans un
package différent ou dont le constructeur par défaut n'est pas accessible
(s'il est private par exemple). Cette exception peut être déclenchée par la
méthode Class.forName().
Les « Exception »

Suite :
• java.lang.InstantiationException : La classe spécifiée en paramètre de
newInstance() est abstraite, un tableau ou une interface, ceci interdisant la
création d'un nouvel objet.
• java.lang.InterruptedException : Le thread courant était en attente et un
autre thread a interrompu son attente grâce la méthode interrupt() de la
classe Thread.
Les « Exception »

Les packages java.io et java.net définissent les exceptions


suivantes qui permettent de vérifier les erreurs d'entrées-
sorties. Ces classes dérivent toutes de la classe
java.io.IOException (qui dérive elle-même de la classe
Exception) :
• java.io.EOFException : La fin de fichier a été rencontrée pendant une
opération de lecture.
• java.io.FileNotFoundException : Fichier inexistant dans le système de
fichiers.
• java.io.InterruptedIOException : Le thread courant était en attente de la
fin d'une opération d'entrée-sortie, et un autre thread a interrompu son
attente grâce la méthode Thread.interrupt().
• java.io.UTFDataFormatException : Erreur de conversion d'une chaîne
au format UTF-8 (ou inversement).
Les « Exception »
Suite :
• java.net.MalformedURLException : Une chaîne de caractères décrivant
un URL était mal formattée ou indiquait un protocole inconnu.
• java.net.ProtocolException : Erreur sur le protocole réseau.
• java.net.SocketException : Une opération sur un socket ne s'est pas
déroulé correctement.
• java.net.UnknownHostException : L'hôte (host) d'un réseau n'a pu être
atteint.
• java.net.UnknownServiceException : La connection réseau ne supporte
pas ce service.

Rappel:
Vous devez obligatoirement prendre en compte les
exceptions dont la classe dérive de java.lang.Exception
(sauf RuntimeException et ses classes dérivées), soit en les
traitant dans un try ... catch, soit en les propageant grâce
à la clause throws.
Exceptions et Informations

Les objets de type Exception possèdent différentes informations:


- un message d’erreur (défini par l’utilisateur)
- une image de la pile d’exécution

Soit e un objet de type Exception:


- e.getMessage() retourne le message défini dans le code
- e.toString() retourne le nom de classe de e + le message défini
dans le code
- e.printStackTrace() affiche une image de la pile d’exécution
Exceptions et Informations

Code type pour afficher ces informations:

try {
// code pouvant déclencher une exception MonException
}
catch(MonException e) {
System.out.println(‘Message : ’+e.getMessage());
System.out.println(‘Message : ’+e.toString());
e.printStackTrace();
}
Exceptions et Informations

Exemple:
Exceptions et Informations

Résultat :

System.out.println(‘Message : ’+e.getMessage());

System.out.println(‘Message : ’+e.toString());

e.printStackTrace());

Ecrit vers le flux d’erreur (System.err),


réorienté dans Eclipse vers la console
La Journalisation

La journalisation consiste à écrire dans un journal (log file)


certains évènements qui se produisent pendant la vie d’une
application

Présentation d’une méthode simple (qui n’exclue pas l’utilisation


d’outils professionnels) :
Ouverture d’un flux vers un fichier texte
Réorientation du flux d’erreur et/ou de sortie vers le fichier

Flux d’erreur: System.err (dans Eclipse: la console)


Flux de sortie: System.out (dans Eclipse: la console)
Fichier de « log »
Réorientation du flux
d’erreur vers le fichier Création d’un flux vers le fichier log

public MainApp() {
super();

//création du fichier log;


try {
logfile=new PrintStream(new FileOutputStream("fichier.log", true));
System.setErr(logfile);
System.setOut(logfile);
} catch (FileNotFoundException fileNotFoundException) {}

//construction du modèle
thisModel = ModelFactory.makeModel(…);

//construction des actions


initActions();

//construction de l’IHM
initGUI();
}
Journalisation des applications

L’utilisation de la journalisation offre de nombreux avantages :

aider à la mise au point du code

avoir un aperçu précis sur le cycle de vie de l’application

permettre aux utilisateurs de transmettre le journal pour


accélérer et faciliter le travail des développeurs

conserver un historique de développement/déboguage


Log4j, la journalisation facile

Les bonnes pratiques de développement actuelles déconseillent


l’utilisation des méthodes System.out.print* et System.err.print*
pour afficher des messages et recommandent plutôt l’utilisation
d’un logger tel Log4J apportant plus de souplesse.

Log4j se présente sous la forme d’un fichier Jar, à ajouter au


projet:
http://logging.apache.org/log4j/1.2/download.html
Log4j

Les fichiers journaux d’une application représentent la mémoire


d’une application, un historique permanent de la vie de celle-ci:
il est donc important de correctement enregistrer ces messages.

Log4J simplifie les gestions des logs et le débogage des


applications Java en fournissant des classes et des méthodes
pour l’enregistrement de ces informations.
Log4j: principales fonctionnalités

possibilité d’afficher ou enregistrer un message en lui assignant


un certain niveau de criticité (DEBUG, INFO, WARNING,
ERROR, CRITICAL)

indication de la classe ou la méthode à l’origine de ce message,


la ligne dans le code source, ou toute autre information utile

possibilité de configuration par fichier XML ou par fichier de


propriétés, donc de façon totalement externe au code
Log4j: autres fonctionnalités

les messages peuvent être enregistrés de différentes façons


(base de données, fichier txt ou XML, socket, serveur SMTP,…)

Log4J permet de remonter les messages d’erreurs de certains


logiciels avec lesquels il s’interface (exemple: Tomcat, on en peut
récupérer les messages dès le niveau DEBUG

gestion de différents fichiers de logs : plutôt qu’un fichier de


log unique dont la taille va rapidement grossir et le rendre
inexploitable, on peut faire en sorte que Log4J archive plusieurs
fichiers de logs selon différents critères (1 par jour, par semaine,
nouveau fichier lorsque le fichier courant atteint une taille
donnée,…).
Log4j et performance

Log4J a nécessairement des impacts sur les performances de


l’application

Mais si le logger est configuré judicieusement (éviter


l’affichage de messages dans des boucles par exemple),
l’impact est négligeable

Avant tout, éviter la profusion de messages !


(peut impliquer un accroissement important de la taille des
fichiers de logs aboutissant même à stopper l’application)
Structure de Log4j

La bibliothèque log4j met trois groupes de composants à


disposition du programmeur :

les loggers: pour envoyer/écrire les messages

les appenders: pour sélectionner la destination des messages

les layouts : pour la mise en forme des messages


Exemple d’utilisation de Log4j

import org.apache.log4j.Logger;

public class Hello {


/**
* déclaration et création du Logger (en statique afin d'économiser la
* mémoire)
*/
private final static Logger logger = Logger.getLogger(Hello.class);

public String helloWorld() {


logger.debug("La méthode helloWorld est invoquée");
return "Hello, world";
}

public static void main(String[] args) {


Hello h = new Hello();
System.out.println(h.helloWorld());
}

}
Exemple d’utilisation de Log4j

Remarque 1 :
logger.debug("La méthode helloWorld est invoquée");

est équivalent à
logger.log(Level.DEBUG,"La méthode helloWorld est invoquée");

Remarque 2 : aucun appender définit…


log4j:WARN No appenders could be found for logger (IHM.Hello).
log4j:WARN Please initialize the log4j system properly.
Hello, world
Ajout d’un Appender

Définition d’un appender:

logger.addAppender(new FileAppender(new SimpleLayout(), "hello.log"));

Création d’un appender


Liaison logger-appender de type fichier

Création d’un layout


de type texte simple
Ajout d’un Appender
import java.io.IOException;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;

public class Hello {


/**
*déclaration et création du Logger
*/
private final static Logger logger = Logger.getLogger(Hello.class);

public Hello() {
try {
logger.addAppender(new FileAppender(new SimpleLayout(), "hello.log"));
} catch (IOException e) {
e.printStackTrace();
}
}
public String helloWorld() {
logger.debug("La méthode helloWorld est invoquée");
return "Hello, world";
}

public static void main(String[] args) {


Hello h = new Hello();
System.out.println(h.helloWorld());
}
}
Autres outils de journalisation

• jLo : http://jlo.jzonic.org/

• Commons Logging :
http://commons.apache.org/logging/

• Java (depuis la version 1.4.2) possède son propre


package de log :
java.util.logging
dont la classe centrale est :
java.util.logging.Logger

Vous aimerez peut-être aussi