You are on page 1of 301

Du C/C++ à JavaT M

Mise à jour du 11/02/2000 (version 1.1.1)

Emmanuel PUYBARET

http://www.eteks.com
Copyrights

Tous les développements (texte, images et programmes) de ce manuel sont protégés par les droits
d'auteur et ne doivent pas être reproduits par n'importe quel moyen et sur n'importe quel support que ce
soit, publiés dans d'autres travaux ou traduits dans d'autres langues sans l'autorisation écrite d'eTeks.

Le manuel Du C/C++ à Java est fourni tel quel, sans aucune garantie d'aucune sorte, ni expresse ni
implicite. Il pourrait notamment comporter des inexactitudes et des erreurs de frappes.
Ce manuel peut être modifié à tout moment en vue de corrections ou d'améliorations.

Le manuscrit correspondant au manuel a été déposé à la Société Des Gens de Lettre.

Marques déposées

Sun, Sun Microsystems, le logo Sun et Java sont des marques déposées ou des marques de services de
Sun Microsystems, Inc, aux Etats Unis et autres pays. Copyright © 1992-1995 Sun Microsystems, Inc.
Tous droits réservés.

eTeks est indépendant de Sun Microsystems, Inc.

eTeks est une marque déposée par Emmanuel PUYBARET.

Les autres marques citées dans ce manuel sont des marques déposées ou des marques commerciales
détenues par leurs propriétaires respectifs.
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 1

Table des Matières

Copyrights

Avant propos

Démarrer en Java

Récupérer le JDK (Java Development Kit)


Introduction
Où trouver le JDK
Installation
Description de l'environnement et des commandes
Principe de fonctionnement
Description des principales commandes
javac
java
appletviewer
Votre première applet : le classique "Hello world !"
Applet HelloWorld

Les notions de base

Objets, classes et héritage


Références
Les mots-clés de Java
Les mots-clés du C/C++ absent en Java
Types de base
Structure d'un programme
Les packages
import
Définir un package

Création et utilisation des classes

Déclaration des classes et des interfaces


Identifiants
Les classes
Les interfaces
Déclaration des variables
Syntaxe
Initialisations static
Initialisations d'instance
Déclaration des méthodes
Syntaxe
Surcharge des méthodes
Constructeur
Création d'objets : opérateur new
Outrepasser une méthode
Application Banque
Utilisation de classes abstact
Destruction des objets
Comment ça marche ?
Application ListeChainee
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 2

Objets, tableaux et chaînes de caractères

La classe Object
La classe Class
Les tableaux
Les chaînes de caractères
La classe String
La classe StringBuffer
La classe System

Les instructions et les opérateurs

Les blocs
if ... else, switch
while, do ... while, for
Les expressions
Utilisation de this et de super
Les opérateurs
Opérateurs arithmétiques
L'opérateur instanceof
Opérateurs du C/C++ absent en Java
Les conversions (ou casts)
Conversions entre types de base avec gain de précision
Conversions entre types de base avec perte de précision
Conversions de références d'une classe dans une autre
Priorité des opérateurs

Les exceptions

throw, try, catch,...


Syntaxe
Application EssaiException
Application InstantiationAvecNom
Avantages des exceptions
La classe Throwable
Les exceptions Runtime
Les classes d'erreurs
Les autres exceptions

Les threads

Définition d'un thread


Le partage du temps entre threads
La création d'un thread
Applet Chrono
Les états d'un thread
La synchronisation des threads
Utilisation de synchronized
Applet AfficheurDeCalcul
Synchronisation avec wait () et notify ()
La classe Thread

Les classes internes

Les classes internes


Syntaxe
Utilisation
Les classes anonymes
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 3

Autres nouveautés Java 1.1


Initialisations d'instance
Initialisation de tableaux
Utilisation du mot-clé class
Variables locales et paramètres final

Conventions d'éciture et portage

Conventions d'écriture
Portage de programmes écrits en C/C++
Conception des classes
Remplacement des définitions de type typedef
Remplacement des instructions de précompilation #define
Remplacement des instructions de précompilation #ifdef, #else, #endif
Remplacement des énumérations enum
Remplacement des unions union
Application TestExpression
Passage des valeurs par adresse
Allocation dynamique
Utilisation des chaînes de caractères
Arithmétique des pointeurs
Transformation des pointeurs sur fonctions
Remplacement de l'héritage multiple
Autres problèmes propres au C++

La bibliothèque Java 1.0

Les packages de la bibliothèque Java 1.0


Classes de base : le package java.lang
Gestion de données et utilitaires : le package java.util
Les entrées-sorties : le package java.io
Les accès réseau : le package java.net
Gestion des applets : le package java.applet
Interface utilisateur : le package java.awt
Manipulation d'images : le package java.awt.image
Liaison avec l'interface utilisateur du système : le package java.awt.peer
Hiérarchie des classes Java 1.0

Les classes de bases

Gestion des objets


La classe java.lang.Object
L'interface java.lang.Cloneable
La classe java.lang.Class
La classe java.lang.ClassLoader
La classe java.lang.Compiler
La classe java.lang.Throwable
Les classes d'emballage
La classe java.lang.Boolean
La classe java.lang.Character
La classe java.lang.Number
La classe java.lang.Integer
La classe java.lang.Long
La classe java.lang.Float
La classe java.lang.Double
La classe java.lang.String
La classe java.lang.StringBuffer
Calcul mathématique : la classe java.lang.Math
Gestion des threads
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 4

L'interface java.lang.Runnable
La classe java.lang.Thread
La classe java.lang.ThreadGroup
Gestion du système
La classe java.lang.System
La classe java.lang.Runtime
La classe java.lang.Process
La classe java.lang.SecurityManager

Les outils Java

Gestion de collections de données


L'interface java.util.Enumeration
Application EssaiEnumeration
La classe java.util.Vector
La classe java.util.Stack
La classe java.util.Dictionary
La classe java.util.Hashtable
La classe java.util.Properties
La classe java.util.BitSet
Gestion des dates
La classe java.util.Date
Génération de nombres aléatoires
La classe java.util.Random
Autres classes d'outils
La classe java.util.StringTokenizer
L'interface java.util.Observer
La classe java.util.Observable
Applet ObservateurCalcul

La gestion des fichiers et des flux de données

Gestion des entrées-sorties


Mode d'accès aux données
Gestion de l'accès aux données avec les exceptions
Manipulation des fichiers
L'interface java.io.FilenameFilter
La classe java.io.File
La classe java.io.FileDescriptor
Gestion des flux de données
Accès à un flux de données en lecture
Application LectureFichier
Application NumerotationLigne
La classe java.io.InputStream
La classe java.io.FileInputStream
La classe java.io.PipedInputStream
La classe java.io.ByteArrayInputStream
La classe java.io.StringBufferInputStream
La classe java.io.SequenceInputStream
La classe java.io.FilterInputStream
La classe java.io.BufferedInputStream
L'interface java.io.DataInput
La classe java.io.DataInputStream
La classe java.io.LineNumberInputStream
La classe java.io.PushBackInputStream
La classe java.io.StreamTokenizer
Accès à un flux de données en écriture
Application ConcatenationFichiers
La classe java.io.OutputStream
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 5

La classe java.io.FileOutputStream
La classe java.io.PipedOutputStream
La classe java.io.ByteArrayOutputStream
La classe java.io.FilterOutputStream
La classe java.io.BufferedOutputStream
L'interface java.io.DataOutput
La classe java.io.DataOutputStream
La classe java.io.PrintStream
Gestion de l'accès aléatoire aux fichiers
La classe java.io.RandomAccessFile

Les accès au réseau

Accès via une URL


La classe java.net.URL
Application TestProtocole
La classe java.net.URLConnection
Applet HelloFromNet
La classe java.net.URLEncoder
L'interface java.net.URLStreamHandlerFactory
La classe java.net.URLStreamHandler
L'interface java.net.ContentHandlerFactory
La classe java.net.ContentHandler
L'architecture client-serveur
Principe
Exemples d'utilisation
Protocoles
Accès via les sockets
La classe java.net.InetAddress
La classe java.net.Socket
La classe java.net.ServerSocket
Le client serveur d'écho
Application EchoServer
Applet EchoClient
Le paper board Internet
Application PaperBoardServer
Applet PaperBoard Client
La classe java.net.SocketImpl
L'interface java.net.SocketImplFactory
Accès via les datagrammes
La classe java.net.DatagramPacket
La classe java.net.DatagramSocket

Les applications et les applets

Les applications Java


Les applets
Caractéristiques
La classe java.applet.Applet
L'intégration des applets dans les navigateurs
L'interface java.awt.AppletContext
L'interface java.applet.AppletStub
Applet PlayApplet
L'interface java.applet.AudioClip
Applet Piano
Transformer une applet en application

Les composants de l'interface utilisateur


vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 6

Les composants Java


La classe java.awt.Component
Les composants prédéfinis
La classe java.awt.Button
La classe java.awt.Checkbox
La classe java.awt.CheckboxGroup
La classe java.awt.Choice
La classe java.awt.List
Applet Unicode
La classe java.awt.Label
La classe java.awt.TextComponent
La classe java.awt.TextField
Applet CalculetteSimple
La classe java.awt.TextArea
La classe java.awt.Scrollbar
Comment ça marche ?
Le peer d'un composant
La classe java.awt.Toolkit

Les containers et la disposition des composants

Les containers
L'architecture container/composant
Applet TraitementTexte
La classe java.awt.Container
La classe java.awt.Panel
La classe java.awt.Window
La classe java.awt.Frame
Transformer une applet en application isolée
La classe java.awt.Dialog
Applet MessageBoxApplet
La classe java.awt.FileDialog
La disposition des composants : les layouts
L'interface java.awt.LayoutManager
La classe java.awt.FlowLayout
La classe java.awt.BorderLayout
Applet BorderBuilder
La classe java.awt.GridLayout
La classe java.awt.GridBagLayout
La classe java.awt.GridBagConstraints
La classe java.awt.CardLayout
Les menus
Applet ShowMenu
L'interface java.awt.MenuContainer
La classe java.awt.MenuComponent
La classe java.awt.MenuBar
La classe java.awt.MenuItem
La classe java.awt.Menu
La classe java.awt.CheckboxMenuItem

La gestion de l'interface utilisateur

La gestion événementielle
Les événements
Applet MiseAJourHorloge
La classe java.awt.Event
La classe Graphics : tout pour dessiner
La classe java.awt.Graphics
Applet DrawIt
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 7

Les polices de caractères


La classe java.awt.Font
La classe java.awt.FontMetrics
Applet ListePolices
La couleur
La classe java.awt.Color
Applet Nuancier
Les classes manipulant des dimensions
La classe java.awt.Dimension
La classe java.awt.Insets
La classe java.awt.Point
La classe java.awt.Polygon
La classe java.awt.Rectangle
La création de nouveaux composants
La classe java.awt.Canvas
Applet BoutonsNavigation

Les images

La génération d'images
Applet MultiImages
La classe java.awt.Image
Le chargement des images
Applet ImageSimple
La classe java.awt.MediaTracker
L'interface java.awt.image.ImageObserver
Applet ChargementImage
La création d'images
La classe java.awt.image.MemoryImageSource
Applet ImageTableau
La classe java.awt.image.ColorModel
La classe java.awt.image.DirectColorModel
La classe java.awt.image.IndexColorModel
Applet ImageNoirEtBlanc
La classe java.awt.image.PixelGrabber
Transformer des images avec un filtre
La classe java.awt.image.FilteredImageSource
La classe java.awt.image.ImageFilter
La classe java.awt.image.CropImageFilter
La classe java.awt.image.RGBImageFilter
Applet NegatifImage
Comment ça marche ?
Applet Compteur
L'interface java.awt.image.ImageProducer
L'interface java.awt.image.ImageConsumer
Gestion d'animations
Enchaînement d'images téléchargées
Applet AnimationFleche
Utilisation du double buffering
Applet ScrollText
Horloge avec image de fond
Applet Horloge

Plus loin avec Java...

Les évolutions du langage


Java 1.1
Java 2
Conclusion
vendredi 11 février 2000 Table des matières Du C/C++ à Java Page: 8

Principales applets Java de ce manuel (les plus intéressantes sont en gras)

Compteur de temps
HelloWorld
Liste des caractères accentués Unicode
Compteurs multiples
Chronomètre
Afficheur synchronisé de calculs
Observateur de calculs
Lecture d'un fichier sur Internet
Paper board Internet partagé
Contrôleur d'applet
Son d'un piano
Afficheur de composants Java
Opération simple entre deux nombres
Descripteur du comportement d'une applet
Couper/Copier/Coller/Effacer dans un traitement de texte
Boite de message
Utilisation de la classe BorderLayout
Générateur d'interface GridBagBuilder
Test de menus
Mini-éditeur graphique
Liste des polices de caractères disponibles
Nuancier
Barre de navigation avec boutons images
Filtrage en négatif
Compteur
Image animée
Défilement de texte
Horloge des étoiles

Les autres applets accessibles sur le site http://www.eteks.com

Interpréteur de fonctions

Démineur Java

Historique du manuel
vendredi 11 février 2000 Du C/C++ a Java : Avant propos Page: 1

Avant propos

Du C/C++ à Java décrit la version 2 du langage Java. Les similitudes et les différences
avec les langages C et C++ sont exposées point par point, en proposant si nécessaire des
solutions concrètes aux concepts du C/C++ qui sont traités différemment en Java
(pointeurs, traitement d'erreur,...). La bibliothèque Java de la version 1.0 est décrite dans son
ensemble classe par classe (classes de base, d'outils, d'entrées- sorties, d'accès réseau et de
gestion de l'interface graphique AWT).

Du C/C++ à Java s'adresse tout d'abord aux développeurs connaissant les langages C ou
C++, mais aussi aux personnes ayant pratiqué d'autres langages structurés ou orientés objet
(PASCAL, SmallTalk, Visual Basic,...). Les remarques sur le C/C++ et sur les pièges à
éviter en Java sont mises en valeur pour les retrouver facilement et permettre une lecture
rapide.

Du C/C++ à Java peut être utilisé comme cours de programmation des applets et des
applications Java, et comme manuel de référence rapide du langage et de la bibliothèque
Java. De nombreux exemples simples et originaux vous guident pas à pas du classique
HelloWorld jusqu'à la programmation d'applets évoluées que vous pouvez tester "en
direct" dans le site http://www.eteks.com.

Niveau : Initiés/Expérimentés

Des développements futurs traiteront plus en détail des ajouts effectués dans la
bibliothèque Java 1.1 et Java 2.
vendredi 11 février 2000 Du C/C++ a Java : Avant propos Page: 2

Comment utiliser ce manuel ?


Suivant le but que vous recherchez et votre méthode d'apprentissage personnelle, vous
pouvez aborder ce manuel de différentes manières :

Si vous voulez apprendre Java par l'exemple :


Vous pouvez lire le premier chapitre pour connaître les principes généraux du
langage, puis explorer directement les différents exemples d'applets fournies sur le
site.
Quand vous commencerez à cerner la structure d'un programme Java, vous pourrez
lire le chapitre sur les applets et piocher dans les cinq premiers chapitres pour
compléter vos connaissances sur le noyau de langage.
Si vous voulez apprendre Java sans affronter dans un premier temps les points les
plus difficiles :
Lisez rapidement les cinq premiers chapitres présentant le noyau du langage, puis
passez directement au chapitre sur les applets et suivants pour réaliser vos premières
applets. Quand vous serez plus à l'aise avec Java, vous pourrez alors revenir sur les
chapitres traitant des exceptions et des threads.
Vous voulez faire une revue rapide des différences entre Java et le C/C++ :
Lisez surtout les neuf premiers chapitres sur le noyau du langage : comme indiqué
ci-après, ces chapitres comportent de nombreuses remarques résumant ces
différences. Pour terminer, le chapitre abordant le portage de programmes C/C++ en
Java vous donnera un complément d'information.
Vous cherchez une référence des classes de Java 1.0 :
Soit vous utilisez la table des matières pour une recherche thématique, soit vous
utilisez la hiérarchie des classes pour une recherche alphabétique et hiérarchisée.
Vous voulez tout connaître sur Java !
Lisez tous les chapitres du premier au dernier, puis si vous avez encore soif de
connaissances, soyez patient, la suite arrive...

Ce cours comporte 20 chapitres et représente plus de 2 Mo de données (fichiers HTML,


images et applets).
Pour éviter toute incompréhension, tous les exemples de code Java ont été vérifiés et sont
sans erreur de compilation.

Ce symbole vous signale des points importants ou des pièges à éviter.

Ces symboles vous signalent les différences entre le C/C++ ou le C++, et Java (utilisation
différente, absence ou ajout dans le langage,...).
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 1

Démarrer en Java

Récupérer le JDK
Installation
Description de l'environnement et des commandes
Votre première applet : le classique "Hello world !"

Récupérer le JDK (Java Development Kit)

Introduction

Bonne nouvelle : jusqu'à nouvel ordre, vous pouvez développer en Java sans débourser un Kopeck (ou
presque : il vous faut quand même un ordinateur si possible équipé d'un modem...).

Java est un langage de programmation développé par Sun Microsystems. Il n'a que quelques années de
vie (les premières versions datent de 1995), et pourtant il a réussi à intéresser et intriguer beaucoup de
développeurs à travers le monde. Et pourquoi donc ?

La réponse est vaste et forcément sujet à polémiques. En voici les principaux avantages :

C'est un langage orienté objet dérivé du C, mais plus simple que le C++.
Il est multi-plateforme : tous vos programmes tourneront sans modification sur toutes les
plateformes où existe Java.
Il est doté en standard d'une riche bibliothèque de classes, comprenant la gestion des interfaces
graphiques (fenêtres, boites de dialogue, contrôles, menus, graphisme), la programmation
multi-threads (multitâches), la gestion des exceptions, les accès aux fichiers et au réseau
(notamment Internet),...

Les deux derniers points ont contribué à utiliser d'abord ce langage pour développer des applets, qui
sont des applications qui peuvent être téléchargées via Internet et exécutées dans un navigateur sur
n'importe quelle plateforme. Ainsi, une page statique HTML peut s'enrichir de programmes qui lui
donneront un comportement dynamique. Cet avantage permet de visualiser directement dans ce manuel
le résultat des programmes.

Où trouver le JDK

Venons-en aux faits. Le JDK est disponible gratuitement sur le site Java de Sun
http://www.javasoft.com/. Actuellement, vous cherchez sur ce site un lien products . Ce lien vous
amène sur une page décrivant tout ce que Sun met à la disposition des développeurs pour développer en
Java (et il y en a beaucoup).

Java est disponible sous 3 versions : Java 1.0, Java 1.1 et Java 2 (la version 2 est uniquement disponible
pour Windows, Solaris et Linux pour le moment).
Il est conseillé de débuter par la version 1.0 en téléchargeant le Java Development Kit - JDK 1.0 (en fait la
version 1.0.2), disponible sous Windows 95/98/NT, MacOS et Solaris sur le site de Javasoft. Si votre
système de développement n'est pas un de ceux-ci, ce site fournit des liens vers ces autres systèmes
tierces (third party ports ).
En commençant par la version 1.0, vous pourrez :

Apprendre Java et utiliser sa bibliothèque de base, sans être noyé par la quantité de fonctionnalités
supplémentaires des versions suivantes (212 classes dans Java 1.0, 504 dans Java 1.1, 1592 dans
Java 2).
Bénéficier d'une documentation complète en utilisant le manuel Du C/C++ à Java (langage et
bibliothèque).
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 2

Faire fonctionner vos applets sur presque tous les navigateurs (notamment Netscape Navigator et
Microsoft Internet Explorer dès leur version 3).

Les versions Java 1.0 et 1.1 pour MacOS développées par Apple (dénommées MRJ 1.5 et MRJ 2.2
respectivement) sont disponibles sur le site http://devworld.apple.com/java/.

Sur son site, Sun fournit aussi des documentations en anglais accompagnant le JDK. Voici la liste des
plus intéressantes, qui ont aidé à rédiger ce manuel :

Les spécifications du langage au format Acrobat Reader, pour ceux qui recherchent des
renseignements détaillés sur le langage et les bibliothèques des classes de base de Java 1.0 (plus de
800 pages).
Le fichier est accessible via Java Language Specification 1.0 ; les personnes ne disposant pas encore
d'Adobe Acrobat Reader peuvent se procurer gratuitement cette application sur le site d'Adobe :
http://www.adobe.com/.
L'ensemble des classes Java et leur interface de programmation sous forme de fichiers HTML à
visualiser avec un navigateur (très utile pour avoir une référence complète des classes).
Cet ensemble de fichiers est rassemblé sous forme de fichier .tar , .zip ou .hqx et est accessible
via API Documentation . Les personnes ne disposant pas d'application pour extraire les fichiers d'un
fichier .zip ou .tar , peuvent se procurer sur toute plateforme (Windows, MacOS ou UNIX) des
freewares faisant parfaitement l'affaire. Les moteurs de recherche vous donneront des liens pour
satisfaire votre bonheur.
Un tutorial de Java très complet, se dénommant The Java Tutorial - Object-Oriented Programming for the
Internet , est disponible sous forme de fichiers HTML rassemblés dans un fichier tutorial.zip .
Voir aussi les liens utiles sur http://www.eteks.com...

Bon courage pour télécharger ces fichiers et à tout à l'heure... Voici une petite applet indiquant depuis
combien de temps cette page est chargée : déjà secondes !

Le kit de développement fourni par Sun est très sommaire (généralement un ensemble de commandes
simples), il vous faudra en plus au moins un éditeur de texte (n'importe lequel, peut importe). De
nombreux éditeurs de logiciels fournissent des environnements de développement complet (éditeur,
debugger intégré,...), mais là, il faudra dégainer la carte bleue !

Installation

Une fois que vous avez (au moins) téléchargé le JDK, vous pouvez vous mettre au travail.

Sous MacOS, vous récupérez un fichier auto-extractible, qui vous installe directement les applications
nécessaires.
Sous Windows 95/NT, vous récupérez un programme d'installation qui vous installe toutes les commandes
sans difficultés.
Sous UNIX, désolé, pas de machine UNIX sous la main... Il faudra lire attentivement la documentation
d'installation mais si vous connaissez bien UNIX, ce sera un jeu d'enfant !

Description de l'environnement et des commandes

Ce chapitre décrit les principales commandes (ou applications pour MacOS) qui vous permettront de
programmer en Java. Mais tout d'abord il faut comprendre comment marche un programme Java pour
utiliser correctement ces commandes.

Principe de fonctionnement

Rapidement résumé, Java est langage qui doit être compilé et interprété. Compilé et interprété ? En fait
dans une première phase, vous compilez un programme (un ou plusieurs fichiers source .java ) en
fichiers .class et le compilateur génère un fichier .class pour chacune des classes définies dans le(s)
fichier(s) .java . L'ensemble des fichiers .class est ensuite interprété par la Machine Virtuelle Java (Java
Virtual Machine ) pour exécuter le programme. Voici une illustration de ce principe :
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 3

Figure 1. Cycle de développement d'une applet

Il est possible de développer soit des applications isolées (standalone applications ), fonctionnant avec
l'interpréteur comme un programme habituel, soit des "applets" ; ce sont des programmes qui sont
téléchargés sur Internet puis exécutés automatiquement quand ils sont intégrés à l'intérieur de pages
HTML. Dans ce dernier cas, l'ensemble des fichiers .class est utilisé avec un fichier HTML qui fait
appel à une des classes (voir aussi la Description des principales commandes).
Bien que les principes de programmation soient très proches, ce manuel donne surtout des exemples
d'applets car elles peuvent être ainsi intégrées dans le texte pour vous montrer le résultat d'un
programme "en direct".

Pourquoi ne pas interpréter directement le programme Java ?

1. Les fichiers .class contiennent du bytecode, une sorte de code machine Java (comparable au code
machine d'un microprocesseur), qui sera exécuté beaucoup plus rapidement par l'interpréteur que si
ce dernier devait travailler avec les fichiers sources .java .
2. Seuls les fichiers .class sont nécessaires à l'exécution d'un programme Java. Comme ils contiennent
du code machine, ils ne peuvent être lus par des tiers, protégeant ainsi le code source.
3. Etant compilés, les fichiers .class sont de taille plus petite que le code source Java ; ceci est un
argument important pour transférer les programmes sur Internet.
4. Chaque fichier .class décrivant une classe d'objet, une même classe peut être utilisé par différents
programmes, sans que cette classe ne soit dupliquée dans chacun des programmes.

Pourquoi ne pas compiler directement un exécutable ?

Sur ce point, la réponse est plus évidente. En effet, un exécutable contient du code qui n'est
exécutable que sur la machine pour lequel il est destiné et le seul moyen de rendre un
langage multi-plateforme sans avoir à recompiler le source (comme en C/C++), c'est
d'utiliser un interpréteur.
L'autre avantage de l'interpréteur est qu'il peut être incorporé par exemple, à un navigateur ce
qui lui permet d'exécuter des programmes Java à l'intérieur de pages HTML.

Limites du système :

A vu d'œil, ce système a l'air d'être parfait : Vous écrivez des programmes, les compilez et le
résultat de la compilation fonctionnera en l'état sur toutes les machines ! Forcément, il doit y
avoir un os quelque part...
Le plus gros problème d'un programme Java est son manque de rapidité d'exécution :
L'interpréteur prend forcément un certain temps pour interpréter le code des fichiers .class ,
ce qui rend vos programmes moins rapides que de vrais exécutables.
Mais Java est encore jeune : Sun Microsystems et d'autres éditeurs de logiciels tentent
d'optimiser les Machines Virtuelles Java. L'un des axes d'optimisation est actuellement le
développement de compilateurs JIT (Just In Time ) ou juste à temps : Quand une classe est
chargée, elle est traduite dans le code machine de la plateforme où elle est exécutée
(traduction à la volée du micro-code Java vers par exemple, le code machine PowerPC ou
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 4

Intel). Cette opération ralentit le départ de l'application, mais ensuite l'exécution du


programme est beaucoup plus rapide !

Contrairement au C/C++, le compilateur Java ne génère pas de fichier exécutable. Il crée pour chacune
des classes d'un fichier Java un fichier .class qui sera interprété par la Machine Virtuelle Java.

Description des principales commandes

L'utilisation des commandes varie forcément suivant l'environnement de développement :

Sous Windows 95/98/NT, ouvrir une fenêtre de commandes ; puis, utiliser les commandes
décrites ci-après, en les tapant directement. Pour éviter d'avoir à décrire le chemin d'accès complet
aux commandes, il vous est vivement conseillé de modifier la variable d'environnement PATH
pour que soit pris en compte le répertoire d'installation des commandes Java.
Sous UNIX, utiliser la ligne de commandes comme sous Windows.
Sous MacOS, soit vous effectuez sur l'icône de la commande un drag-and-drop (ou glisser-déposer
pour les francophiles purs) des fichiers à passer en paramètres, soit vous utilisez le premier item du
menu File pour choisir le fichier en question (Java Runner ouvre directement une boite de dialogue
vous demandant de choisir la classe à exécuter).

Toutes les options de chacune de ces commandes peuvent être obtenues en exécutant la commande sans
paramètre.

javac, le compilateur (Icône Java Compiler ou javac sous MacOS) :

javac permet de compiler un ou plusieurs fichiers Java passé en paramètres. Le compilateur


génère pour chaque classe ClasseI définie dans le(s) fichier(s) Java compilé(s), un fichier
ayant pour nom le nom de classe et l'extension .class (ClasseI.class) .

En cas d'erreurs dans vos programmes, le compilateur javac vous donne généralement des
informations assez précises sur le contexte de l'erreur. C'est pourquoi n'hésitez pas à
compiler vos classes en cours de développement quand vous hésitez sur la bonne syntaxe à
utiliser : javac vous donnera peut-être la solution !

Principales options à spécifier avant la liste des fichiers Java à compiler (boite de dialogue
File | Properties sous MacOS) :

-O : optimisation (Dans certains cas, les méthodes final sont compilées inline ,
c'est-à-dire que l'appel à une méthode est remplacé par le code de cette méthode).
-classpath dir : dir spécifie un ou plusieurs répertoires d'accès aux classes
importées (séparés par ; sous Windows, et : sous UNIX).
-d dir : dir spécifie le répertoire de destination des fichiers de classes .class .

La variable d'environnement CLASSPATH est utilisée comme équivalent de l'option


-classpath .

Exemple : javac Cube.java

Si vous compilez certains programmes Java respectant la version 1.0 avec le compilateur de la version
1.1 ou 2, vous pouvez avoir des warnings vous indiquant que certaines méthodes que vous utilisez ne
sont pas recommandés (deprecated ), mais ceci n'empêchera pas le programme de fonctionner
correctement.

Note aux utilisateurs de systèmes dont les fichiers ne font pas la différence entre les minuscules et
majuscules (Windows et MacOS) : Vous devez créer et utiliser des fichiers utilisant les combinaisons
majuscules/minuscules correctes ; par exemple, si vous créez une classe Cube3D, le nom de fichier doit
être Cube3D.java et pas cube3d.java ou Cube3d.java.
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 5

Java, l'interpréteur (Icône Java Runner ou JBindery sous MacOS) :

Cette commande est utilisée pour exécuter des applications isolées. Dans ce cas, le fichier
.class passé en paramètre doit définir une méthode main () qui sera le point d'entrée de
l'application. Toutes les valeurs qui suivent le nom de la classe sont passées en paramètres à
la méthode main ().

Principales options de la ligne de commande à spécifier avant le nom de la classe (vous


retrouverez ces options dans la boite de dialogue de JBindery sous MacOS ) :

-classpath dir : dir spécifie un ou plusieurs répertoires d'accès aux classes


importées (séparés par ; sous Windows, et : sous UNIX).
-Dpropriete=valeur : modifie la propriété du système avec la valeur donnée (voir
aussi la classe System).
-version : écrit la version de la Machine Virtuelle que vous utilisez.

La variable d'environnement CLASSPATH est utilisée comme équivalent de l'option


-classpath .

Exemple : java ClasseExec

appletviewer, l'interpréteur d'applets (Icône Applet Viewer ou Applet Runner sous MacOS) :

cette commande est utilisée pour exécuter des applets. Dans ce cas, le fichier .html passé en
argument doit définir au moins une balise (tag ) APPLET respectant la syntaxe suivante :

<APPLET CODE=ClasseApplet WIDTH=largeur HEIGHT=hauteur CODEBASE="repertoire"


ALT="Ca marche pas" NAME="NomApplet" ALIGN=alignement ARCHIVE="fichier.jar">
</APPLET>.

L'argument de CODE, ClasseApplet, doit correspondre à un fichier .class qui est la classe
de l'applet. Vous verrez dans le premier exemple comment définir une classe utilisable
comme point d'entrée d'une applet.
WIDTH et HEIGHT définisse la largeur et la hauteur de la zone où sera affichée l'applet.
CODEBASE (optionnel) permet de définir le chemin d'accès aux classes utilisées par
l'applet. Par défaut le chemin d'accès est le répertoire d'où provient le fichier HTML.
Le chemin spécifié par CODEBASE peut être relatif au répertoire courant du fichier
HTML, ou être une URL désignant un chemin sur un serveur différent.
ALT (optionnel) définit la chaîne de caractères à afficher quand l'applet ne peut être
exécutée (si par exemple, un navigateur n'autorise pas Java, il écrira cette chaîne).
NAME (optionnel) définit un nom pour l'applet (utilisé quand vous recherchez une applet
par son nom).
ALIGN (optionnel) permet de définir l'alignement horizontal de l'applet dans la page
HTML (LEFT, RIGHT ou MIDDLE).
ARCHIVE (optionnel) est un attribut apparu à partir de Java 1.1 pour définir le fichier JAR
qui rassemble les classes, les images et autres fichiers qu'utilise l'applet.

appletviewer crée pour chaque balise APPLET, une fenêtre de taille (WIDTH,HEIGHT), où sera
exécutée l'applet correspondant à la classe de CODE.
Entre <APPLET ...> et </APPLET> peuvent figurer la description d'un certain nombre de
paramètres à passer à l'applet, sous la forme <PARAM NAME="appletAttributei"
VALUE="valuei">. Tout le reste du fichier HTML est ignoré.
Vous pouvez aussi exécuter les applets d'un fichier HTML avec un navigateur intégrant un
interpréteur Java. Ceci vous est même vivement conseillé afin de pouvoir vérifier comment
s'intègre vos applets au milieu du texte dans lequel elles sont utilisées.
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 6

Tout le code HTML compris entre les balises <APPLET ...> et </APPLET> (sauf bien sûr les balises
<PARAM ...>) est affichée par les navigateurs qui ne sont pas dotés de Java ou sur lesquels Java n'est
pas autorisé. Ceci est très pratique pour offrir une alternative aux applets sur un site Internet.
Si vous consultez les source des fichiers du site http://www.eteks.com, vous verrez que justement
chaque applet du site a pour alternative l'affichage d'une image qui est la capture d'écran de l'applet.

De nombreux exemples d'Applet avec leur programme Java, sont fournis avec le JDK.
Essayez-les pour vous rendre compte des possibilités du langage.

Si une applet définit la méthode main (), elle peut être aussi utilisée comme une application
avec l'interpréteur Java.

Exemple : appletviewer index.html

Sur la plupart des Machines Virtuelles Java, tant que vous ne quittez pas l'interpréteur (java ,
appletviewer ou navigateur), les classes déjà chargées restent en mémoire, et NE sont PAS rechargées
quand vous relancez un même programme Java.
Ceci implique qu'en phase de mise au point de vos programmes, il faut quitter l'interpréteur ou le
navigateur à chaque fois que vous changez une des classes de votre programme, sinon vous aurez
l'impression que vos modifications n'ont pas été prises en compte !...

Votre première applet : le classique "Hello world !"

Comme vous devez l'avoir compris maintenant, il faut écrire écrire au moins deux fichiers différents
pour pouvoir créer et exécuter une applet : Un fichier source Java et un fichier .html .

Voici le programme source Java. Donnez lui le nom HelloWorld.java (le nom de la classe public du
fichier) :

import java.applet.Applet;
import java.awt.Graphics;

public class HelloWorld extends Applet


{
public void paint (Graphics g)
{
g.drawString("Hello world !", 50, 20);
}

public static void main (String [ ] args)


{
System.out.println ("Hello World !");
}
}

Les instructions utilisées dans ce programme seront décrites ultérieurement.


Vous pouvez le compiler de la manière suivante : javac HelloWorld.java
Voici le source HMTL. Donnez lui le nom que vous voulez (MonPremierProgrammeJava.html si ça vous
chante !) :

<HTML>
<HEAD>
<TITLE> Un programme simple </TITLE>
</HEAD>
<BODY>

voici le résultat que vous devriez voir apparaître :


<APPLET CODE="HelloWorld.class" WIDTH=150 HEIGHT=25>
</APPLET>
</BODY>
</HTML>
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 7

Vous pouvez exécuter l'applet de la manière suivante : appletviewer MonPremierProgrammeEnJava.html


voici le résultat que vous devriez voir apparaître :

Comme HelloWorld définit aussi une méthode main (), qui est le point d'entrée d'une application Java, il
est donc possible de l'exécuter directement grâce à la commande : java HelloWorld

N'oubliez pas d'autoriser l'utilisation de Java dans votre navigateur, si vous voulez l'essayer pour
interpréter vos applets et visualiser le résultat des applets de ce manuel.

Pour les débutants, qui n'ont que de faibles notions de programmation (ou qui ne savent pas comment
modifier le PATH sous Windows), voici une procédure plus détaillée pour réaliser cette applet :

Windows 95/98/NT MacOS


Ouvrez un nouveau document avec notepad.exe . Ouvrez un nouveau document avec
Recopiez le programme source Java HelloWorld SimpleText .
décrit ci-dessus dans ce document (en faisant Recopiez le programme source Java
attention aux majuscules / minuscules). HelloWorld décrit ci-dessus dans ce document
Enregistrez ce document dans un fichier (en faisant attention aux majuscules /
HelloWorld.java (Attention toujours aux minuscules).
majuscules / minuscules, c'est important) : enregistrez ce document dans un fichier
Enregistrez-le dans le même répertoire que le HelloWorld.java (Attention toujours aux
programme javac.exe ; javac.exe doit se trouver majuscules / minuscules, c'est important).
dans un répertoire du style c:\jdk...\bin. Le dossier où vous enregistrez vos
Vérifiez qu'à l'enregistrement notepad n'a pas programmes importent peu.
ajouté une extension .txt au fichier ; si c'est le Ouvrez un autre document avec SimpleText .
cas, renommez-le pour que ce soit exactement Recopiez le source HTML de HelloWorld
HelloWorld.java décrit à ci-dessus dans ce document.
ouvrez un autre document avec notepad.exe . Enregistrez ce document dans un fichier
Recopiez le source HTML de HelloWorld HelloWorld.html dans le même dossier que
décrit à ci-dessus dans ce document. celui où vous avez mis HelloWorld.java .
Enregistrez ce document dans un fichier Faites ensuite un drag-and-drop du fichier
HelloWorld.html dans le même répertoire que HelloWorld.java sur l'icône Java Compiler (si
celui où vous avez mis HelloWorld.java . vous utilisez le JDK de Sun) ou l'icône javac
Ouvrez une fenêtre de commandes MS/DOS. (si vous utilisez MRJ 2.x), pour compiler le
Avec la commande CD , allez dans le répertoire programme.
de javac.exe (cd c:\jdk...\bin). S'il y une erreur, c'est que vous n'avez pas
Tapez ensuite javac HelloWorld.java pour recopié exactement le programme HelloWorld
compiler le programme. à l'identique.
S'il y une erreur, c'est que vous n'avez pas Corrigez-le, enregistrez-le, et refaites la
recopié exactement le programme HelloWorld à procédure précédente, jusqu'à ce qu'il n'y ait
l'identique. plus d'erreur.
Corrigez-le, enregistrez-le, et refaites javac Faites ensuite un drag-and-drop du fichier
HelloWorld.java, jusqu'à ce qu'il n'y ait plus HelloWorld.html sur l'icône Applet Viewer (si
d'erreur. vous utilisez le JDK de Sun) ou l'icône Applet
Tapez finalement appletviewer Runner (si vous utilisez MRJ 2.x).
HelloWorld.html. Normalement une fenêtre Normalement une fenêtre s'ouvre affichant le
s'ouvre affichant le texte "Hello world !", texte "Hello world !", comme le montre
comme le montre l'applet ci-dessus. l'applet ci-dessus.
C'est tout. C'est tout.
Pour faire d'autres programmes, c'est toujours la Pour faire d'autres programmes, c'est toujours
même procédure : Reprenez un programme Java la même procédure : reprenez un programme
et créez un fichier HTML appelant l'applet Java et créez un fichier HTML appelant
grâce à la balise <APPLET ...>, compilez-le avec l'applet grâce à la balise <APPLET ...>,
javac et exécutez-le avec appletviewer suivi du compilez-le et exécutez-le avec Applet Viewer
nom du fichier HTML. ou Applet Runner .
vendredi 11 février 2000 Du C/C++ à Java : Démarrer en Java Page: 8

Enregistrer vos programmes dans le répertoire où se


trouve javac.exe n'est pas la solution idéale pour
organiser le rangement de vos programmes, mais vous
évite d'avoir à changer la variable d'environnement
PATH.

Une fois que vous commencez à comprendre comment marche un programme Java, vous pouvez en
modifier un existant ou en créer de complètement nouveaux, et vous pourrez considérer que vous savez
programmer en Java. Bonne chance...
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 1

Les notions de base

Objets, classes et héritage


Références
Les mots-clés de Java
Types de base
Structure d'un programme
Les packages

Objets, classes et héritage

Qu'entend on par langage objet, ou plus exactement langage orienté objet ? Le propos qui suit, n'est que
la nième réponse, qui complétera une littérature déjà vaste sur le sujet. Cette explication rapide vous
permettra de vous rappeler le vocabulaire couramment utilisé en programmation orientée objet.
L'exemple donné décrit un cas typique de programmation (gestion de compte en banque), plus parlant
que des cas qui peuvent paraître plus abstraits (les catégories de voitures, les espèces animales, etc...).
Le meilleur moyen d'expliquer la programmation objet passe en effet par l'exemple, car elle essaye de
s'appuyer sur l'habitude qu'a l'homme, de classer les objets du monde qui l'entourent en catégories,
sous-catégories, et ainsi de suite...

Il faut faire la différence entre la programmation objet et un langage objet : Un langage objet tel que Java,
Small Talk ou C++, est le moyen le plus aisé et le plus rapide de "programmer objet", mais la
programmation objet est plus un style de programmation que peut respecter un programme écrit en C ou
en PASCAL (plus difficilement tout de même !).

Mettons-nous donc à la place d'une banque : elle a des comptes à gérer, le votre, le mien et des milliers
d'autres. Tous ces comptes ont en commun un certain nombre de caractéristiques communes que vous
retrouvez sur votre relevé : un numéro et un solde, au minimum (même pas forcément une identité pour
certains comptes).
Le banquier va donc créer un modèle de relevé avec les cases Numéro et Solde. L'imprimeur va en tirer
des centaines d'exemplaires pour que le banquier puisse les remplir avec les informations de ses clients.
Le banquier gère aussi des comptes qui comportent d'autres informations dont il veut garder la trace. Il
n'a pas besoin de créer un modèle entièrement nouveau : à partir de son premier modèle, il crée d'autres
modèles de relevés : un pour les comptes de dépôts où il ajoute n cases Opération, un autre pour les
comptes d'épargne où il ajoute une case Taux d'intérêts :

Figure 2. Des objets "relevés de compte"

Notre banquier décide de s'informatiser, et engage un informaticien expert dans la technologie magique
dont il entend parler dans tous les journaux : "les technologies objets".
Celui-ci lui explique sa démarche : "Pour un informaticien, chaque relevé que vous imprimez est un
objet , chaque modèle que vous avez créé est une classe , et le premier modèle que vous avez créé est
une classe dont les modèles suivant ont hérité . Les cases Numéro, Solde, Opération, Taux d'Intérêt sont
des variables qui permettent de mémoriser l'état courant d'un compte et le solde se calcule grâce à une
méthode ".
Mais le banquier, pas dupe, lui répond : "Je ne veux pas vous acheter un dictionnaire !... Tout ça ne sont
que de nouveaux mots. Quelle est la vraie différence avec d'autres technologies classiques et éprouvées
?".
Aïe, aïe, expliquer la différence sans terme trop technique et fumeux ?!? "Une classe est une entité qui
forme un tout : chaque objet qui est une instance (désolé encore un nouveau mot !) d'une classe
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 2

comporte un ensemble de variables qui décrivent son état ET un ensemble de méthodes qui permettent
de le modifier : on appelle ceci l'encapsulation . L'héritage vous permet de créer de nouvelles classes
dérivées d'anciennes dont elle garde ou modifie les caractéristiques, sans que vous ayez à retoucher vos
anciennes classes. Convaincu ?" (Espérons-le...)

Les liens d'héritage définis entre les différentes classes d'un modèle définissent un graphe d'héritage ou
une hiérarchie de classes :

Figure 3. Graphe d'héritage

CompteDepot est une classe dérivée de Compte. Compte est la "super classe" de CompteEpargne et
CompteDepot, et CompteEpargne est la super classe de PEL. L'application Banque décrite au chapitre
suivant s'inspire du modèle décrit ici.

La différence principale entre une structure C et une classe est évidente : on ne peut pas déclarer des
méthodes (ou des fonctions) à l'intérieur du corps d'une structure C. A l'opposé, Java ne permet pas de
déclarer de méthodes en dehors du corps d'une classe.
Une classe peut comporter uniquement des variables sans méthodes ; elle peut aussi n'avoir que des
méthodes sans déclarer de variable.

L'héritage est différent de la composition : en C, vous pouvez créer une structure Compte et une
structure CompteEpargne, utilisant la première :

typedef struct
{
int numero;
float solde;
} Compte;

typedef struct
{
Compte compte;
float tauxInterets;
} CompteEpargne;

...
CompteEpargne unCompte;
unCompte.compte.numero = 1;
/* Pour accéder au numero vous passez par le champ */
/* compte de CompteEpargne */

En Java, vous pouvez utilisez la composition comme en C. Par contre, grâce à l'héritage, toutes les
variables (champs) et méthodes héritées sont accessibles directement comme si elles avaient été
déclarées par la classe dérivée elle-même.
De toute façon, ne confondez pas l'héritage et la composition : Bien que l'héritage soit une
caractéristique d'un langage objet, il ne faut pas se précipiter pour l'utiliser. Vous utiliserez sûrement
bien plus souvent la composition (en créant des classes qui sont l'assemblage de différents composants)
et à part pour les classes d'applets, la plupart de vos premières classes n'hériteront pas d'autres classes.
Pour vous en convaincre, vous n'avez qu'à étudier la hiérarchie des classes de la bibliothèque Java, et
vous verrez que la plupart des classes n'héritent pas les unes des autres.
L'héritage sert le plus souvent quand on veut modifier le comportement par défaut de classes existantes
(par exemple, modifier le comportement de la classe Applet), ou quand vous avez besoin d'ajouter des
fonctionnalités à une classe existante et que vous ne voulez pas modifier celle-ci parce qu'elle est déjà
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 3

utilisée (par exemple, le compteur de temps dérive d'un afficheur digital statique).

Références

La notion de référence est fondamentale en Java. La différence avec la notion de pointeur en C est
faible, mais essentielle :
Les variables en Java sont soit d'un type de base (byte, short, int, long, float, double, char ou
boolean), soit des références désignant des objets. Comme les pointeurs en C, ces références sont
comparables à des adresses mémoires permettant de désigner un objet alloué dynamiquement. Un même
objet peut être référencé par plusieurs variables à un moment donné.
Par contre, la comparaison s'arrête ici : en effet, en C un pointeur peut désigner un type de base (int*
par exemple), ou un autre pointeur (char** par exemple). Java lui, ne permet pas de déclarer une variable
qui serait une référence sur un type de base ou une référence sur une autre référence. Tout objet ne
peut être créé que dynamiquement grâce à l'opérateur new : ClasseObjet unObjet; ne crée aucun objet
en Java mais une référence sur un objet de classe ClasseObjet.

La notion de pointeur du C est remplacée par la notion de référence en Java, différente de celle du
C++. Les variables qui sont des références ne peuvent désigner que des objets alloués dynamiquement.
En C/C++, on utilise souvent une convention d'écriture pour les noms de variables permettant de
distinguer les variables qui sont des pointeurs et celles qui n'en sont pas,... comme par exemple :

struct Point
{
int x, y;
};

struct Point point1, // point1 n'est pas un pointeur


*ptrPoint2; // ptrPoint2 est un pointeur sur Point
int entier, // entier est d'un type de base
*ptrEntier; // ptrEntier est un pointeur sur int

Comme en Java il n'est possible de déclarer que des références comparables à ptrPoint2 ou des
variables d'un type de base comparables à entier, il n'est pas utile d'utiliser un qualificatif dans le nom
des variables qui permet de rappeler qu'une variable est une référence. En général, on écrit directement
point2.

Il ne faut pas voir la notion de référence comme une limitation de Java par rapport au C, mais plutôt
comme une simplification de la programmation : La seule chose réellement perdue est l'arithmétique de
pointeurs du C, par contre le gain en sécurité d'accès à la mémoire est important, car une référence ne
peut avoir pour valeur que null ou l'adresse valide d'un objet.

Les opérateurs * et & n'existent pas en Java. Le seul opérateur d'accès aux variables et aux méthodes
d'un objet est le point (.), et les opérateurs -> et :: du C++ sont absents. Tout ceci simplifie
grandement la manipulation des adresses.

La création d'objet et leur manipulation sont décrites au chapitre traitant de la création des classes.

Les mots-clés de Java


vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 4

abstract default if private throw

boolean do implements protected throws

break double import public transient

byte else instanceof return try

case extends int short void

catch final interface static volatile

char finally long super while

class float native switch

const for new synchronized

continue goto package this

goto et const sont des mots-clés qui sont réservés mais non utilisés par Java ; ils permettent
notamment au compilateur de vérifier qu'en cas de portage d'un programme écrit en C vers Java, des
mots-clés du C n'ont pas été oubliés...
null est une valeur utilisée pour les références inconnues et qui ne désignent aucun objet. Il ne s'écrit
pas en majuscules comme en C.

Les mots-clés du C/C++ absent en Java

auto extern register typedef

#define friend sizeof union

delete inline struct unsigned

enum operator template virtual

Types de base
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 5

VALEUR PAR
TYPE DESCRIPTION
DEFAUT
byte Entier signé occupant 8 bits (valeurs de -128 à 127) 0

short Entier signé occupant 16 bits (valeurs de -32768 à 32767) 0

int Entier signé occupant 32 bits (valeurs de -2147483648 à 0


21474836487).
Le type int occupe toujours la même taille en Java : 32 bits.

long Entier signé occupant 64 bits (valeurs de -9223372036854775808 à 0L


9223372036854775807)
Le type long occupe 64 bits en Java contrairement à 32 bits
en C.
float Nombre à virgule flottante occupant 32 bits (IEEE 754) 0.0f

double Nombre à virgule flottante occupant 64 bits (IEEE 754) 0.0d

char Caractère Unicode occupant 16 bits (valeurs littérales de '\u0000' à '\u0000'


'\uffff' avec 4 chiffres hexadécimaux obligatoires après \u).
Les 128 premiers caractères sont les codes ASCII et se notent
comme en C, entre '' ('a', '1',...). Voici la liste des caractères
compris entre '\u0080' et '\u00ff', qui contient notamment les
caractères accentués français :

Méfiez-vous car la plupart des éditeurs de texte ne génèrent pas à


la compilation la bonne valeur Unicode pour ces caractères.
Utilisez alors les valeurs hexadécimales ('\u00e9' au lieu de 'é'
par exemple).
Le type char occupe 16 bits en Java contrairement à 8 bits
en C. Utilisez byte pour un 8 bits. Les valeurs littérales des
caractères spéciaux sont les mêmes : '\n' pour un saut de
ligne, '\t' pour une tabulation, '\'' pour le caractère ',
'\"' pour le caractère ", '\\' pour le caractère \,...

boolean Booléen dont la valeur est true ou false false


Le type boolean n'existe pas en C. En Java, il est obligatoire
dans certaines expressions (if (...) par exemple).

Les variables de type float et double peuvent prendre aussi des valeurs correspondant à l'infini positif
ou négatif, ou représentant une valeur non significative. Voir les classes Float et Double.
Les valeurs littérales entières (byte, short, int et long) peuvent se noter de trois façons :

Comme une suite de chiffres décimaux : 3443, -123,...


Comme une suite de chiffres hexadécimaux (base 16) précédée de 0x : 0xFF, 0X12ab,...
Comme une suite de chiffres octaux (base 8) précédée de 0 : 0123, 056,...

Le modifieur unsigned du C n'existe pas en Java, où tous les types entiers sont signés. Comme en C,
les entiers peuvent prendre pour valeur des littéraux notés sous forme décimale (i=10) , hexadécimale
(i=0x0A) ou octale (i=012).

Chacun des types de base Java occupe toujours la même place mémoire quelque soit la plate-forme
d'exécution. La taille d'un entier de type int est toujours de 32 bits (ou 4 octets).
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 6

Les opérateurs qui s'appliquent à chacun des types de bases sont étudiés au chapitre sur les instructions
et les opérateurs.

Une valeur littérale d'un nombre à virgule flottante sans l'extension f ou d, comme par exemple 10.2
est considérée de type double.

Structure d'un programme

La structure d'un programme Java est plus simple qu'en C.


Chaque fichier qui le compose, se divise en trois parties (optionnelles) :

/* Début du fichier NouvelleClasse.java */

/* 1. Une éventuelle déclaration de package */


package nomDuPackage;

/* 2. Zéro ou plusieurs import */


import nomDeClasse; // Importer une classe sans package
import nomDuPackage.nomDeClasse; // Importer une classe d'un package
import nomDuPackage.*; // Importer toutes les classes d'un package

/* 3. Déclarations des classes et des interfaces du fichier */


public class NouvelleClasse // Une seule classe ou interface déclarée public,
{ // et par convention qui porte le même nom que le fichier
// Corps de NouvelleClasse
}

class NouvelleClasse2
{
// Corps de NouvelleClasse2
}

interface NouvelleInterface
{
// Corps de NouvelleInterface
}

// ...

/* Fin du fichier NouvelleClasse.java */

Les packages sont comparables à des sous-répertoires et sont traités au paragraphe suivant.

Les classes d'un même package peuvent s'utiliser mutuellement sans avoir à utiliser une clause import :
Si, par exemple, vous créez deux fichiers Classe1.java et Classe2.java déclarant respectivement les
classes Classe1 et Classe2, vous pouvez utiliser directement Classe2 dans le fichier Classe1.java.
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 7

Les commentaires s'écrivent de la même manière en Java qu'en C++ :

Tout ce qui suit // est ignoré jusqu'à la fin de la ligne.


Tout ce qui est compris entre /* et */ est ignoré. Ces commentaires peuvent incorporer des
commentaires écrits avec la syntaxe précédente //, mais pas d'autres commentaires avec la
syntaxe /* */.
Il est conseillé d'utiliser d'abord les commentaires avec // pour permettre d'imbriquer ce type de
commentaire, dans ceux utilisant /* */ au cas où vous ayez de besoin de commenter tout un
bloc. Par exemple :

class Classe1
{
/* Bloc inutilisé
int x = 1; // x sert à ...
*/

// ...
}

Il existe un troisième type de commentaire utilisant la syntaxe précédente : Les commentaires


javadoc . javadoc est une commande qui utilise tous les commentaires compris entre /** et */ et
respectant une syntaxe spéciale pour générer automatiquement une documentation des classes.
Toute la documentation des classes fournie par Javasoft est créée grâce à cet outil.
L'équivalent de #include est import.
Java ne requiert pas de déclaration externe via un fichier header .h ; les fichiers .java ou .class sont
suffisants au compilateur pour résoudre les références aux types externes. Le mot-clé extern est
inutile en Java.
Java est un langage "pur" objet et ne permet de définir au niveau global qu'UNIQUEMENT des
classes ou des interfaces : Pas de constantes, ni de macros (#define du C), pas de variables globales
qu'elles soient statiques ou non, pas de types autres que des classes (typedef est inutile), et toutes les
fonctions ne peuvent être déclarées que comme méthodes appartenant à une classe.
C'est la raison pour laquelle vous verrez que tous les exemples donnés déclarent des classes qui sont
souvent inutiles pour expliciter tel ou tel problème, mais obligatoires pour que l'exemple puisse être
compilé.

Les packages
import

Aussitôt que vous utilisez une classe fournie avec le Java Development Kit ou par d'autres sources, vous
devez indiquer à votre programme où la trouver. Pour cela, il faut utiliser la clause import pour chacune
(ou chaque groupe) des classes dont vous voulez vous servir dans un fichier .java . Ces clauses se
placent en début de fichier avant la déclaration de la première classe ou interface du fichier :

import nomDeClasse; // importer une classe sans package


import nomDuPackage.nomDeClasse; // importer une classe d'un package
import nomDuPackage.*; // importer toutes les classes d'un package

import est suivi soit directement d'un nom de classe, soit d'un nom de package, suivi lui-même d'un nom
de classe ou d'un astérisque (*). L'astérisque permet d'importer les classes d'un package à la demande,
c'est-à-dire que quand le compilateur recherchera une classe Classe1 qu'il ne connait pas encore, il
cherchera notamment dans les packages suivis d'un astérisque si Classe1 existe.
La classe nomDeClasse peut correspondre soit à un fichier source nomDeClasse.java, soit à un fichier
compilé nomDeClasse.class, dans lequel est définie la classe public à importer.
Un package représente une arborescence indiquant au compilateur quel chemin il faut emprunter pour
retrouver la classe. Par exemple, si le package est java.util, il va effectuer sa recherche dans le
répertoire java/util (ou java\util sous Windows). Mais où est ce répertoire java/util ? Vous ne
trouverez sûrement pas sur votre disque dur de répertoire java à la racine, et non plus dans le répertoire
courant où vous écrivez vos programmes... Comme la plupart des langages, le compilateur Java utilise
une variable système d'environnement indiquant l'ensemble des chemins prédéfinis à utiliser avec un
package pour construire le chemin complet d'accès aux classes : Sous UNIX et Windows, cette variable
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 8

s'appelle CLASSPATH. Vous pouvez aussi utiliser l'option -classpath avec les commandes javac et java ,
pour spécifier ce chemin.
Vous pouvez modifier cette variable pour y ajouter le chemin d'accès à d'autres bibliothèques Java ou à
vos propres packages, que vous créerez (les environnements de développement plus complets
permettent d'ajouter ces chemins plus simplement).
Le chemin correspondant à un package est donc un chemin relatif construit à partir du répertoire courant
de compilation ou aux chemins cités dans la variable d'environnement CLASSPATH.

import est optionnel dans deux cas :

Quand vous voulez utiliser dans un fichier UneClasse.java des classes définies dans des fichiers
situés dans le même répertoire que celui où se trouve UneClasse.java (toutes les classes public
ou non d'un même package / répertoire peuvent s'invoquer entre elles).
Quand vous utilisez une classe du package java.lang : Toutes les classes de ce package de base,
sont importées automatiquement à chaque compilation.

Si un package nomDuPackage comporte des sous-packages (par exemple nomDuPackage.sousPackage), la


clause import nomDuPackage.* n'importe pas ces sous packages. Il faut explicitement importer chacun
d'eux (avec par exemple import nomDuPackage.sousPackage.*).

Les packages peuvent servir aussi à donner un nom complet d'accès à une classe quand il y a risque de
conflit pour retrouver une classe : Par exemple, à chaque fois que vous avez besoin de la classe String,
vous pouvez utiliser String seul, mais, si dans un de vos fichiers .java vous définissez une autre classe
String (ce qui n'est pas très conseillé !), vous pourrez accéder dans le même fichier à votre nouvelle
classe String et à la classe String du package java.lang en écrivant pour cette dernière
java.lang.String, à chaque fois qu'il y a risque de confusion.
Cette caractéristique est particulièrement appréciable le jour vous serez amené à utiliser des packages de
sources différentes qui ont défini certaines classes avec le même nom.

import permet d'importer les classes définies dans d'autres répertoires, mais n'est pas obligatoire pour
importer entre elles les classes définies dans un même répertoire, et notamment le répertoire courant
de développement.
Toutes les classes que vous importez explicitement ou implicitement (parce qu'elles sont du même
package ou qu'elles sont du package java.lang), sont chargées dynamiquement une à une à l'exécution
d'un programme Java, la première fois qu'elles sont utilisées.
Comme il est décrit au premier chapitre, les liens ne sont donc pas statiques comme en C, où le link
rassemble dans un fichier exécutable tous les composants dont un programme a besoin pour
fonctionner. Chaque classe est mémorisée dans un fichier .class qui peut être comparé à une (petite)
librairie dynamique (ou DLL Dynamic Link Library ).

Définir un package

import permet d'importer n'importe quelle classe d'une bibliothèque, mais rien ne vous empêche de
créer votre propre bibliothèque, pour y rassembler par exemple un ensemble de classes utilisées comme
outils dans un ou plusieurs projets. Ceci se fait très simplement grâce à la clause package. Si cette clause
est utilisée, elle doit être définie en tête d'un fichier .java , comme suit :

package nomDuPackage;

Comme expliqué précédemment, le nom de package doit correspondre au chemin d'accès à la classe qui
utilise la clause package.
En général, une société qui s'appelle nomSociete utilise com.nomsociete comme base pour le nom des
packages des produits Java qu'elle livre, par exemple com.nomsociete.produitxxx pour le produit
produitxxx . Les classes de ce package devront être enregistrées dans le sous-répertoire
com/nomsociete/produitxxx.
Si dans ce sous-répertoire, vous créez une classe public Outil1 dans le fichier Outil1.java, chacune
des classes désirant utiliser la classe Outil1, devra inclure la clause import
com.nomsociete.produitXXX.Outil1; et le fichier Outil1.java devra définir la clause package
com.nomsociete.produitxxx;.
vendredi 11 février 2000 Du C/C++ à Java : Les notions de base Page: 9

La figure symbolisant les contrôles d'accès à des classes représente aussi un exemple simple d'utilisation
des packages.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 1

Création et utilisation des classes

Déclaration des classes et des interfaces


Déclaration des variables
Déclaration des méthodes
Création d'objets : opérateur new
Outrepasser une méthode
Destruction des objets

Déclaration des classes et des interfaces

Les seuls types que le programmeur peut définir en Java sont des classes ou des interfaces. Donc, les
mots-clé struct, union et enum n'existent pas en Java. De plus, les template n'existent pas.

Identifiants

Les identifiants que vous créez en Java (classes, interfaces, variables, méthodes,...) peuvent être
n'importe quelle suite illimitée de lettres ou de chiffres Unicode, de caractères _ ou $. Seul le premier
caractère ne doit pas être un chiffre. Il doit bien sûr être différent des mots-clés Java.
Par conséquent, il est possible d'utiliser des caractères accentuées pour une meilleure lisibilité de vos
programmes.

Les identifiants sont codés comme en C/C++, mais en Java vous pouvez utilisez en plus toutes les
lettres et tous les chiffres Unicode et le caractère $. Comme en C, le compilateur fait la nuance entre
les minuscules et les majuscules.

Vous pouvez créer des identifiants avec des lettres accentuées, ce qui n'est pas conseillé car la plupart
des éditeurs de texte et des systèmes fonctionnant en ligne de commande (comme MS/DOS ou
UNIX) n'utilisent pas Unicode pour les lettres accentuées (qui ne sont pas ASCII).

Les classes

La déclaration d'une classe peut prendre une des formes suivantes :

// Déclararation d'une classe simple


ModifieurDeClasse class NomDeClasse
{
// Corps de NomDeClasse :
// Déclaration des variables, des méthodes, des constructeurs
// et/ou initialisations static
}

// Déclaration d'une classe dérivant d'une super classe


ModifieurDeClasse class NomDeClasseDerivee extends NomDeSuperClasse
{
// Corps de NomDeClasseDerivee :
// Déclaration des variables, des méthodes, des constructeurs
// et/ou initialisations static
}

// Déclaration d'une classe implémentant une interface


ModifieurDeClasse class NomDeClasse2 implements NomInterface //, NomInterface2, ...
{
// Corps de NomDeClasse2 :
// Déclaration des variables, des méthodes, des constructeurs
// et/ou initialisations static
// et implémentation des méthodes de nomInterface
}
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 2

// Déclaration d'une classe dérivant d'une super classe et implémentant une interface
ModifieurDeClasse class NomDeClasse3 extends NomDeSuperClasse
implements NomInterface //, NomInterface2, ...
{
// Corps de NomDeClasse3 :
// Déclaration des variables, des méthodes, des constructeurs
// et/ou initialisations static
// et implémentation des méthodes de nomInterface
}

Une classe simple dérive implicitement de la classe Object (class nomDeClasse est équivalent à class
nomDeClasse extends Object). Java ne permet pas l'héritage multiple (une seule classe peut suivre la
clause extends), mais une classe peut implémenter plusieurs interfaces.
Le corps d'une classe est une suite quelconque de déclaration de variables, de méthodes, de
constructeurs et/ou d'initialisations static.
A partir de Java 1.1, le corps d'une classe peut aussi déclarer des classes internes, des interfaces internes
et des initialisations d'instance.

ModifieurDeClasse est optionnel et peut prendre une ou plusieurs des valeurs suivantes :

public : Une seule classe ou interface peut être déclarée public par fichier source .java . Par
convention, le fichier porte le nom de la classe déclarée public. Si d'autres classes (non public)
sont déclarées dans un fichier Classe1.java, elles ne peuvent être utilisés que dans les fichiers qui
appartiennent au même package que Classe1.java.
final : Une classe déclarée final ne peut être dérivée et ne peut donc jamais suivre la clause
extends. Cette clause peut être utile quand vous considérez qu'une classe ne doit pas ou n'a pas
besoin d'être dérivée.
abstract : Il est impossible de créer une instance d'une classe déclarée abstract. Cette catégorie
de classe peut comporter une ou plusieurs méthodes déclarées abstract. Par contre, si une classe
comporte une méthode abstract, elle doit être être déclarée abstract.
A quoi sert une classe abstract si on ne peut créer aucun objet de cette classe ? Ce type de classe
est utilisé pour fournir des méthodes et des variables communes à toutes les classes qui en
dérivent. L'intérêt des classes abstract est démontrée plus loin dans ce chapitre.
Une classe ne peut être déclarée abstract et final (elle ne servirait à rien puisqu'il serait
impossible de créer des classes dérivées de celle-ci).

figure 4. Symbolisation des contrôles d'accès aux classes

Contrairement au C++, le point-virgule en fin de déclaration d'une classe est optionnel.


En Java, on utilise la clause extends pour préciser la super classe d'une classe dérivée, à la place des
deux points qui suivent la déclaration d'une classe en C++.
L'héritage se fait systématiquement de manière public en Java. Il n'existe pas d'équivalence à la
déclaration C++ : class Classe2 : private Classe1 { /* ... */ }.
Une classe abstraite doit être déclarée abstract en Java et peut contenir aucune ou plusieurs méthodes
abstract (équivalent des méthodes virtuelles pures du C++).
Les modifieurs de classe public et final n'ont pas d'équivalent en C++.
En Java une classe et tout ce qu'elle contient devant être déclarée entièrement dans un seul fichier, il
n'est pas possible comme en C++ de répartir les méthodes d'une même classe sur plusieurs fichiers.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 3

Les interfaces

Une interface est une catégorie un peu spéciale de classe abstract, dont le corps ne contient que la
déclaration de constantes et de méthodes abstract :

// Déclararation d'une interface simple


ModifieurInterface interface NomInterface
{
// Corps de NomInterface :
// Déclaration des constantes et des méthodes non implémentées
}

// Déclaration d'une interface dérivant d'une super interface


ModifieurInterface interface NomInterfaceDerivee extends NomSuperInterface
{
// Corps de NomInterfaceDerivee :
// Déclaration des constantes et des méthodes non implémentées
}

A partir de Java 1.1, le corps d'une interface peut aussi déclarer des classes internes et des interfaces
internes.
II est impossible de créer une instance d'une interface. Une ou plusieurs interfaces peuvent suivre la
clause extends.
Une classe non abstract, qui implémente une interface Interface1 doit implémenter le code de
chacune des méthodes de Interface1. L'implémentation d'une méthode est l'ensemble des instructions
que doit exécuter une méthode.
ModifieurInterface est optionnel et peut prendre une ou plusieurs des valeurs suivantes :

public : Une seule interface ou classe peut être déclarée public par fichier source. Par convention,
le fichier porte le nom de l'interface déclarée public. Si d'autres interfaces (non public) sont
déclarées dans un fichier Interface1.java, elles ne peuvent être utilisés que dans les fichiers qui
appartiennent au même package que Interface1.java. Une interface ne peut porter le même nom
qu'une classe.
abstract : Toute interface est implicitement abstract. Ce modifieur est permis mais pas
obligatoire.

A quoi sert une interface et quelle est la différence avec une classe abstract ?
Tout d'abord, vous noterez qu'une classe ne peut hériter que d'une super classe, mais par contre peut
implémenter plusieurs interfaces. Ensuite, une classe abstract peut déclarer des variables et le code de
certaines méthodes.

Soit une classe Classe1 qui implémente toutes les méthodes d'une interface Interface1. Cette
classe peut déclarer d'autres méthodes, ce qui compte c'est que chaque instance de cette classe
garantit qu'au moins toutes les méthodes d'Interface1 existent et peuvent être appelées, comme
dans l'exemple suivant :

interface CouleurDominante
{
// Déclaration d'une méthode QuelleCouleurDominante ()
}

class Classe1 extends SuperClasse1 implements CouleurDominante


{
// Corps de la méthode QuelleCouleurDominante ()

// Autres méthodes éventuelles


}

class Classe2 extends SuperClasse2 implements CouleurDominante


{
// Corps de la méthode QuelleCouleurDominante ()

// Autres méthodes éventuelles


}

class ClasseAyantBesoinDeLaCouleurDominante
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 4

{
CouleurDominante objetColore;
// On peut affecter à objetColore une référence
// à un objet de classe Classe1 ou Classe2
// et ainsi obtenir la couleur dominante d'un objet coloré en invoquant
// la méthode QuelleCouleurDominante () sur la variable objetColore
}

Une interface peut être utilisée pour masquer l'implémentation d'une classe. Ce concept est utilisé
dans plusieurs packages Java dont java.awt.peer : Ce package déclare un ensemble d'interfaces qui
offrent les mêmes méthodes sur chaque Machine Virtuelle Java mais qui sont implémentées
différemment pour que l'interface utilisateur du système (bouton, fenêtre,...) soit utilisée.
Une interface vide (comme l'interface Cloneable) permet de créer une catégorie de classes : chaque
classe implémentant ce type d'interface appartient à telle ou telle catégorie. Pour tester si la classe
d'un objet appartient à une catégorie, il suffit d'utiliser l'opérateur instanceof avec le nom de
l'interface.
Une interface peut servir aussi pour déclarer un ensemble de constantes, qui seront utilisées dans
des classes sans lien d'héritage entre elles, comme dans l'exemple suivant :

interface ConstantesCommunes
{
// Déclaration des constantes A, B et C
}

class Classe1 extends SuperClasse1 implements ConstantesCommunes


{
// Les constantes A, B et C sont utilisables dans la classe Classe1
}

class Classe2 extends SuperClasse2 implements ConstantesCommunes


{
// Les constantes A, B et C sont utilisables dans la classe Classe2
}

Une classe qui implémente une interface InterfaceDerivee dérivée d'une autre interface Interface1
doit implémenter les méthodes des deux interfaces InterfaceDerivee et Interface1.
Java ne permet pas l'héritage multiple. Mais l'utilisation des interfaces peut compenser cet absence, car
une classe peut implémenter plusieurs interfaces. Voir aussi le chapitre sur le portage.

Si vous avez quelque mal au départ à comprendre le concept d'interface et leur utilité, considérez les
simplement comme des classes abstract, déclarant des constantes et des méthodes abstract. Vous en
percevrez leur intérêt au fur et à mesure que vous serez amené à vous en servir.

Déclaration des variables

Le corps d'une classe est un ensemble de déclarations de variables et de méthodes implémentées ou


non, déclarées dans n'importe quel ordre.

Syntaxe

Les déclarations de variables se font comme en C/C++ :

class Classe1
{
// Déclaration de variables
TypeVariable variable1;
TypeVariable variable2, variable3;
ModifieurDeVariable TypeVariable variable4;
TypeVariable variable5 = valeurOuExpression ; // Initialisation d'une variable

TypeVariable tableau1 [ ]; // Création d'une référence sur un tableau


// Allocation d'un tableau de taille n initialisé avec les n valeu
TypeVariable tableau2 [ ] = {valeur1, valeur2, /*..., */ valeurn};

// ...
}
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 5

TypeVariable est soit un type de base, soit le nom d'une classe, soit le nom d'une interface. Dans ce
dernier cas, la variable est une référence sur un objet.
Les tableaux sont cités ici en exemple de variable et sont traités dans la partie sur les tableaux.

ModifieurDeVariable est optionnel et peut être un ou plusieurs des mots-clés suivants :

public, protected ou private :


Une variable public est accessible partout où est accessible la classe Classe1 dans laquelle
elle est déclarée.
Une variable protected est accessible par les autres classes du même package que Classe1,
et par les classes dérivées de Classe1.
Une variable private n'est accessible qu'à l'intérieur du corps de Classe1.
static : Si une variable est static, elle est créée en un unique exemplaire quelque soit le nombre
d'instance de Classe1 : c'est une variable de classe. L'accès à cette variable se fait grâce à
l'opérateur point (.) en indiquant le nom de la classe ou une référence à un objet
(Classe1.variable ou objetClasse1.variable).
A l'opposé, si une variable n'est pas static, chaque nouvelle instance objetClasse1 de Classe1
créera une variable pour objetClasse1 : c'est une variable d'instance. L'accès à cette variable se fait
grâce à l'opérateur point (.) , de la manière objetClasse1.variable.
final : Une variable final n'est pas modifiable une fois initialisée : en fait, c'est une constante qui
peut prendre une valeur initiale différente d'un objet à l'autre d'une même classe. Si cette variable
prend toujours la même valeur d'initialisation, il vaut mieux optimiser votre code en y ajoutant
static pour en faire une constante de classe.
Les variables déclarées dans le corps d'une interface sont des constantes implicitement public,
final et static, et doivent être initialisées avec une expression constante.
transient : Une variable transient sert à indiquer aux méthodes gérant la persistance qu'elle ne
fait pas partie de la persistance de l'objet. Cette fonctionnalité n'a été mise en œuvre qu'à partir de
Java 1.1, et sert à éviter de sauvegarder des variables ne servant qu'à des calculs intermédiaires
(indices par exemple).
volatile : Une variable volatile permet d'être sûr que deux threads (tâches) auront accès de
manière ordonnée à cette variable (modifieur implémenté uniquement à partir de Java 1.1). Voir
aussi le chapitre sur les threads. Une variable volatile ne peut être aussi final.

figure 5. Symbolisation des contrôles d'accès aux variables

Le contrôle d'accès à une variable est soit public, soit protected, soit private, soit par défaut, si aucun
de ces modifieurs n'est précisé, amical (friendly en anglais) : la variable est alors accessible uniquement
par les autres classes du même package. Les liens de la figure précédente indique les endroits où il est
possible d'utiliser une variable. Comme vous pouvez le voir, l'utilisation d'une variable se fait
directement par son nom à l'intérieur d'une classe et de ses classes dérivées, sans besoin d'utiliser
l'opérateur point (.).
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 6

L'initialisation d'une variable se comporte exactement comme l'affectation. Si la variable est static, alors
l'initialisation est effectuée au chargement de la classe.
Les variables non initialisées prennent obligatoirement une valeur par défaut (voir le tableau sur les types
de base). Si cette variable est une référence à un objet, la valeur par défaut est null.

Une classe Classe1 peut déclarer une variable qui est une référence à une classe Classe2 déclarée
après Classe1 (pas besoin de déclarer les types avant de les utiliser comme en C).
En Java, le modifieur final est utilisé pour déclarer une constante (pas de #define, ni de const).
Contrairement au C/C++, Java permet d'initialiser à la déclaration les variables de classe ainsi que les
variables d'instance.
Les variables d'instance et de classe sont toutes initialisées à une valeur par défaut. En C++, il est
obligatoire d'initialiser les variables de classe à part ; en Java, soit ces variables prennent une valeur par
défaut, soit elles sont initialisées à leur déclaration, soit elles sont initialisées dans un bloc
d'initialisation static.
L'accès aux variables static se fait grâce à l'opérateur point (.) et pas l'opérateur ::, comme en C++.
De plus, si vous voulez donner une valeur par défaut aux variables static, vous le faites directement à
la déclaration de la variable, comme par exemple static int var = 2;.
Le contrôle d'accès aux variables (et aux méthodes) se fait pour chacune d'elles individuellement, et
pas en bloc comme en C++ (avec par exemple public :).
Les variables (et les méthodes) d'une classe Classe1 dont le contrôle d'accès est protected sont
accessibles par les classes dérivées de Classe1 comme en C++, mais aussi par les classes du même
package que Classe1.
En Java, les variables (et les méthodes) d'une classe Classe1 ont un contrôle d'accès par défaut qui est
amical (friendly ), c'est à dire qu'elles sont accessibles uniquement par les autres classes du même
package que Classe1. En C++, cette notion n'existe pas et par défaut le contrôle d'accès est private.
Le contrôle d'accès par défaut de Java est très pratique car il donne accès à toutes les variables et
toutes les méthodes des classes d'un même package. Mais attention, si après avoir développé certaines
classes, vous pensez qu'elles peuvent vous être utiles pour d'autres programmes et qu'ainsi vous les
mettez dans un nouveau package outils, il vous faudra ajouter les contrôles d'accès public pour
accéder en dehors du package outils aux méthodes et variables dont vous avez besoin.
Donc, prenez l'habitude de préciser dès le départ les contrôles d'accès des variables et des méthodes.

Initialisations static

Le corps d'une classe peut comporter un ou plusieurs blocs d'initialisation static. Ces blocs sont
exécutés au chargement d'une classe, et permettent d'exécuter des opérations sur les variables static. Ils
sont exécutés dans l'ordre de déclaration et peuvent ne manipuler que les variables static déclarées
avant le bloc.

class Classe1
{
// Déclaration de variables static
static int variable1 = 10;
static int variable2;

static
{ // Bloc static
variable2 = variable1 * 2;
}

// ...
}
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 7

Sur une même Machine Virtuelle Java, vous pouvez très bien exécuter différentes applets ou
applications l'une après l'autre ou en même temps grâce au multi-threading. Par contre, chaque classe
ClasseX n'existe qu'en un seul exemplaire pour une Machine Virtuelle, même si ClasseX est utilisée
par différentes applets.
Ceci implique que les variables static de ces classes sont uniques pour une Machine Virtuelle, et
partagées entre les différentes applets. Donc, attention aux effets de bord ! Si vous modifiez la valeur
d'une variable static dans une applet, elle sera modifiée pour toutes les applets amenées à utiliser
cette variable.
Ceci est à opposer au C, où les variables static sont uniques pour chaque contexte d'exécution d'un
programme.

Initialisations d'instance

A partir de Java 1.1, le corps d'une classe peut comporter un ou plusieurs blocs d'initialisation d'instance.
Comme pour les constructeurs, ces blocs sont exécutés à la création d'un nouvel objet dans l'ordre de
déclaration.

class Classe1
{
// Déclaration d'une variable comptant le nombre d'instances créées
static int nombreInstances = 0;
// Déclaration d'une variable d'instance
int variable1;

{ // Bloc d'instance
variable1 = 10;
nombreInstances++;
}

// ...
}

Déclaration des méthodes

Syntaxe

Les déclarations des méthodes en Java ont une syntaxe très proche de celles du C/C++ :

class Classe1
{
// Déclarations de méthodes
TypeRetour methode1 (TypeParam1 param1Name /*,... , TypeParamN paramNName*/)
{
// Corps de methode1 ()
}

ModifieurDeMethode TypeRetour methode2 (TypeParam1 param1Name /* ... */)


{
// Corps de methode2 ()
}

ModifieurDeMethode TypeRetour methode3 (/* ... */) throws TypeThrowable


/*, TypeThrowable2 */
{
// Corps de methode3 ()
}

// Déclaration d'une méthode abstract


// Dans ce cas, Classe1 doit être aussi abstract abstract ModifieurDeMethode TypeRetour method

// Déclaration d'une méthode native


native ModifieurDeMethode TypeRetour methode4 (/* ... */);

// ...
}

TypeRetour peut être :

soit un type (de base, une classe ou une interface) : dans ce cas, la méthode doit utiliser
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 8

l'instruction return suivie d'une valeur du type TypeRetour , pour renvoyer une valeur. Le type
peut être une référence à un tableau en utilisant [ ] (par exemple, int [ ] methode ()).
soit void quand la méthode ne renvoie pas de valeur.

TypeThrowable doit être une classe dérivée de la classe Throwable. Les exceptions qui sont déclenchées
via l'instruction throw doivent avoir leur classe déclarée après la clause throws. Voir le chapitre traitant
des exceptions.

ModifieurDeMethode est optionnel et peut être un ou plusieurs des mots-clés suivants :

public, protected ou private :


Une méthode public est accessible partout où est accessible la classe Classe1 dans laquelle
elle est déclarée. Ce sont les méthodes public que l'utilisateur d'une classe peut appeler.
Une méthode protected est accessible par les autres classes du même package que Classe1,
et par les classes dérivées de Classe1.
Une méthode private n'est accessible qu'à l'intérieur du corps de Classe1. Les méthodes
private sont typiquement des routines internes de calcul pour lesquels il n'y a pas de raison
de donner un accès à l'utilisateur d'une classe.
static : Si une méthode est static, c'est une méthode de classe qui ne peut utiliser que les
variables et les méthodes de Classe1, qui sont déclarées static. L'appel à cette méthode se fait
grâce à l'opérateur point (.) en indiquant le nom de la classe ou une référence à un objet
(Classe1.methode () ou objetClasse1.methode ()).
A l'opposé, si une méthode n'est pas static, c'est une méthode d'instance, accessible pour chaque
instance objetClasse1 de Classe1 grâce à l'opérateur point (.) , de la manière
objetClasse1.methode ().
Les méthodes déclarées dans le corps d'une interface ne peuvent être static.
final : Une méthode final ne peut pas être outrepassée par les classes dérivant de Classe1.
abstract : Une méthode abstract permet de déclarer une méthode d'instance sans en donner
l'implémentation, et ne peut apparaître qu'au sein d'une classe abstract. Toute classe non abstract
dérivée de cette classe, doit implémenter cette méthode, en l'outrepassant.
Les méthodes déclarées dans le corps d'une interface sont implicitement abstract et public.
native : Une méthode native est implémentée dans une bibliothèque annexe propre à la
plateforme de développement qui peut être développée en C ou en C++ par exemple. Ceci permet
de faire appel à certaines fonctionnalités propres à la plateforme ciblée, qui ne seraient pas
disponibles en Java. Mais une méthode native n'est pas portable...
synchronized : Une méthode synchronized permet d'obtenir un verrou sur l'objet sur lequel elle
est appelée (ou sur la classe si la méthode est aussi static). Ce verrou empêche qu'en cas de
programmation multi-threads (multitâches), différents threads aient accès de manière simultanée à
un même objet. Voir aussi la synchronisation des threads.

Le contrôle d'accès à une méthode est soit public, soit protected, soit private. Leur utilisation est la
même que pour les variables.
Dans la plupart des cas, il est conseillé de ne rendre public que les méthodes et les constantes
(variables final static), dont a besoin l'utilisateur d'une classe. Les autres variables sont déclarées
private voir friendly ou protected et sont rendues accessibles si besoin est, par des méthodes public
permettant de les interroger et de les modifier (get... () et set... ()). Ceci permet de cacher aux
utilisateurs d'une classe ses variables et de vérifier éventuellement les conditions pour modifier une
variable.
Ce style de programmation est largement utilisé dans la bibliothèque Java.

Pour les méthodes non abstract et non native, le corps de la méthode est un bloc, comportant une
suite d'instructions.
A l'intérieur de la déclaration d'une classe Classe1, l'appel à toute méthode methode () de Classe1 ou de
ses super classes, peut se faire directement sans l'opérateur point (.) ; l'utilisation de cet opérateur n'est
obligatoire que pour accéder aux méthodes des autres classes, comme dans l'exemple suivant :

class Classe1
{
static public int Factorielle (int i)
{
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 9

if (i == 0)
return 1;
else
return i * Factorielle (i - 1);
// Factorielle () est directement accessible à l'intérieur de Classe1
}
}

class Classe2
{
// Pour accéder à la méthode Factorielle () de Classe1
// vous devez utilisez l'opérateur point.
int factorielle10 = Classe1.Factorielle (10);
}

Java ne permet pas l'utilisation des listes d'arguments variables qui existent en C (défini avec ...). Cette
absence peut être partiellement détournée grâce à l'utilisation de la surcharge des méthodes.
En Java, chaque paramètre doit être déclarer avec son type et un nom. En C++, quand un paramètre
est requis mais n'est pas utilisé dans une méthode, il n'est pas obligatoire de spécifier un nom, comme
pour le deuxième paramètre de l'exemple void methode1 (int a, float).
A l'opposé du C++, il est possible de donner le même nom à une variable et à une méthode (à éviter
pour ne pas nuire à la lisibilité du programme).
Contrairement au C/C++, dans une classe Classe1, vous pouvez utiliser toutes les variables et les
méthodes de Classe1 dans le corps de ses méthodes qu'elles soient déclarées avant ou après dans
Classe1, comme dans l'exemple suivant :

class Classe1
{
void methode1 ()
{
x = 1; // x est déclaré après
methode2 (); // methode2 () est déclarée après
}

void methode2 ()
{
}

int x;
}

Java ne permet pas de déclarer de variables ou de fonctions globales. Mais, si vous tenez absolument à
garder le style de programmation procédurale du C, vous pouvez créer et utiliser des variables et des
méthodes static, dans ce but.
La notion de fonction "amie" du C++ (friend) n'existe pas en Java : aucune méthode ne peut être
déclarée en dehors d'une classe.
Contrairement au C++, toutes les méthodes d'instance non private sont virtuelles en Java. Donc le
mot-clé virtual est inutile, ce qui peut éviter certains bugs difficiles à déceler en C++.
Les méthodes abstract sont l'équivalent des méthodes virtuelles pures du C++ (qui se déclarent en
faisant suivre la déclaration de la méthode de = 0).
Java introduit le mot-clé final. Ce modifieur empêche d'outrepasser une méthode dans les classes
dérivées. Cette notion est absente du C++.
Toutes les méthodes Java sont déclarées et implémentées à l'intérieur de la classe dont elles dépendent.
Mais, cela n'a pas pour conséquence de créer comme en C++ toutes les méthodes de Java inline !
Avec l'option d'optimisation (-O), le compilateur lui-même évalue les méthodes final qui peuvent être
traitées inline (remplacement de l'appel à la méthode par le code implémentant la méthode) : Donc, il
est important d'utiliser ce modifieur quand cela est nécessaire (pour les méthodes d'accès aux variables
par exemple).
La surcharge des opérateurs n'existe pas en Java. Seule la classe String autorise l'opérateur + pour la
concaténation.
Java ne permet pas de donner aux paramètres des valeurs par défaut comme en C++ (void f (int x,
int y = 0, int z = 0); ). Vous êtes obligés de surcharger une méthode pour obtenir les mêmes
effets, en créant une méthode avec moins de paramètres qui rappellera la méthode avec les valeurs par
défaut.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 10

L'appel aux méthodes static se fait grâce à l'opérateur point (.) et pas l'opérateur ::, comme en C++.

Une méthode reçoit la valeur de chacun des paramètres qui lui sont passés, et ces paramètres se
comportent comme des variables locales :

1. Si un paramètre param est une référence sur un objet objet1, alors vous pouvez modifier le
contenu de objet1 ; par contre, si vous affectez à param une référence sur un autre objet, cette
modification n'aura d'effet qu'à l'intérieur du corps de la méthode. Si vous voulez mimer le
passage par valeur, vous pouvez utiliser la méthode clone () de la classe Object pour créer une
copie de l'objet objet1.
2. Si un paramètre est d'un type base, la modification de sa valeur n'a de portée qu'à l'intérieur du
corps de la méthode. Si vous voulez prendre en compte en dehors de la méthode la modification
du paramètre, vous serez obligé de créer un objet dont la classe comporte une variable
mémorisant cette valeur. Vous pourrez alors modifier la valeur comme indiqué en 1.
(Voir aussi le chapitre traitant du portage de programmes C/C++ en Java).

Surcharge des méthodes

Une méthode methodeSurchargee (), est surchargée (overloaded ) quand elle est déclarée plusieurs fois
dans une même classe ou ses classes dérivées, avec la même nom mais des paramètres de types
différents, ou de même type mais dans un ordre différent, comme dans l'exemple suivant :

class Classe1
{
void methodeSurchargee (int entier)
{
// Corps de methodeSurchargee ()
}
void methodeSurchargee (float nombre)
{
// Corps de methodeSurchargee ()
}
}

class Classe2 extends Classe1


{
// Classe2 hérite de Classe1 donc elle déclare
// implicitement toutes les méthodes de Classe1

void methodeSurchargee (float nombre, short param)


{
// Corps de methodeSurchargee ()
}
}

Il est autorisé de surcharger une méthode en utilisant des paramètres de types différents pour chaque
méthode. Les valeurs de retours peuvent être aussi différentes, mais il est interdit de créer deux
méthodes avec les mêmes paramètres et un type de valeur de retour différent (par exemple, int
methode () et float methode ()).
En Java, une classe hérite de toutes les méthodes de la super classe dont elle dérive, même si elle
surcharge une ou plusieurs des méthodes de sa super classe. Dans l'exemple précédent, contrairement
au C++, les méthodes que l'on peut invoquer sur un objet de classe Classe2 sont les 3 méthodes
methodeSurchargee (int entier), methodeSurchargee (float nombre) et methodeSurchargee (float
nombre, short param). En C++, le fait de surcharger la méthode methodeSurchargee () dans Classe2,
interdit d'appeler directement sur un objet de classe Classe2 les méthodes surchargées de Classe1.

Constructeur

Chaque variable d'une classe peut être initialisée à une valeur par défaut à sa déclaration. Mais si vous
voulez initialiser certaines de ces variables avec une valeur donnée à la création d'un nouvel objet, il
vous faut déclarer une méthode spéciale appelée un constructeur.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 11

Un constructeur est appelée automatiquement à la création d'un objet, et les instructions du corps d'un
constructeur sont généralement destinées à initialiser les variables de l'objet nouvellement créé avec les
valeurs récupérées en paramètre. Il a une syntaxe un peu différente de celle des méthodes :

class Classe1
{
// Déclaration du constructeur sans paramètre remplaçant le constructeur par défaut
public Classe1 ()
{
// Corps du constructeur
}

ModifieurDeConstruceur Classe1 (TypeParam1 param1Name /* ... */)


{
// Corps du constructeur
}

ModifieurDeConstruceur Classe1 (/* ... */) throws TypeThrowable /*, TypeThrowable2 */


{
// Corps du constructeur
}
}

Un constructeur porte le même nom que la classe où il est déclaré, et n'a pas de type de retour. A
l'usage vous verrez que c'est une des méthodes qui est le plus souvent surchargée.
Toute classe qui ne déclare pas de constructeur a un constructeur public par défaut sans paramètre qui
ne fait rien. Aussitôt qu'un constructeur est déclaré avec ou sans paramètre, le constructeur par défaut
n'existe plus. Si vous avez déclarer dans une classe Classe1 un constructeur avec un ou plusieurs
paramètres, ceci oblige à préciser les valeurs de ces paramètres à la création d'un objet de la classe
Classe1.

TypeThrowable est une classe dérivée de la classe Throwable. Les exceptions qui sont déclenchées via
l'instruction throw doivent avoir leur classe déclarée après la clause throws. Voir le chapitre traitant des
exceptions.

ModifieurDeConstruceur est optionnel et peut être un des mots-clés suivants : public, protected ou
private. Ils sont utilisés de la même manière que pour les déclarations de méthodes.

Voici par exemple, une classe Classe1 n'utilisant pas de constructeur transformée pour qu'elle utilise un
constructeur :

class Classe1 class Classe1


{ {
int var1 = 10; int var1;
int var2; int var2;
}
public Classe1 (int valeur)
{
// Initialisation de var1 avec valeur
var1 = valeur;
}
}

Le constructeur Classe1 (int valeur) sera appelé avec la valeur donnée par l'instruction de création
d'un objet de classe Classe1. Ce constructeur permet ainsi de créer un objet de classe Classe1 dont la
variable var1 est initialisée avec une valeur différente pour chaque nouvel objet.
Mais quel est l'intérêt d'un constructeur puisqu'il est toujours possible de modifier une variable d'un
objet après sa création ?
Un constructeur permet d'initialiser certaines variables d'un objet dès sa création, ce qui permet de
garantir la cohérence d'un objet. En effet, même si vous précisez aux utilisateurs d'une classe qu'ils
doivent modifier telle ou telle variable d'un nouvel objet avant d'effectuer certaines opérations sur
celui-ci, rien ne garantit qu'ils le feront effectivement, ce qui peut être source de bugs.
Un constructeur permet justement d'éviter ce genre de problème car toutes les variables d'un objet
seront correctement initialisées au moment de sa création, ce qui garantit que les utilisateurs d'une classe
pourront effectuer n'importe quelle opération sur un objet juste après sa création.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 12

La plupart des classes de la bibliothèque Java utilisant un ou plusieurs constructeurs, vous serez souvent
amener à les utiliser en créant des objets et ceci vous permettra de comprendre comment en déclarer
vous-même dans vos propres classes.

Le corps d'un constructeur peut éventuellement commencé par une des deux instructions suivantes :

this (argument1 /*, argument2, ...*/);


super (argument1 /*, argument2, ...*/);

La première instruction permet d'invoquer un autre constructeur de la même classe : il est souvent utilisé
par un constructeur pour passer des valeurs pas défaut aux paramètres d'un autre constructeur.
La seconde instruction permet d'appeler un constructeur de la super classe pour lui repasser des valeurs
nécessaires à l'initialisation de la partie de l'objet dépendant de la super classe, comme dans l'exemple
suivant :

class Classe1
{
int variable;
Classe1 (int var)
{
variable = var;
}
}

class Classe2 extends Classe1


{
int variable2;
Classe2 (int var)
{
// Appel du constructeur de Classe1 avec la valeur 3
super (3);
variable = var;
}

Classe2 ()
{
// Appel du premier constructeur avec la valeur 2
this (2);
}
}

Si aucune des instructions précédentes n'est citée, Java considère qu'il y a implicitement un appel super
();. Ceci implique que la super classe doit avoir un constructeur sans paramètre (déclaré explicitement
ou fourni par Java par défaut), et que par enchaînement, la création de tout nouvel objet de classe
invoquera le constructeur de sa classe et tous les constructeurs de des super classes.
Donc si comme dans l'exemple précédent vous créez une classe Classe2 qui dérive d'une classe Classe1
n'ayant pas de constructeur par défaut ou sans paramètre, vous serez obliger de déclarer au moins un
constructeur dans Classe2 qui rappelle un constructeur de la classe Classe1 avec l'instruction super
(...);.

A partir de Java 1.1, le corps d'une classe peut comporter aussi un ou plusieurs blocs d'initialisation
d'instance, qui sont comparables au constructeur par défaut. A la création d'un objet, un objet d'une
classe Classe1 est initialisé dans l'ordre suivant :

Si Classe1 hérite d'une super classe Classe0, appel d'un constructeur de Classe0 soit par un appel
explicite à super (...), ou implicitement si Classe0 possède une constructeur par défaut.
Exécution d'éventuels blocs d'initialisation d'instance déclarés dans Classe1.
Exécution du constructeur de Classe1.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 13

Java introduit le mot-clé super : il permet de passer des valeurs d'un constructeur d'une classe au
constructeur de sa super classe grâce à l'appel super (arguments). En C++, il faut donner les
paramètres à passer au(x) constructeur(s) des supers classes à la suite de la déclaration d'un
constructeur.
Contrairement au C++, les constructeurs d'une même classe peuvent s'appeler entre eux en Java grâce
à this (...). Cette fonctionnalité est très pratique pour remplacer l'absence de valeur par défaut des
paramètres des constructeurs.
Java ne permet de passer les objets en paramètre que par référence. Le constructeur par recopie du
C++, appelé pour construire les objets passés par valeur, n'est pas utile.

Création d'objets : opérateur new

La création d'objet (on dit aussi l'instanciation d'une classe) se fait grâce à l'opérateur new suivi d'un nom
de classe et des arguments envoyés au constructeur :

new Classe1 (/* argument1, argument2, ...*/)

Un nouvel objet de classe Classe1 est créé, l'espace mémoire nécessaire pour les variables d'instance est
alloué, ces variables sont ensuite initialisées puis finalement le constructeur correspondant aux types des
arguments est appelé.
La valeur renvoyée par l'opérateur peut être affectée à une référence de classe Classe1 ou de super
classe de Classe1 (voir les casts).
Si Classe1 n'a pas encore été utilisée, l'interpréteur charge la classe, alloue l'espace mémoire nécessaire
pour mémoriser les variables static de la classe et exécutent les initialisations static.

A partir de Java 1.1, il est possible de créer des objets ayant une classe anonyme.

La méthode newInstance () de la classe Class permet aussi de créer un nouvel objet. Cette méthode est
équivalente à utiliser new sans argument, et donc si vous voulez utiliser newInstance () pour créer un
objet de classe Classe1, Classe1 doit avoir un constructeur sans paramètre (celui fourni par défaut ou
déclaré dans la classe), sinon une exception NoSuchMethodError est déclenchée. Voir aussi l'application
InstantiationAvecNom .
A partir de Java 1.1, les méthodes newInstance () des classes java.lang.reflect.Constructor et
java.lang.reflect.Array permettent de créer un objet de n'importe quelle classe ayant un constructeur
avec ou sans paramètres.

Une exception OutOfMemoryError peut être éventuellement déclenchée en cas de mémoire insuffisante
pour allouer l'espace mémoire nécessaire au nouvel objet.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 14

La seule manière de créer des objets en Java se fait grâce à l'opérateur new. Vous ne pouvez pas comme
en C++, créer des objets sur la pile d'exécution.
Maintenant que vous connaissez comment créer une classe, un constructeur et un objet en Java,
comparons un programme simple C avec un programme Java :

#include <stdlib.h>
/* Déclaration du type Classe1 */ // Déclaration de la classe Classe1
typedef struct class Classe1
{ {
int var1; int var1;
int var2; int var2;
}
Classe1; // Constructeur de Classe1
// permettant d'initialiser
// les variables var1 et var2
public Classe1 (int valeur1,
int valeur2)
{
var1 = valeur1;
var2 = valeur2;
}

/* Fonction renvoyant la somme des */ // Méthode renvoyant la somme des


/* deux champs de objet */ // deux variables d'un objet de
int division (Classe1 *objet) public int division ()
{ {
return objet->var1 / objet->var2; return var1 / var2;
} }

void main () public static void main


(String [] args)
{ {
/* Allocation puis initialisation */ // Création d'une instance de
/* d'une instance de Classe1 */ // Classe1 directement initialisée
Classe1 *objet1 = (Classe1 *) Classe1 objet1
calloc (1, sizeof (Classe1)); = new Classe1 (10, 20);
objet1->var1 = 10;
objet1->var2 = 20;
int quotient = division (objet1); int quotient = objet1.division ();
} }
}

Outrepasser une méthode

Une méthode methodeOutrepassee (), est outrepassée (overridden) si elle est déclarée dans une super
classe et une classe dérivée, avec le même nom, le même nombre de paramètres, et le même type pour
chacun des paramètres.
Les exemples qui suivent montrent l'intérêt de ce concept :

L'application suivante décrit une gestion de comptes en banque simplifiée et correspond au graphe
d'héritage décrit au chapitre précédent (sans la classe PEL). Recopiez la dans un fichier Banque.java , que
vous compilez avec l'instruction javac Banque.java pour ensuite l'exécuter avec java ou Java Runner ,
grâce à l'instruction java Banque :

class Compte
{
private int numero;
protected float soldeInitial; // Variable protected accessible
// par les classes dérivées

// Constructeur
Compte (int nouveauNumero, float sommeDeposee)
{
// Mise à jour des variables de la classe
numero = nouveauNumero;
soldeInitial = sommeDeposee;
}

int numeroCompte ()
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 15

{
return numero;
}

float calculerSolde ()
{
return soldeInitial;
}
}

// La classe CompteDepot dérive de la classe Compte


class CompteDepot extends Compte
{
// Création d'un tableau de 1000 float pour les opérations
private float operations [] = new float [1000];
private int nbreOperations; // Initialisée à 0

// Constructeur
CompteDepot (int nouveauNumero)
{
// Appel du constructeur de la super classe
super (nouveauNumero, 0);
}

void ajouterOperation (float debitCredit)


{
// Mémorisation de l'opération et augmentation de nbreOperation
operations [nbreOperations++] = debitCredit;
}

float calculerSolde () // outrepasse la méthode de la classe Compte


{
float solde = soldeInitial;
// Somme de toutes les opérations
for (int i = 0; i < nbreOperations; i++)
solde += operations [i];
return solde;
}
}

// La classe CompteEpargne dérive de la classe Compte


class CompteEpargne extends Compte
{
private float tauxInteretPourcentage;

// Constructeur (tauxInteret en %)
CompteEpargne (int nouveauNumero, float depot, float tauxInteret)
{
super (nouveauNumero, depot);
tauxInteretPourcentage = tauxInteret;
}

float calculerSolde () // outrepasse la méthode de la classe Compte


{
return soldeInitial * (1f + tauxInteretPourcentage / 100f);
}
}

// Classe d'exemple
public class Banque
{
// Méthode lancée à l'appel de l'instruction java Banque
public static void main (String [ ] args)
{
// Création de 3 comptes de classe différente
Compte compte101 = new Compte (105, 201.1f);

CompteDepot compte105 = new CompteDepot (101);


compte105.ajouterOperation (200);
compte105.ajouterOperation (-50.5f);

CompteEpargne compte1003 = new CompteEpargne (1003, 500, 5.2f);

// Appel de la méthode editerSoldeCompte () sur chacun


// des comptes. Cette méthode prend en paramètre une
// référence de classe Compte : Les objets désignés par
// compte105 et compte1003 sont d'une classe qui dérive
// de la classe Compte, c'est pourquoi ils peuvent être
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 16

// acceptés en paramètre comme des objets de classe Compte


editerSoldeCompte (compte101);
editerSoldeCompte (compte105);
editerSoldeCompte (compte1003);
}

// Méthode éditant le numéro et le solde d'un compte


static void editerSoldeCompte (Compte compte)
{
// Récupération du numéro et du solde du compte
// La méthode calculerSolde () qui est appelée
// est celle de la classe de l'objet désigné
// par la variable compte
int numero = compte.numeroCompte ();
float solde = compte.calculerSolde ();
System.out.println ( "Compte : " + numero
+ " Solde = " + solde);
}
}

Le résultat de ce programme donne ceci :

Compte : 105 Solde = 201.1


Compte : 101 Solde = 149.5
Compte : 1003 Solde = 526.0

La méthode editerSoldeCompte () reçoit en paramètre la variable compte. compte est une référence
désignant une instance de la classe Compte, ou d'une des classes dérivées de Compte, ici CompteDepot ou
CompteEpargne.
Que se passe-t-il à l'appel de la méthode calculerSolde () sur cette référence ?
La Machine Virtuelle connait à l'exécution la classe de l'objet désigné par la référence compte : en plus,
des variables d'instance qui sont allouées à la création d'une nouvelle instance des classes Compte,
CompteDepot ou CompteEpargne, une variable cachée qui représente la classe du nouvel objet lui est
ajoutée. A l'appel compte.calculerSolde (), consulte cette variable pour connaître la classe de l'objet
désigné par compte. Une fois qu'il a cette classe il appelle l'implémentation de la méthode calculerSolde
() de cette classe ce qui fait que la méthode calculerSolde () de la classe Compte ne sera
effectivement appelée que si l'objet désigné par compte est de classe Compte.

Globalement en Java, la manière d'appeler une méthode d'instance quelle qu'elle soit, respecte ce
schéma : c'est la ligature dynamique (la bonne méthode à appeler n'est pas déterminée statiquement à la
compilation, mais dynamiquement à l'exécution en fonction de la classe de l'objet désigné). A première
vue, son intérêt paraît pourtant limité aux méthodes outrepassées, mais souvenez-vous que toute classe
qui n'est pas final peut être appelée à être dérivée un jour, et que ses méthodes seront peut-être
outrepassées dans la classe dérivée. Il faut donc "préparer le terrain" pour ces méthodes...

Une méthode outrepassant une autre ne peut avoir un contrôle d'accès plus restrictif que la méthode
outrepassée (pas possible d'avoir un accès protected ou private si la méthode outrepassée est public).
L'ordre de priorité des contrôles d'accès est du plus restrictif au moins restrictif : private, friendly ,
protected et public.

La notion de méthode outrepassée est fondamentale et contribue pour une grande part à la puissance
d'un langage objet. Elle est très souvent utilisée en Java, car il vous faut souvent outrepasser les
méthodes des classes de la bibliothèque Java pour modifier leur comportement par défaut. On parle
souvent aussi de polymorphisme.

En Java, toutes les méthodes d'instance utilisent la ligature dynamique. Contrairement au C++, toutes
les méthodes sont virtual.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 17

Faites très attention à bien respecter l'orthographe du nom et des types des paramètres des méthodes que
vous outrepassez. Si la nouvelle méthode créée a un nom différent, elle n'outrepassera plus celle de la
super classe, le compilateur ne vous dira rien et finalement la méthode appelée pourra être celle de la
super classe au lieu de celle que vous avez déclaré.
Par contre, les noms des paramètres n'ont pas d'importance, et peuvent être différents de ceux de la
méthode outrepassée.
Seules les méthodes sont outrepassées et pas les variables, comme le montre l'exemple suivant :

class Classe1
{
final static int cste1 = 0;
}

class Classe2 extends Classe1


{
// Déclaration d'une variable (constante) qui cache celle de Classe1
final static int cste1 = 1;

void methode1 ()
{
Classe2 objet2 = new Classe2 (); // création d'un objet de classe Classe2
int var = objet2.cste1; // var vaut 1
Classe1 objet1 = objet2; // cast de Classe2 vers Classe1
var = objet1.cste1; // var vaut 0 pourtant objet1 est une référence
// sur un objet de classe Classe2 !
}
}

Dans cet exemple, Classe2 a besoin de donner une valeur différente à cste1 pour les objets de cette
classe : si on redéclare cette constante dans Classe2 avec une valeur différente, on pourrait s'attendre à
ce que objet1.cste1 vaille 1 puisque objet1 désigne un objet de classe Classe2. En fait, objet1.cste1
renvoie la valeur de cste1 de la classe Classe1 (pas de ligature dynamique sur les variables)... Si vous
voulez que var vaille 1 dans les deux cas de cet exemple, vous devez créer une méthode dans chaque
classe qui renvoie la valeur de cste1, comme par exemple :

class Classe1
{
public int valeur1 ()
{
return 0;
}
}

class Classe2 extends Classe1


{
public int valeur1 () // valeur1 () outrepasse la méthode de Classe1
{
return 1;
}

void methode1 ()
{
Classe2 objet2 = new Classe2 (); // création d'un objet de classe Classe2
int var = objet2.valeur1 (); // var vaut 1
Classe1 objet1 = objet2; // cast de Classe2 vers Classe1
var = objet1.valeur1 (); // var vaut 1 car c'est la methode
// valeur1 () qui est appelée (objet1
// est une référence sur un objet de
// classe Classe2)
}
}

Utilisation de classes abstract

Les classes abstract sont une catégorie spéciale de classe : il est impossible de créer une instance d'une
classe abstract classeAbstract, mais par contre il est possible de déclarer une variable var1 qui est une
référence de classe classeAbstract. var1 peut être égale à null ou désigner un objet d'une des classes
dérivées de la classe classeAbstract à la condition suivante : Si la classe classeAbstract déclare une ou
plusieurs méthodes d'instance abstract, la classe dérivée doit outrepasser ces méthodes et donner leur
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 18

implémentation. Si cette classe ne donne pas l'implémentation de toutes les méthodes abstract, elle est
elle-même abstract.
Rappelez-vous qu'en fait, pour créer une instance d'une classe Classe1, il faut que toutes les méthodes
de cette classe soient implémentées pour pouvoir les appeler, que ces méthodes soient déclarées dans
Classe1 ou héritées des super classes de Classe1. Si une super classe déclare des méthodes abstract il
faut donc que ces méthodes soient implémentées.
De même, une interface est une sorte de classe abstract dont toutes les méthodes sont implicitement
abstract. C'est pourquoi toute classe qui implémente une interface, doit implémenter toutes les
méthodes de l'interface pour ne pas être abstract.
L'exemple suivant vous montre l'intérêt de l'utilisation d'une classe abstract :

abstract class Vehicule


{
abstract int nombreDeRoues ();
}

class Velo extends Vehicule


{
int nombreDeRoues () // outrepasse nombreDeRoues () de la classe Vehicule
{
return 2;
}
}

class Voiture extends Vehicule


{
int nombreDeRoues () // outrepasse nombreDeRoues () de la classe Vehicule
{
return 4;
}
}

class VoitureAvecRemorque extends Voiture


{
int nombreDeRoues () // outrepasse nombreDeRoues () de la classe Voiture
{
// super.nombreDeRoues () fait appel à la méthode outrepassée
return 2 + super.nombreDeRoues ();
}
}

class Classe1
{
// Création de deux objets avec l'opérateur new
Velo unVelo = new Velo ();
Voiture uneVoiture = new Voiture ();
// Déclaration d'une référence sur la super classe Vehicule
// Comme la classe Vehicule est abstract, il est impossible de créer un
// objet de cette classe, mais on peut affecter à cette référence
// de classe Vehicule, un objet d'une classe dérivée de Vehicule
Vehicule unVehicule;

void methode ()
{
int a = unVelo.nombreDeRoues (); // a est égal à 2
int b = uneVoiture.nombreDeRoues (); // b est égal à 4

unVehicule = unVelo; // cast de Voiture vers Vehicule


int c = unVehicule.nombreDeRoues (); // c est égal à 2
unVehicule = uneVoiture; // cast de Voiture vers Vehicule
int d = unVehicule.nombreDeRoues (); // d est égal à 4
}
}

Dans cet exemple, unVehicule est une référence permettant de désigner un objet de classe Vehicule ou
toute autre classe dérivée de Vehicule. Quand nombreDeRoues () est invoquée sur la référence
unVehicule, l'interpréteur va consulter la classe réelle d'appartenance de l'objet référencé par unVehicule
; une fois, qu'il a déterminé cette classe, il va appeler la méthode nombreDeRoues () de cette classe.
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 19

Le mot-clé super permet aussi d'invoquer la méthode methode1 () outrepassée d'une super classe
Classe1, par super.methode1 (). Il correspond à la notation Classe1::methode1 () du C++.
Par contre, si Classe1 hérite elle-même d'une super classe Classe0, implémentant elle aussi methode1 (),
vous ne pourrez pas appeler directement methode1 () de Classe0, comme vous le feriez en C++ grâce à
Classe0::methode1 (). Mais ceci n'est pas souvent utilisé...
Java ne permet pas d'utiliser des pointeurs sur fonctions. Dans certains cas, l'utilisation des méthodes
outrepassées est une alternative à cette absence. Voici un programme C et un programme Java mis en
parallèle pour illustrer ce propos (l'exemple utilise une interface mais il est aussi possible d'utiliser une
classe abstract) :

/* Déclaration d'un type pointeur sur */ // Déclaration d'une interface déclarant


/* fonction prenant en paramètre un int */ // une méthode prenant en paramètre un int
typedef void (* methodeX) (int i); interface MethodeX
{
void methodeX (int i);
}

/* Déclaration de deux fonctions du */ // Déclaration de deux classes implémentant


/* même type que methodeX () */ // la méthode methodeX () de cette interface
class Classe1 implements MethodeX
{
void methodeX_1 (int i) public void methodeX (int i)
{ /* Corps de methodeX_1 () */ } { /* Corps de methodeX () */ }
}

class Classe2 implements MethodeX


{
void methodeX_2 (int i) public void methodeX (int i)
{ /* Corps de methodeX_2 () */ } { /* Corps de methodeX () */ }
}

// Déclaration d'une classe utilisant


// methodeX () de différentes classes
class ClasseUtilisantMethodeX
{
/* Méthode appelant la méthode */ // Méthode appelant la méthode methodeX ()
/* de type methodeX */ // d'une classe implémentant MethodeX
void appelMethodeX void appelMethodeX
(methodeX methode, int i) (MethodeX objet, int i)
{ {
methode (i); objet.methodeX (i);
} }

void appelMethodeXClasse1 () void appelMethodeXClasse1 ()


{ {
/* Appel de methodeX_1 () */ // Appel de methodeX () de Classe1
appelMethodeX (methodeX_1, 5); // La référence désignant l'objet créé
} // avec new Classe1 () peut être
// casté en MethodeX car
// Classe1 implémente MethodeX
appelMethodeX (new Classe1 (), 5);
}

void appelMethodeXClasse2 () void appelMethodeXClasse2 ()


{ {
/* Appel de methodeX_2 () */ // Appel de methodeX () de Classe2
appelMethodeX (methodeX_2, 10); appelMethodeX (new Classe2 (), 10);
} }
}

Destruction des objets

Java gère de manière automatique pour le programmeur l'allocation dynamique de mémoire. La mémoire
nécessaire à la mémorisation de tout nouvel objet est allouée dynamiquement à sa création, et la mémoire
qu'il occupe est automatiquement libérée quand celui-ci n'est plus référencé par aucune variable du
programme. Cette libération est réalisée grâce au Garbage Collector (littéralement Ramasseur d'Ordure)
fourni avec la Machine Virtuelle Java.
Cette fonctionnalité très pratique de Java simplifie énormément la programmation, d'autant plus qu'elle
implique que la notion de destructeur (méthode appelée à la destruction d'un objet en C++, très souvent
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 20

utilisée pour libérer la mémoire utilisée par un objet) est beaucoup moins utile en Java.
Toutefois, Java fournit une méthode à outrepasser dans vos classes si vous avez besoin d'effectuer
certains traitements spécifiques à la destruction d'un objet : void finalize (). Cette méthode est
invoquée juste avant que le Garbage Collector ne récupère la mémoire occupée par l'objet.
Normalement, vous ne l'utiliserez que rarement mais elle peut être utile pour libérer certaines ressources
dont Java ne géreraient pas directement la destruction (contextes graphiques, ressources ou mémoire
alloués par des méthodes native écrites en C ou C++).
Vous pouvez éventuellement indiquer à la Machine Virtuelle qu'une référence var1 désignant un objet
n'est plus utile en la mettant à null (var1 = null;), pour que le Garbage Collector puisse détruire l'objet
désigné par var1 si celui-ci n'est plus désigné par aucune référence.

En java, la destruction des objets se fait automatiquement quand ils ne sont plus utilisés (référencés).
L'opérateur delete du C++ servant à détruire explicitement les objets créés dynamiquement est donc
inutile.
En java, il n'existe pas de syntaxe prédéfinie pour les destructeurs. Vous pouvez outrepasser la
méthode finalize () pour "nettoyer" vos objets mais contrairement au destructeur du C++ où le
destructeur est invoqué à l'appel de delete (), finalize () est invoquée automatiquement par le
Garbage Collector quand un objet n'a plus aucune référence le désignant et qu'il peut donc être détruit.
Le moment précis où va intervenir le Garbage Collector n'est pas prévisible, donc s'il vous faut
effectuer des opérations obligatoires quand un objet devient inutile (effacement d'un dessin à l'écran
par exemple), c'est à vous de créer une méthode que vous invoquerez au moment voulue (vous
pouvez l'appeler delete () si vous voulez).
Comme en C++, les objets Java sont alloués dynamiquement à leur création via l'opérateur new. En
Java, c'est le seul moyen d'allouer de la mémoire, donc il n'existe plus de fonctions telles que malloc
(), realloc () ou free (). L'opérateur sizeof () servant surtout pour évaluer la taille d'un objet à
allouer n'existe plus non plus. En C, sizeof () est utile aussi pour la portabilité d'un programme, car
tous les types de base n'ont pas la même taille suivant les systèmes (int peut avoir une taille de 16 ou
32 bits, par exemple) : en Java, tous les types de base ont la même taille.

Comment ça marche ?

Pour mieux comprendre comment Java manipule les objets de leur création à leur destruction, voici une
figure décrivant la vie d'un objet :

figure 7. La vie d'un objet Java de sa création à sa destruction


vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 21

Ce programme très simple vous montre la nuance très importante entre une référence et un objet : un
même objet peut avoir n'importe quel nombre de références le désignant, mais une référence ne peut
désigner qu'un seul objet à la fois.
A tout moment, la Machine Virtuelle connaît le nombre de références (ou de variables) qui désignent
chacun des objets d'un programme : quand pour un objet, ce nombre devient nul, ceci signifie que plus
aucune variable du programme ne désigne cet objet. S'il n'existe plus de variable désignant cet objet, le
programme n'a donc plus de moyen de le manipuler, il est logique de le considérer comme inutile et de
le supprimer.

Pour vous montrer toute la puissance et l'intérêt du Garbage Collector, voici un programme C et un
programme Java mettant en oeuvre l'utilisation de listes chaînées :

#include <stdlib.h>
/* Déclaration d'un type de liste chaînée */ // Déclaration d'une classe de liste chaînée
typedef struct _eltListe public class ListeChainee
{ {
int nombre; int nombre;
struct _eltListe *suivant; ListeChainee suivant;
}
EltListe,
*ListeChainee;

/* Crée une liste d'éléments ayant */ // Construit une liste d'éléments ayant
/* leur nombre compris entre min et max */ // leur nombre compris entre min et max
ListeChainee creerListe (int min, int max) ListeChainee (int min, int max)
{ {
ListeChainee elt = (ListeChainee)
malloc (sizeof (EltListe));
elt->nombre = min; nombre = min;
if (min < max) if (min < max)
elt->suivant = suivant =
creerListe (min +1, max); new ListeChainee (min + 1, max);
else else
elt->suivant = NULL; suivant = null;
return elt; }
}

/* Enlève un élément individuel */ // Enlève un élément individuel


ListeChainee enleverElement ListeChainee enleverElement
(ListeChainee liste, (int nombre)
int nombre)
{ {
ListeChainee eltCherche; ListeChainee eltCherche;
ListeChainee eltPrecedent = NULL; ListeChainee eltPrecedent = null;

/* Recherche de l'élément contenant */ // Recherche de l'élément contenant


/* le nombre */ // le nombre (this désigne la tête
for (eltCherche = liste; // de liste elle-même)
eltCherche != NULL; for (eltCherche = this;
eltCherche = eltCherche->suivant) eltCherche != null;
if (eltCherche->nombre != nombre) eltCherche = eltCherche.suivant)
eltPrecedent = eltCherche; if (eltCherche.nombre != nombre)
else eltPrecedent = eltCherche;
{ else
/* Suppression de l'element */ // Suppression de la référence sur
/* de la liste chaînée */ // de l'element recherche, l'objet
if (eltCherche == liste) // peut donc être supprimé
liste = liste->suivant; if (eltCherche == this)
else return this.suivant;
eltPrecedent->suivant = else
eltCherche->suivant; eltPrecedent.suivant =
free (eltCherche); eltCherche.suivant;
}
return liste; return this;
} }

/* Libère la mémoire prise par tous */ // void viderListe ()


/* les éléments de la liste */ // est inutile
void viderListe (ListeChainee liste)
{
while (liste != NULL)
{
ListeChainee eltPrecedent = liste;
vendredi 11 février 2000 Du C/C++ à Java : Création et utilisation des classes Page: 22

liste = liste->suivant;
free (eltPrecedent);
}
}

void main () public static void main


(String [ ] args)
{ {
ListeChainee liste = ListeChainee liste =
creerListe (1, 10); new ListeChainee (1, 10);
liste = enleverElement (liste, 8); liste = liste.enleverElement (8);
viderListe (liste); liste = null;
} }
}

L'instruction liste = null entraîne que l'unique référence sur l'objet de tête de liste est perdue, donc le
Garbage Collector peut supprimer cet objet. Quand il le supprime, la variable suivant de cet objet est
supprimée et il n'existe plus de référence sur l'élément suivant. Ce dernier peut être supprimé à son tour,
ainsi de suite jusqu'à qu'au dernier élément de la liste. Dans cet exemple, liste = null n'est même pas
obligatoire car la variable liste est supprimée à la sortie de la méthode main (), ce qui provoque les
mêmes effets.
Une fois compris l'exemple précédent, vous pouvez essayer de créer à partir de celui-ci une classe de
liste doublement chaînée (avec liens suivant et precedent).

Si vous ne voulez pas croire en la magie, il vous faudra sûrement un certain temps pour faire confiance
au Garbage Collector sans arrière pensée. Ce type de gestion de la mémoire étant très pratique, le réflexe
de ne plus libérer la mémoire explicitement comme en C/C++ (avec free () ou delete) s'acquière
d'office, mais vous prendrez plus de temps à comprendre comment vos objets sont détruits dans telle ou
telle situation.
La meilleure piste pour répondre à vos interrogations, est de vous demander par combien de variables
sont référencés le ou les objets sur lesquels vous avez des doutes. Si par enchaînement, ce nombre
tombe à 0, vous pouvez oublier vos doutes.
Comme en Java, vous ne détruisez pas explicitement les objets, toute référence est égale soit à null soit
elle désigne un objet TOUJOURS valide. Vous ne pouvez pas avoir de risque de manipuler un objet qui
n'existe plus comme dans le programme C suivant :

void fonction1 ()
{
char *chaine = malloc (20);
strcpy (chaine, "bonjour\n");
/* ... */
free (chaine);
/* ... */
printf (chaine);
}

En C, si dans un programme vous utilisez par erreur un pointeur après avoir libéré l'espace mémoire qu'il
désigne, le compilateur ne vous indiquera aucune erreur. De plus, ce genre d'erreur est souvent difficile à
trouver.
En Java, si vous essayez d'accéder à un objet par une référence égale à null, une exception
NullPointerException est déclenchée.
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 1

Objets, tableaux et chaînes de caractères

La classe Object
La classe Class
Les tableaux
Les chaînes de caractères
La classe String
La classe StringBuffer
La classe System

La classe Object

Java étant un langage pur objet, la première classe à étudier est naturellement la super classe dont
héritent implicitement toute les autres (celles fournies avec Java et celles que vous créerez) : la classe
Object.
Cette classe comporte un petit nombre de méthodes, dont toutes les autres classes héritent et que
certaines classes dérivées peuvent éventuellement outrepasser. Les classes différentes de la classe
Object utilisées par ces méthodes seront détaillées ultérieurement.

Constructeur

public Object ()

Méthodes

public final Class getClass ()

Renvoie la classe d'un objet. A l'exécution, chaque classe utilisée dans un programme est représentée par
une instance de la classe Class.

public boolean equals (Object obj)

Renvoie true si les deux objets (l'objet sur lequel est invoqué equals () et obj) sont égaux, false sinon.
Il faut entendre par égaux, s'ils désignent le même objet. Une classe doit outrepasser cette méthode, si
elle veut que la signification de l'égalité entre deux objets soit moins stricte, par exemple en comparant la
valeur de leurs variables.

public int hashCode ()

Renvoie un code entier utilisé pour le stockage des objets dans les tables de hash (Hashtable). Si une
classe outrepasse equals (), elle doit en général outrepasser aussi hashCode () pour renvoyer le même
code pour deux objets égaux par la méthode equals (). La condition inverse n'est pas obligatoire (deux
objets peuvent renvoyer un code de hash identique mais être différents par la méthode equals ()).
La méthode hashCode () de la classe Object renvoie l'adresse d'un objet.

protected Object clone () throws CloneNotSupportedException

Duplique un objet. Un objet de la même classe que l'objet sur lequel on invoque clone () est créé avec
une copie de toutes ses variables. Renvoie le nouvel objet.
Une exception OutOfMemoryError peut être éventuellement déclenchée en cas de mémoire insuffisante.
L'exception CloneNotSupportedException est déclenchée si la classe de l'objet cloné ou une de ses
super classes n'implémente pas explicitement l'interface Cloneable.
La méthode clone () est protected donc pour pouvoir dupliquer un objet objet1 de classe Classe1 par
l'expression objet1.clone (), Classe1 doit outrepasser clone () et lui donner un contrôle d'accès
public. Comme toutes les classes dérivent de la classe Object, cette méthode n'est en fait utile pour les
autres classes que pour créer un nouvel objet de même classe et copier la valeur de toutes les variables
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 2

de l'original.
Si une des variables recopiées est une référence sur un objet, seule la référence est recopiée et désignera
le même objet. Donc, si vous voulez que les références clonées désignent des objets différents, vous
devez cloner ces objets vous même, dans la méthode clone ().
Voici un exemple d'utilisation de clone () :

class Classe0 implements Cloneable


{
double unNombre;

public Object clone () throws CloneNotSupportedException


{
// Appel de la méthode clone () de la classe Object
return super.clone ();
}
// ...
}

class Classe1 implements Cloneable


{
int var;
Classe0 objet0 = new Classe0 ();

public Object clone () throws CloneNotSupportedException


{
// Appel de la méthode clone () de la classe Object
// puis clonage de objet0
Classe1 nouveau = (Classe1)super.clone (); // cast de Object vers Classe1
nouveau.objet0 = (Classe0)objet0.clone (); // cast de Object vers Classe0
return nouveau;
}
// ...
}

public String toString ()

Renvoie une chaîne de caractères représentant la valeur d'un objet. Chaque classe devrait outrepasser
cette méthode pour créer une chaîne représentant la valeur de leurs instances. La méthode toString ()
de la classe Object renvoie le nom de la classe de l'objet sur lequel on invoque cette méthode, suivi du
caractère '@' et de la valeur en hexadécimal renvoyée par hashCode ().

public final void notify () throws IllegalMonitorStateException


public final void notifyAll () throws IllegalMonitorStateException
public final void wait (long millis )
throws IllegalMonitorStateException, InterruptedException
public final void wait (long millis, int nanos)
throws IllegalMonitorStateException, InterruptedException
public final void wait ()
throws IllegalMonitorStateException, InterruptedException

Ces méthodes sont utilisées pour prévenir ou attendre des threads (tâches) synchronisés sur l'accès à un
objet. millis désigne un nombre de millisecondes et nanos un nombre de nanosecondes. wait (0) et
wait (0, 0) sont équivalentes à wait ().
Ces méthodes seront développées ultérieurement, dans le chapitre sur les threads. Notez qu'étant final,
elles ne peuvent être outrepassées dans les classes dérivées.

protected void finalize() throws Throwable

Cette méthode est invoquée automatiquement avant qu'un objet soit détruit par le Garbage collector. Les
classes ayant des traitements spécifiques à effectuer à la destruction d'un objet, doivent outrepasser cette
méthode. La méthode finalize () de la classe Object ne fait rien. Voir aussi la destruction d'objets.

Toutes les classes Java héritent de la classe Object. Vous ne pouvez créer plusieurs hiérarchies de
classes comme en C++.

La classe Class

Cette classe final permet de représenter chacune des classes (et des interfaces) chargées par la Machine
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 3

Virtuelle Java, en faisant correspondre à chaque classe une instance de classe Class.

Méthodes

public static Class forName (String className) throws ClassNotFoundException

Renvoie un objet de classe Class correspondant à la classe ou à l'interface de nom className. className
doit être un nom de classe complet avec package (comme par exemple java.lang.Object). Comme la
classe Class n'a pas de constructeur public, les seules manières de récupérer une instance de classe
Class sont de passer par cette méthode ou la méthode getClass () de la classe Object.

public Object newInstance () throws InstantiationException, IllegalAccessException

Crée une nouvelle instance d'une classe. Cette méthode est moins pratique que l'opérateur new pour
créer des objets car il faut intercepter les exceptions InstantiationException et
IllegalAccessException que peut déclencher la méthode newInstance ().
Associée à la méthode forName (), elle est par contre très utile pour instancier des objets d'une classe
dont vous n'avez que le nom et qui a un constructeur sans paramètre, comme par exemple
Class.forName ("java.lang.Object").newInstance () qui crée un objet de classe Object. Voir aussi la
création d'objets et l'application InstantiationAvecNom .

public ClassLoader getClassLoader ()

Renvoie le chargeur de classe utilisé pour charger une classe ou null si c'est le chargeur par défaut.

public String getName ()

Renvoie le nom complet d'une classe (avec son package).

public Class getSuperclass ()

Renvoie la super classe d'une classe.

public Class [ ] getInterfaces ()

Renvoie un tableau des interfaces implémentées par une classe. Si la classe n'implémente aucune
interface ce tableau a une longueur nulle (length = 0).

public boolean isInterface ()

Renvoie true ou false suivant que l'objet sur lequel cette méthode est appelée est une interface ou une
classe.

public String toString ()

Cette méthode renvoie le nom de la classe ou de l'interface précédé des mots class ou interface .
toString () outrepasse la méthode de la classe Object.

La méthode equals () de la classe Object n'est pas outrepassée dans cette classe car c'est inutile :
Comme à chaque classe chargée par la Machine Virtuelle correspond une instance de la classe Class
unique, vous pouvez comparez deux objets de classe Class directement avec l'opérateur == ou !=.

A partir de Java 1.1, cette classe a été très enrichie et comporte des méthodes qui permettent d'interroger
toutes les variables, les méthodes et les contructeurs d'une classe.

Les tableaux

Comme tout type qui n'est pas un type de base, les tableaux sont des objets alloués dynamiquement.
Les éléments du tableau peuvent être d'un type de base, d'une classe ou d'une interface, et le tableau
lui-même hérite de la classe Object et de toutes ses méthodes.
Comme en C, un tableau contenant n éléments, a son premier élément à l'indice 0 et son dernier à
l'indice n-1. Une référence table qui désigne un tableau peut accéder à la variable length de ce tableau
(table.length) pour déterminer le nombre d'éléments mémorisés dans celui-ci.
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 4

Exemple de déclarations et de créations de tableaux :

class Classe1
{
// Déclaration de références sur des tableaux
int [ ] tableEntiers;
float tableFloats [ ]; // Les crochets peuvent être avant ou après la variable
Object [ ] tableObjets;
Cloneable [ ] tableClonaeable;

// Création de tableaux
int [ ] tableEntiers2 = new int [5];
Object [ ] tableObjets2 = new Object [10];
Cloneable [ ] tableClonaeable2 = new Cloneable [10];

// Création d'un tableau de longueur nulle


int [ ] tableauVide = new int [0];

// Création de tableaux initialisés


long [ ] tableLongs = {1L, 2L, 5L};
Classe1 objet1 = new Classe1 ();
Classe1 [ ] tableObjets3 = {objet1, new Classe1 (), null};
short [ ][ ] tableShorts = {{1, 2}, {1, 2, 3}, null, {0}};
int [ ] tableauVide2 = {};
}

A partir de Java 1.1, les méthodes static newInstance () de la classe java.lang.reflect.Array


permettent aussi de créer des tableaux comme dans l'exemple suivant :

import java.lang.reflect.*;

public class ClasseJava11


{
public static void main (String [] args) throws ClassNotFoundException
{
// Autre possibilité de création de tableaux à partir de Java 1.1
Object tableEntiers = Array.newInstance (int.class, 5);
float [] tableFloats = (float [])Array.newInstance (float.class, 2);
Object tableObjets = Array.newInstance (Class.forName ("java.lang.Object"), 10);
}
}

Un tableau peut être créé avec une longueur nulle. Quel en est l'intérêt ?

Certaines méthodes comme getInterfaces () de la classe Class renvoie un tableau qui peut
n'avoir aucun élément. Après avoir appelé une telle méthode, vous n'aurez pas à tester si la
référence renvoyée est égale à null avant de lancer une boucle sur les n éléments du tableau
renvoyé.
Si table désigne un tableau de longueur nulle, on peut connaître la classe du tableau grâce à
table.getClass () mais pas si table est égal à null.

Une fois qu'un tableau est créé, sa taille ne peut être modifiée ; par contre, la classe System du package
java.lang fournit la méthode arraycopy () permettant de copier une partie d'un tableau dans un autre,
et la classe Vector du package java.util est idéale pour manipuler des tableaux de taille variable.

Les valeurs d'un tableau créé grâce à l'opérateur new sont initialisées à leur valeur par défaut.

Dans un soucis de sécurité, Java vérifie si les tableaux sont manipulés correctement :

Si l'accès à un élément i (grâce à table [i]) est invalide (i < 0 ou i >= table.length), Java
déclenche une exception ArrayIndexOutOfBoundsException.
Si à la création d'un tableau, la taille requise est négative une exception
NegativeArraySizeException est déclenchée.
Si vous tentez de stocker dans un tableau un élément incompatible avec ceux du tableau, une
exception ArrayStoreException est déclenchée.

Java permet de créer les tableaux multi-dimensionnels de deux manières différentes : vous pouvez créer
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 5

un tableau directement avec plusieurs dimensions ou créer vous même les sous tableaux, un par un,
comme dans l'exemple suivant, permettant de créer un tableau de taille dim1 x dim2 initialisé avec
valeurDefaut :

class Classe1
{
public int [ ][ ] methode1 (int dim1, int dim2)
{
int [ ][ ] table1 = new int [dim1][dim2]; // Création d'un tableau dim1 x dim2
return table1;
}

public int [ ][ ] methode2 (int dim1, int dim2, int valeurDefaut)


{
int [ ][ ] table1 = new int [dim1][ ]; // Création de la première dimension
int i, j;

for (i = 0; i < dim1; i++)


{
table1 [i] = new int [dim2]; // Création de la deuxième dimension
for (j = 0; j < dim2; j++)
table1 [i][j] = valeurDefaut;
}

return table1;
}
}

L'avantage de ce système est que vous pouvez créer des sous tableaux de dimensions différentes (pour
créer un triangle de Pascal, par exemple).

Il est possible d'interroger la classe d'un tableau et le nom de cette classe, grâce aux méthodes getClass
() de la classe Object et getName () de la classe Class. Ce nom est construit avec en tête autant de
caractère [ que le nombre de dimensions du tableau suivi soit du caractère L, du nom de la classe ou de
l'interface complet et d'un point virgule (;) si le tableau mémorise des références sur des objets, soit d'un
des caractères suivant si le tableau mémorise des valeurs d'un des types de base :

Type des éléments


Caractère
du tableau
B byte

C char

D double

F float

I int

J long

L classe ou interface
S short

Z boolean

Par exemple, (new Object [3]).getClass ().getName () renvoie "[Ljava.lang.Object;" et (new


double [0][0]).getClass ().getName () renvoie "[[D".
Avec Java 1.0, il est impossible de créer une nouvelle instance d'un tableau, en connaissant sa classe et
en utilisant la méthode newInstance () de la classe Class. Par exemple, si vous exécutez l'instruction
(new int [2]).getClass ().newInstance (), une exception InstantiationException sera déclenchée.
A partir de Java 1.1, l'utilisation des méthodes static newInstance () de la classe
java.lang.reflect.Array, permet de résoudre ce problème, comme dans l'exemple Array.newInstance
(Class.forName ("java.lang.Object"), 5).

Les tableaux implémentant implicitement l'interface Cloneable, il est possible de les dupliquer grâce à la
méthode clone () de la classe Object. Si le tableau comporte plus d'une dimension, il faut dupliquer
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 6

aussi les sous-tableaux pour obtenir une copie complète, comme dans l'exemple :

class Classe1
{
void methode1 ()
{
int [ ][ ] table = {{15, 20, 30},
{ 5, 10, 15}};

// Duplication de la première dimension


int [ ][ ] tableClone = (int [][])table.clone ();
int i;
for (i = 0; i < table.length; i++)
// Duplication des sous tableaux
tableClone [i] = (int [])table [i].clone ();
}
}

La création d'un tableau de n objets ne crée pas n instances pour les n éléments du tableau, mais
uniquement n références à des objets. C'est à vous d'affecter chacune de ces références, soit à un
nouvel objet à créer, soit à un objet existant.
De même, la duplication d'un tableau de n objets avec la méthode clone () de la classe Object, ne
crée aucune copie des objets qu'il contient.

Les tableaux sont une sorte de classe final (pas dérivable) en Java. Ils sont d'un abord plus simple
qu'en C, langage dans lequel ils peuvent être utilisés comme des pointeurs, ce qui sème souvent la
confusion chez les débutants.
Les tableaux Java sont alloués dynamiquement. Ils comportent une variable length indiquant le nombre
d'éléments du tableau et Java vérifie les dépassements d'indice quand on accède à un élément.
Les tableaux étant des objets, il est possible de créer des tableaux de longueur nulle. L'instruction
Object [ ] table = { }; crée un tableau qui ne contient aucun objet et qui est désigné par la
référence tab. Par contre l'instruction Object [ ] table = null; initialise la référence tab à null sans
créer d'objet.
L'accès aux tableaux étant en Java, bien protégé par les exceptions, l'impossibilité de pouvoir surcharger
l'opérateur [ ], comme en C++, ne s'avère pas si importante.

Les chaînes de caractères

Les chaînes de caractères sont représentées en Java par les classes String ou StringBuffer. Ces deux
classes ne jouent pas exactement le même rôle :

String est utilisé pour représenter les chaînes de caractères constantes, qui peuvent être partagées
sans risque par plusieurs threads, puisque leur contenu ne peut pas être changé.
StringBuffer est utilisé pour les chaînes de caractères dont on veut modifier le contenu.

Toutes les chaînes de caractères Java mémorisent des caractères de type char, donc des caractères
Unicode codés sur 16 bits.

Les objets de classe String peuvent être initialisés directement avec une chaîne de caractères, sans
passer par le constructeur :

class Classe1
{
// Deux manières différentes de créer un objet
// de classe String avec la chaîne "Bonjour"
String chaine1 = "Bonjour";
String chaine2 = new String ("Bonjour");
}

Les chaînes de caractères Java peuvent utiliser l'opérateur + pour effectuer des concaténations. De plus,
l'opérateur + peut convertir automatiquement les valeurs littérales et les objets (en appelant leur méthode
toString ()), pour fabriquer une chaîne, comme dans l'expression suivante :

"Bonjour au numéro " + 1 + " et à l'objet " + unObjet


vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 7

Si unObjet est égal à null, la chaîne de conversion est "null".


En fait, le compilateur convertit les opérateurs + utilisés en appel aux différentes méthodes append () de la
classe StringBuffer. Le premier argument de l'opérateur + doit être de classe String, c'est à dire soit une
chaîne de caractères entre guillemets "abc" ou un objet de classe String. Pour les objets des autres classes,
utilisez la méthode toString () pour obtenir une chaîne de caractères, et pour les variables de type de base
utilisez les méthodes static valueOf () de la classe String.

En Java, les chaînes de caractères sont représentées par deux classes, l'une String pour les chaînes
constantes, l'autre StringBuffer pour les chaînes modifiables. A la différence du C, elles ne sont pas
une application particulière des tableaux et ne se terminent pas par le caractère nul ('\u0000').
L'opérateur + permet d'effectuer la concaténation de chaînes, de valeurs littérales et d'objets. Adieu
strcat (), printf () et compagnie !
Les opérateurs << et >> utilisés sur les streams pour fabriquer plus facilement des chaînes en C++, ont
leur équivalent avec l'opérateur + sur les chaînes de caractères en Java.

La classe String

La classe String qui est final comporte de nombreuses méthodes. En voici la liste (quelques méthode
mineures ont été ajoutées à partir de Java 1.1) :

Constructeurs

public String (String value)

Construit une nouvelle chaîne qui est la copie de value.

public String (char value [ ])

Construit une nouvelle chaîne initialisée avec la suite de caractères données dans le tableau value.

public String (byte ascii [ ], int hibyte)

Construit une nouvelle chaîne initialisée avec la suite d'octets donnée dans le tableau ascii ; chaque
caractère ASCII du tableau ascii est transformé en un caractère Unicode, en utilisant hibyte pour la
partie haute du caractère.
Autres constructeurs :

public String ()
public String (char value [ ], int offset, int count)
throws IndexOutOfBoundsException
public String (byte ascii [ ], int hibyte, int offset, int count)
throws IndexOutOfBoundsException
public String (StringBuffer buffer)

Méthodes

public int length ()

Renvoie la longueur de la chaîne, c'est à dire le nombre de caractères Unicode de la chaîne.

public char charAt (int index)

Renvoie le caractère à l'indice index (index est compris entre 0 et length () - 1).

public void getChars (int srcBegin, int srcEnd, char dst [ ], int dstBegin)
throws IndexOutOfBoundsException
public void getBytes (int srcBegin, int srcEnd, byte dst [ ], int dstBegin)
throws IndexOutOfBoundsException
public char [ ] toCharArray ()

public int hashCode ()

Cette méthode outrepasse la méthode hashCode () de la classe Object, pour renvoyer un code différent
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 8

si deux chaînes de caractères sont différentes.

public boolean equals (Object anObject)

Cette méthode outrepasse la méthode equals () de la classe Object, pour renvoyer true si anObject est
de la classe String, et si les deux chaînes de caractères sont les mêmes.

public boolean equalsIgnoreCase (String anotherString)

Comme equals () mais la comparaison est faite sans tenir compte des majuscules/minuscules des
chaînes comparées.

public int compareTo (String anotherString)

Compare une chaîne de caractères avec anotherString. Renvoie une valeur négative, nulle, ou positive
suivant que la chaîne de caractère est respectivement plus petite, égale ou plus grande que
anotherString.

public boolean regionMatches (int toffset, String other, int ooffset, int len)
public boolean regionMatches (boolean ignoreCase, int toffset,
String other, int ooffset, int len)
public boolean startsWith (String prefix, int toffset)
public boolean startsWith (String prefix)
public boolean endsWith (String suffix)

Ces méthodes permettent de comparer une partie d'une chaîne de caractères à une autre (entre deux
indices, au début ou à la fin).

public int indexOf (int ch)


public int indexOf (int ch, int fromIndex)
public int lastIndexOf (int ch)
public int lastIndexOf (int ch, int fromIndex)
public int indexOf (String str)
public int indexOf (String str, int fromIndex)
public int lastIndexOf(String str)
public int lastIndexOf (String str, int fromIndex)

Renvoient l'indice de la première ou la dernière occurrence d'un caractère ch ou d'une chaîne str dans
une chaîne de caractères, ou -1 si aucune occurrence n'a pas été trouvée.

public String substring (int beginIndex)


public String substring (int beginIndex, int endIndex)

Renvoient une sous-chaîne d'une chaîne de caractères, à partir de l'indice beginIndex ou entre les deux
indices beginIndex et endIndex (endIndex exclu).

public String concat (String str)

Renvoie la chaîne, résultat de la concaténation de str à la fin d'une chaîne de caractères.

public String replace (char oldChar, char newChar)

Renvoie une chaîne où tous les caractères oldChar sont convertis en newChar.

public String toLowerCase ()


public String toUpperCase ()

Renvoie une chaîne où tous les caractères majuscules sont convertis en minuscules, et inversement.

public String trim ()

Renvoie une chaîne où tous espaces en tête et en queue d'une chaîne de caractères sont supprimés.

public static String valueOf (char data [ ])


public static String valueOf (char data [ ], int offset, int count)
throws IndexOutOfBoundsException

Ces méthodes permettent de créer des chaînes de caractères à partir de tableaux de caractères.
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 9

public static String valueOf (Object obj)

Renvoie une chaîne représentant la valeur de obj sous forme de chaîne de caractères. En fait, équivalent
à obj.toString ().

public static String valueOf (boolean b)


public static String valueOf (char c)
public static String valueOf (int i)
public static String valueOf (long l)
public static String valueOf (float f)
public static String valueOf (double d)

Renvoie une chaîne de caractères correspondant à la valeur des types par défaut (voir aussi les classes
Integer, Long, Float et Double qui fournissent des méthodes pour convertir un nombre en une chaîne
de caractères et inversement).

public String intern ()

public String toString ()

Méthode de la classe Object, outrepassée pour qu'elle renvoie la chaîne de caractères.

La classe StringBuffer

La classe StringBuffer contrairement à la classe String, utilise un buffer de taille variable pour
mémoriser une chaîne de caractères modifiables. Cette classe final maintient elle-même l'allocation
d'espace supplémentaire pour mémoriser des caractères supplémentaires. Voici la liste des méthodes de
cette classe :

Constructeurs

public StringBuffer ()

Construit une chaîne vide.

public StringBuffer (int length) throws NegativeArraySizeException

Construit une chaîne vide, avec une taille initiale length.

public StringBuffer (String str)

Construit une chaîne vide, à partir de la chaîne str.

Méthodes

public int length ()

Renvoie le nombre de caractères mémorisés dans de la chaîne.

public int capacity ()

Renvoie la capacité courante de la chaîne, représentant le nombre de caractères qu'il est possible
d'insérer, avant qu'il ne soit alloué de l'espace supplémentaire.

public synchronized void ensureCapacity (int minimumCapacity)

Permet de s'assurer, que la chaîne à une capacité d'au moins minimumCapacity caractères
supplémentaires.

public synchronized void setLength (int newLength)


throws IndexOutOfBoundsException

Modifie la taille de la chaîne. Si la chaîne actuellement mémorisée est plus longue, elle sera tronquée.
Les caractères éventuellement ajoutés sont nuls ('\u0000'), pour que length () renvoie la valeur
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 10

newLength.

public synchronized char charAt (int index)


throws IndexOutOfBoundsException

Renvoie le caractère mémorisé à l'indice index (valeur comprise entre 0 et length () - 1).

public synchronized void getChars (int srcBegin, int srcEnd,


char dst [ ], int dstBegin)
throws IndexOutOfBoundsException

Copie à l'indice dstBegin du tableau dst les caractères compris entre les indices srcBegin et srcEnd.

public synchronized void setCharAt (int index, char ch)


throws IndexOutOfBoundsException

Change par ch le caractère à l'indice index de la chaîne.

public synchronized StringBuffer append (Object obj)


public synchronized StringBuffer append (String str)
public synchronized StringBuffer append (char str [ ])

Ajoute en fin de chaîne un objet, une chaîne ou un tableau de caractères.

public synchronized StringBuffer append (char str [ ],


int beginIndex, int length)
throws IndexOutOfBoundsException

Ajoute en fin de chaîne les caractères du tableau str, compris entre beginIndex et beginIndex + length
- 1.

public synchronized StringBuffer append (char c)


public StringBuffer append (boolean b)
public StringBuffer append (int i)
public StringBuffer append (long l)
public StringBuffer append (float f)
public StringBuffer append (double d)

Ajoute en fin de chaîne la valeur d'un type de base convertie en chaîne.

public synchronized StringBuffer insert (int offset, Object obj)


throws IndexOutOfBoundsException
public synchronized StringBuffer insert (int offset, String str)
throws IndexOutOfBoundsException
public synchronized StringBuffer insert (int offset, char str [ ])
throws IndexOutOfBoundsException

Insère à l'indice offset d'une chaîne un objet, une chaîne ou un tableau de caractères.

public synchronized StringBuffer insert (int offset, char c)


throws IndexOutOfBoundsException
public StringBuffer insert (int offset, boolean b)
throws IndexOutOfBoundsException
public StringBuffer insert (int offset, int i)
throws IndexOutOfBoundsException
public StringBuffer insert (int offset, long l)
throws IndexOutOfBoundsException
public StringBuffer insert (int offset, float f)
throws IndexOutOfBoundsException
public StringBuffer insert (int offset, double d)
throws IndexOutOfBoundsException

Insère à l'indice offset d'une chaîne la valeur d'un type de base convertie en chaîne.

public synchronized StringBuffer reverse ()

Inverse les caractères d'une chaîne.

public String toString ()

Méthode de la classe Object, outrepassée pour qu'elle renvoie la chaîne de caractères mémorisée.
vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 11

Les méthodes append (), insert () et reverse () renvoie l'objet de classe StringBuffer lui-même,
une fois modifié.

La classe System

Cette classe final permet d'accéder à différentes fonctionnalités du système de la Machine Virtuelle
Java, et comportent plusieurs variables et méthodes très utiles.
Toutes les variables et les méthodes de cette classe sont static.
Les variables in, out et err permettent de sortir ou de lire sur l'entrée et la sortie standard ; out est
particulièrement pratique pour imprimer des chaînes au moment du debug des applications Java avec
java , ou des applets avec appletviewer ou avec certains navigateurs (Microsoft Internet Explorer et
Netscape Navigator vous permettent d'afficher la fenêtre de sortie standard Java dans la fenêtre console
Java).
Alors, n'hésitez pas à utiliser out pour vérifier les variables ou les points de passage d'un programme (par
exemple en appelant System.out.println ()), surtout si vous cherchez les bugs dans un programme
multi-threads, situation où il est difficile de positionner des points d'arrêts (breakpoints ).

Variables

public static InputStream in

Entrée standard pour saisir des caractères.

public static PrintStream out

Sortie standard pour écrire des messages.

public static PrintStream err

Sortie standard des erreurs pour écrire des messages d'erreurs.

Méthodes

public static void arraycopy (Object source, int srcOffset,


Object dest, int dstOffset, int length)
throws ArrayStoreException,
IndexOutOfBoundsException

Copie les length éléments du tableau source à partir de l'indice srcOffset dans le tableau dest à l'indice
dstOffset.
Si les tableaux source et dest désignent le même tableau, le traitement effectué permet d'éviter que
certaines informations soient perdues (au cas où les intervalles se recouvrent).
Une exception ArrayStoreException est déclenchée s'il n'est pas possible de stocker un élément de
source dans dest avec un cast implicite.

public static long currentTimeMillis ()

Renvoie le temps courant en millisecondes écoulé depuis le 1er Janvier 1970.

public static void exit (int status) throws SecurityException

Quitte la Machine Virtuelle Java avec le code de sortie status.

public static void gc ()

Lance volontairement le Garbage Collector.

public static void runFinalization ()

Permet volontairement d'appeler la méthode finalize () des objets qui ne sont plus référencés.

public static SecurityManager getSecurityManager ()


vendredi 11 février 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 12

public static void setSecurityManager (SecurityManager s)


throws SecurityException

Permet d'obtenir ou de changer le gestionnaire de sécurité. setSecurityManager () ne peut être appelé


qu'une seule fois.

public static String getProperty (String property) throws SecurityException


public static String getProperty (String property, String default)
throws SecurityException

Permet d'obtenir une des propriétés du système (si le gestionnaire de sécurité le permet). Si la propriété
property n'existe pas, default est renvoyée.
Voici la liste des différentes propriétés définies dans Java 1.0 :
java.version, java.vendor, java.vendor.url, java.home, java.class.version, java.class.path
os.name, os.arch, os.version
file.separator, path.separator, line.separator
user.name, user.home, user.dir

Les valeurs par défaut des propriétés du système peuvent être modifiées directement au lancement de la
Machine Virtuelle en utilisant une ou plusieurs options -Dpropriete=valeur avec la commande java .

public static Properties getProperties () throws SecurityException


public static void setProperties (Properties props) throws SecurityException

Permet d'obtenir ou de modifier toutes les propriétés du système.

public static void load (String filename)


throws SecurityException, UnsatisfiedLinkError
public static void loadLibrary (String libname)
throws SecurityException, UnsatisfiedLinkError

Permet de charger un fichier de code ou une librairie (qui implémentent par exemple des méthodes
native d'une classe).

A partir de Java 1.1, cette classe déclare notamment les méthodes setIn (), setOut () et setErr () qui
permettent de rediriger l'entrée et les sorties standards.

L'application suivante vous permet d'avoir un aperçu des propriétés courantes de votre Machine
Virtuelle. Recopiez la dans un fichier SystemProperties.java , que vous compilez avec l'instruction javac
SystemProperties.java pour ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction java
SystemProperties :

public class SystemProperties


{
public static void main (String [ ] args)
{
System.getProperties ().list (System.out);
}
}
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 1

Les instructions et les opérateurs

Les blocs
if ... else, switch
while, do ... while, for
Les expressions
Les opérateurs
Les conversions (casts)
Priorité des opérateurs

Les blocs

Vous allez pouvoir parcourir très rapidement ce chapitre si vous connaissez déjà le C ou le C++ : C'est
surtout à ce niveau que Java et le C se ressemblent le plus car leur jeu d'instructions et d'opérateurs sont
très proches.

Un bloc est une ensemble dans un ordre quelconque de déclarations de variables locales, d'instructions
(if, switch, while, do, for, try, throw, synchronized ou return), d'affectations, d'appels de méthode ou
de créations d'objets.
A partir de Java 1.1, un bloc peut aussi déclarer n'importe où des classes internes :

{
TypeVariable variableLocale1;
TypeVariable variableLocale2 = valeurOuExpression ;
TypeVariable variableLocale3 [ ];
TypeVariable variableLocale4,
variableLocale5;

// Instructions if, switch, do, while, for, try, throw, synchronized ou return
// ou affectations, appels de méthode ou création d'objets suivis de points virgules ;

// A partir de Java 1.1 : déclaration de classes internes


}

Une instruction peut être éventuellement vide (ne rien faire) : dans ce cas, elle se note ; . Ce type
d'instruction est quelques fois utilisé comme corps des instructions de boucles.
TypeVariable est soit un type de base, soit le nom d'une classe.
A partir de Java 1.1, une variable locale ou un paramètre peut être déclaré final, pour garantir que la
valeur qu'ils contiennent ne sera pas modifiée.
Une variable locale ne peut être déclarée deux fois avec le même nom à l'intérieur d'un même bloc, ni
porter le même nom qu'un des paramètres d'une méthode. De plus, dans le cas d'un bloc imbriqué dans
un autre, une variable locale ne peut porter le même nom qu'une autre variable déclarée dans le bloc
externe.
Une variable locale (ou un paramètre d'une méthode) variable1 déclarée dans un bloc d'une classe
Classe1 peut porter le même nom que celui d'une variable de Classe1. Dans ce cas, la variable
variable1 de Classe1 est cachée par la variable locale variable1. Si vous voulez faire référence à la
variable de la classe dans le bloc, vous pouvez utiliser l'expression Classe1.variable1, si variable1 est
une variable de classe (static), ou l'expression this.variable1 si variable1 est une variable d'instance.
L'utilisation de this est développée plus loin dans ce chapitre.
Toute variable (locale ou non) variable1 déclarée dans une classe Classe1 peut porter le même nom
qu'une méthode de Classe1 ou le même nom qu'une classe.
Une fois que le contrôle quitte un bloc, toutes les variables locales qui y ont été déclarées n'existent
plus. Par conséquent, dans un bloc, la portée d'une variable locale est limitée entre le point où elle est
déclarée et la fin de ce bloc.
De façon similaire, les classes internes ont la même portée que des variables locales.
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 2

Les variables locales en Java, sont toutes "automatiques" : Elles sont détruites une fois sorties du bloc
dans lequel elles sont déclarées. Les mots clé du C auto et register n'existent pas, et static ne peut
pas être utilisé pour les variables locales.
Les variables locales du C/C++ allouées sur la pile d'exécution de type struct, union ou class doivent
être remplacées par des références qui désignent des objets créés dynamiquement avec l'opérateur new.
Leur destruction se fera automatiquement non à la sortie du bloc, mais grâce à l'intervention du Garbage
Collector.
Les variables locales peuvent être déclarées à n'importe où à l'intérieur d'un bloc, comme en C++. Si
une variable locale est utilisée avant d'être initialisée, ceci provoque une erreur de compilation et pas
seulement un warning comme en C/C++.
Contrairement au C, une variable locale déclarée dans un bloc d'une méthode methode1 () ne peut
porter le même nom qu'un des paramètres de methode1 () ou qu'une des variables locales déclarées à
l'extérieur de ce bloc. Les variables locales ne peuvent cacher qu'une variable de classe ou d'instance.
C'est à dire l'exemple suivant provoquera des erreurs à la compilation :

class Classe1
{
int var;
void methode1 (int var) // Pas d'erreur
{
//...
{
int var; // Erreur : var existe déjà comme paramètre
//...
}
}

void methode2 ()
{
int var; // Pas d'erreur
//...
{
int var; // Erreur : var existe déjà comme variable locale
int var2;
//...
} // Après l'accolade fermante var2 n'existe plus.
//...
{
int var2; // Pas d'erreur : var2 n'est pas déclarée
// dans le bloc externe
//...
}
}
}

if ... else, switch

Les instructions if et switch ont la même syntaxe qu'en C/C++, c'est-à-dire :

if (expressionBooleenne )
instructionOuBlocSiVrai

if (expressionBooleenne )
instructionOuBlocSiVrai
else
instructionOuBlocSiFaux

switch (expressionEntiere )
{
case expressionConstante1 :
instructionOuBlocCas1
break; // ou return; ou rien pour passer au case suivant
case expressionConstante2 :
instructionOuBlocCas2
break; // ou return; ou rien pour passer au case suivant
// ...
default :
instructionOuBlocParDefaut
break; // ou return;
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 3

Si les expressions expressionConstantei qui suivent chaque case ne sont pas constantes, vous devez
utiliser l'instruction if.

instructionOuBloc... peut éventuellement contenir les instructions return; ou return valeur; pour
sortir de la méthode courante (valeur doit être du type de retour de la méthode).

Il existe une petite différence entre le C et Java pour l'instruction if (expression) : expression doit
être obligatoirement du type boolean ; Donc, une condition comme if (x = y - 1) ... ne générera
pas un warning (avec certains compilateurs C) mais une erreur de compilation en Java.
Cette obligation alourdit peut être l'écriture mais en améliore la lisibilité.

while, do ... while, for

Les instructions de boucles ont la même syntaxe qu'en C/C++, c'est-à-dire :

while (expressionBooleenne )
instructionOuBloc

Tant que l'expression expressionBooleenne est true, l'instruction ou le bloc d'instruction


instructionOuBloc est exécuté.

do
instructionOuBloc
while (expressionBooleenne );

Comme pour l'instruction while, mais l'instruction ou le bloc d'instruction instructionOuBloc est
exécuté au moins une fois, puisque expressionBooleenne est vérifiée après la première exécution de
instructionOuBloc .

for (expressionInit ; expressionBooleenne ; expressionIncrementation )


instructionOuBloc

for (TypeVariable variableLocale = valeurOuExpression ;


expressionBooleenne ;
expressionIncrementation )
instructionOuBloc

expressionInit est exécuté puis tant que expressionBooleenne est true, l'instruction ou le bloc
d'instruction instructionOuBloc puis expressionIncrementation sont exécutés.

instructionOuBloc peut éventuellement contenir les instructions suivantes :

break; pour sortir de la boucle courante.


continue; pour retourner au while ( ) ou au for ( ) courant sans terminer instructionOuBloc .
return; ou return valeur; pour sortir de la méthode courante (valeur doit être du type de retour
de la méthode).

L'équivalent de l'instruction for avec while est comme suit :

{
expressionInit ;
while (expressionBooleenne )
{
instructionOuBloc ;
expressionIncrementation ;
}
}

Cette équivalence est vraie sauf si instructionOuBloc contient l'instruction continue, En effet, après
l'exécution d'un continue dans instructionOuBloc , l'instruction suivante à être exécutée est l'expression
expressionIncrementation de l'instruction for, tandis que pour l'instruction while c'est l'expression
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 4

expressionBooleenne .

La première instruction du for expressionInit , peut comporter plusieurs initialisation de variables,


séparées par des virgules (,) ; la troisième instruction expressionIncrementation peut elle aussi, être un
ensemble d'instructions séparées par des virgules (,), comme dans l'exemple suivant manipulant deux
indices dans un même for :

class Classe1
{
int [ ] factorielles (int max)
{
// Création d'un tableau de max éléments
int [ ] tableau = new int [max];

// Initialisation du tableau avec les max premières factorielles


for (int i = 0, fact = 1; i < max; i++, fact = fact * i)
tableau [i] = fact;

return tableau;
}
}

Comme pour if, l'expression déterminant la poursuite d'une boucle while ou for doit être du type
boolean.
forpermet de déclarer une ou plusieurs variables locales, qui servent généralement d'incréments
comme dans l'exemple : for (int i = 0; i < 5; i++) table [i] = 0;

Les expressions

Une expression peut prendre une des formes suivantes :

Une valeur littérale d'un des types de base.


Un accès à une variable locale au bloc ou à un paramètre d'une méthode, de la forme variable1.
Un accès à une variable, de la forme variable1 ou expression .variable1. expression est soit
une classe ou une interface si variable1 est une variable de classe (static), soit une référence sur
un objet de Classe1 si variable1 est une variable d'instance de la classe Classe1 ou d'une des
super classes de Classe1.
Une expression pour accéder à un élément d'un tableau, de la forme tab [expression2 ] ou
expression .tab [expression2 ], où expression2 doit être une expression convertible en int. tab
peut être une variable locale, une variable de classe, ou une variable d'instance.
this, super ou null.
Une expression de création de tableau, utilisant l'opérateur new, de la forme new typeOuClasse
[expression ].
Une conversion avec l'opérateur de cast, de la forme (typeOuClasse)expression .
Une expression booléenne utilisant l'opérateur instanceof, de la forme objet instanceof
Classe1.
Une expression utilisant un opérateur arithmétique unaire (opunaire expression ) ou binaire (
expression1 opbinaire expression2 ).

Les expression suivantes peuvent être utilisées comme expression ou comme instruction, si elles sont
suivies d'un point virgule (;) :

Une expression utilisant un des opérateurs arithmétiques unaires ++ ou --, de la forme


++variable1, --variable1, variable1++ ou variable1--.
Un appel à une méthode, de la forme methode (arg1, arg2, ...) ou expression .methode (arg1,
arg2, ...). expression est soit une classe ou une interface si methode () est une méthode de
classe (static), soit une référence sur un objet de Classe1 si methode () est une méthode
d'instance de Classe1 ou d'une des super classes de Classe1. Chacun des arguments peut être bien
sûr une expression.
Si methode () est surchargée, la méthode appelée sera celle dont la déclaration correspond au type
des arguments.
Si methode () est outrepassée, la méthode appelée par l'expression objet.methode () sera la
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 5

méthode methode () de la classe d'appartenance de objet.


Une expression de création d'objet, utilisant l'opérateur new, de la forme new Classe
(argConstructeur ).
Si Classe a plusieurs constructeurs qui sont surchargées, le constructeur appelé sera celui dont la
déclaration correspond au type des arguments.
Une affectation avec l'opérateur = ou les opérateurs d'affectation composés op = (ou op peut être
un des opérateurs suivants : *, /, %, +, -, <<, >>, >>>, &, ^ ou |). Dans ce cas, l'expression x op = y
est équivalente à x = x op y.

Contrairement au C/C++, lors de l'appel d'une méthode en Java, les arguments sont obligatoirement
évalués l'un après l'autre de gauche à droite avant d'être passés à la méthode. Ceci permet d'éviter des
effets de bords imprévisibles. Par exemple, l'expression methode (i = 1, i++, ++i) sera évaluée
comme methode (1, 1, 3).
A part les appels aux méthodes, les créations d'objet, les affectations, l'utilisation des opérateurs ++ et
--, toutes les expressions Java doivent être utilisées (comme argument ou comme valeur à affecter, par
exemple). Vous ne pouvez écrire val1 * val2; tout seul. Ceci permet de repérer des erreurs
d'écritures du type i == 0; qui ne font rien (suite à une opération Copier/Coller trop rapide).
L'opérateur virgule (,) de séparation d'instructions du C, n'existe pas en Java, excepté pour
éventuellement la première et la troisième instruction d'un for, et pour séparer la déclaration de
plusieurs variables de même type (comme par exemple int x, y;). Vous pouvez très bien l'éviter en
rassemblant les instructions dans un bloc

Les expressions constantes utilisées après un case ou pour initialiser les constantes d'une interface, sont
des expressions ne pouvant utiliser que des opérateurs arithmétiques avec des valeurs littérales ou des
variables final initialisées avec des expressions constantes.

Utilisation de this et de super

Si dans une méthode non static methode1 () d'une classe Classe1, vous avez besoin d'une référence de
l'objet sur lequel methode1 () a été invoqué, vous pouvez utiliser le mot clé this. Ceci est illustré dans
l'exemple suivant :

class Classe1
{
boolean methode1 ()
{
return equals (this);
}
}

class Classe2
{
void methode ()
{
Classe1 objet1 = new Classe1 ();
objet1.methode1 ();
}
}

La méthode equals () de la classe Object requiert comme paramètre une référence sur un objet. Ici,
l'argument this lui est passé, qui en fait désigne objet1 (au passage le résultat est forcément true). this
ne peut être utilisé que dans des méthodes non static (une méthode static n'est pas invoquée sur un
objet).
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 6

Pour bien comprendre ce que représente this et l'appel d'une méthode sur un objet, voici un exemple de
programme C (et non C++), avec une traduction possible en Java :
/* Définition d'un type de structure */ // Déclaration d'une classe
* utilisant un int */ // utilisant un int
typedef struct class Type1
{ {
int unEntier; int unEntier;
}
Type1;

void modifier (Type1 *objet1, int var) void modifier (int var)
{ {
// this est facultatif
objet1->unEntier = var; this.unEntier = var;
} }
}

class Classe1
{
/* Fonction créant un objet de type */ // Méthode créant un objet de classe
* Type1 et appelant modifier () */ // Type1 et appelant modifier ()
void uneMethode () void uneMethode ()
{ {
Type1 *objet1 = malloc (sizeof (Type1)); Type1 objet1 = new Type1 ();
modifier (objet1, 10); objet1.modifier (10);
} }
}

A l'appel objet1.modifier (10); plusieurs opérations sont effectuées implicitement :

1. La classe Type1 de objet1 est recherchée.


2. La méthode modifier () de Type1 est appelée avec son argument 10.
3. La "variable" this est initialisée avec objet1 dans la méthode modifier ().

this peut être utilisé seul pour désigner une référence d'un objet, instance de la classe Classe1 ou suivi
de l'opérateur point (.) pour accéder aux variables et méthodes d'instance d'un objet. Ceci peut être utile
pour distinguer une variable et un paramètre (ou une variable locale) qui ont le même nom, comme dans
l'exemple suivant :

class Classe1
{
int var;
int var2;

Classe1 (int var)


{
this.var = var;
var2 = var + 1; // équivalent à this.var2 = this.var + 1;
}
}

En fait, à chaque fois que vous utilisez une méthode ou une variable non static de la classe Classe1, à
l'intérieur d'une des méthodes de Classe1, vous utilisez implicitement this, mais pour des commodités
d'écriture, il ne vous est requis de l'écrire que pour éviter toute confusion.

A partir de Java 1.1, une nouvelle utilisation de this a été introduite pour distinguer si besoin est,
l'instance d'une classe interne ClasseI de l'instance de la classe Classe0 dans laquelle ClasseI est
déclarée : ClasseI.this désigne l'instance de la classe interne ClasseI et Classe0.this désigne
l'instance de la classe Classe0.

Pour référencer l'objet de la super classe de this, on utilise super. Dans la pratique, super est surtout
utilisé pour invoquer explicitement la méthode outrepassée d'une super classe.
Un exemple d'utilisation de super est donné au chapitre sur la création des classes.
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 7

L'opérateur -> n'existe pas. Donc, comme pour toute référence en Java, this est suivi de l'opérateur
point (.) pour accéder aux variables et méthodes d'instance d'un objet.
Contrairement au C++, this ne peut être la variable de destination d'une expression d'affectation,
c'est-à-dire que par exemple, Java n'accepte pas l'expression this = unObjet.

Les opérateurs

Opérateurs arithmétiques

++, -- Opérateurs de post incrémentation et post décrémentation : variable++ ajoute 1 à variable,


et renvoie la valeur de variable avant que celle-ci soit incrémentée.
variable-- retire 1 à variable, et renvoie la valeur de variable avant que celle-ci soit
décrémentée.
++, -- Opérateurs de pré incrémentation et pré décrémentation : ++variable ajoute 1 à variable, et
renvoie la nouvelle valeur de variable.
--variable retire 1 à variable , et renvoie la nouvelle valeur de variable.
+, - +expression n'a aucun effet sur expression , -expression renvoie la valeur opposée de
expression .
~ ~expressionEntiere renvoie le complément de expressionEntiere , bit à bit. Par exemple,
~0xA7 (10100111 en binaire) est égale à 0x58 (01011000 en binaire).
! !expressionBooleenne renvoie le complément logique de expressionBooleenne , c'est-à-dire
false si expressionBooleenne est égale à true, et inversement.
*, / expression1 * expression2 renvoie la multiplication de expression1 par expression2 .
expression1 / expression2 renvoie la division de expression1 par expression2 , ou
déclenche une exception ArithmeticException, si expression2 est nulle et la division
s'opère sur des expressions entières.
% expression1 % expression2 renvoie le reste de la division de expression1 par
expression2 , ou déclenche une exception ArithmeticException, si expression2 est nulle et
les expressions sont des entiers.
+, - expression1 + expression2 renvoie l'addition de expression1 et de expression2 .
Si l'un des opérandes expression1 ou expression2 est de la classe String, l'autre opérande
est converti en String et le résultat est la concaténation des deux chaînes.
expression1 - expression2 renvoie la valeur de expression1 + (-expression2 ).
<<, >>, >>> expressionEntiere << nbreDecalage renvoie la valeur de expressionEntiere dont les bits
sont décalés à gauche, nbreDecalage fois.
expressionEntiere >> nbreDecalage renvoie la valeur de expressionEntiere dont les bits
sont décalés à droite, nbreDecalage fois, avec extension du bit de signe.
expressionEntiere >>> nbreDecalage renvoie la valeur de expressionEntiere dont les
bits sont décalés à droite, nbreDecalage fois, avec introduction de zéro à gauche.
<, >, <=, >= expression1 < expression2 renvoie true si expression1 est strictement inférieure à
expression2 .
expression1 > expression2 renvoie true si expression1 est strictement supérieure à
expression2 .
expression1 <= expression2 renvoie true si expression1 est inférieure ou égale à
expression2 .
expression1 >= expression2 renvoie true si expression1 est supérieure ou égale à
expression2 .
==, != expression1 == expression2 renvoie true si expression1 est égale à expression2 . Si
expression1 et expression2 sont des références sur des objets, le résultat est true si les 2
références désignent le même objet, ou si elles sont égales à null.
expression1 != expression2 renvoie true si expression1 est différente de expression2 .
expression1 != expression2 est équivalent à !(expression1 == expression2 ).
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 8

&, ^, | expressionEntiere1 & expressionEntiere2 renvoie le résultat d'un ET bit à bit entre les
opérandes.
expressionEntiere1 ^ expressionEntiere2 renvoie le résultat d'un OU exclusif bit à bit
entre les opérandes.
expressionEntiere1 | expressionEntiere2 renvoie le résultat d'un OU bit à bit entre les
opérandes.
Les opérandes peuvent être éventuellement de type boolean.
&&, || expressionBooleenne1 && expressionBooleenne2 renvoie le résultat d'un ET logique entre
les opérandes. Si expressionBooleenne1 est false alors expressionBooleenne2 n'est pas
évaluée, et false est tout de suite renvoyé.
expressionBooleenne1 || expressionBooleenne2 renvoie le résultat d'un OU logique entre
les opérandes. Si expressionBooleenne1 est true alors expressionBooleenne2 n'est pas
évaluée, et true est tout de suite renvoyé.
? : expressionBooleenne1 ? expression1 : expression2 renvoie la valeur de expression1 si
expressionBooleenne1 est true, sinon la valeur de expression2 .
x = expressionBooleenne1 ? expression1 : expression2 ; est équivalent à :

if (expressionBooleenne1 )
x = expression1 ;
else
x = expression2 ;

Java a deux opérateurs de décalages à droite >> et >>>. >> recopie à gauche le bit de signe tandis que
>>> introduit des zéros à gauche.

L'opérateur instanceof

L'opérateur instanceof permet de vérifier si une référence désigne un objet d'une certaine classe ou
d'une certaine interface. Il s'utilise de la manière suivante : objet instanceof Classe1 et le résultat est
true si objet désigne une instance de Classe1 ou des classes dérivées de Classe1 , et false sinon. Si
objet est égal à null, le résultat est toujours false.
Cet opérateur est surtout utile pour évaluer la classe Classe1 d'un objet dont la référence est d'une
super classe de Classe1, comme dans l'exemple suivant :

class Classe0
{ }

class Classe1 extends Classe0


{ }

class UneClasse
{
void methode ()
{
Classe0 objet1 = new Classe1 ();
Classe0 objet2 = new Classe0 ();
Object objet3 = new Classe0 ();
Object objet4 = null;

boolean resultat1 = objet1 instanceof Classe1; // true


boolean resultat2 = objet1 instanceof Classe0; // true
boolean resultat3 = objet2 instanceof Classe1; // false
boolean resultat4 = objet3 instanceof Classe0; // true
boolean resultat5 = objet4 instanceof Object; // false
}
}

Par extension, objet instanceof Object est toujours égal à true si objet est différent de null (toute
classe hérite de Object et donc tout objet est une instance d'une classe dérivée de Object).

Java introduit l'opérateur instanceof qui permet de vérifier si une référence désigne un objet qui est
une instance d'une classe ou d'une interface donnée.
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 9

Opérateurs du C/C++ absent en Java

OPERATEUR DESCRIPTION

-> Opérateur primaire d'accès aux pointeurs


:: Opérateur primaire d'accès aux classes
* & Opérateurs unaires pour les pointeurs
sizeof

delete Opérateur de destruction d'objet

Les conversions (casts)

Il existe 5 contextes dans lesquels une conversion peut avoir lieu :

Les conversions intervenant dans une affectation d'une expression, variable = expression, où le
type de expression doit être converti dans le type de variable.
Les conversions appliquées sur chacun des arguments d'une méthode pour que leur type
correspondent à celui déclaré par chacun des paramètres de la méthode. Ces conversions se font
de la même manière que pour les affectations.
Les conversions explicites du programmeur par un opérateur de cast. Celles-ci permettent
d'effectuer n'importe quelle conversion d'un type dans un autre, si ce sont des types de base, et
d'une classe dans une autre classe, si ces deux classes ont un lien d'héritage entre elles.
Les conversions en chaîne de caractères permettent de convertir n'importe quel type en un objet
de classe String.
Les conversions nécessaires pour que dans une opération numérique comme oper1 op oper2, les
opérandes oper1 et oper2 soient convertis dans un même type pour que l'opérateur op puisse être
appelé :
Si l'un des deux opérandes est du type double, l'autre est converti en double.
Si l'un des deux opérandes est du type float, l'autre est converti en float.
Si l'un des deux opérandes est du type long, l'autre est converti en long.
Sinon, les deux opérandes sont convertis en int.

Conversions entre types de base avec gain de précision

Conversions de byte en short, int, long, float ou double.


Conversions de short en int, long, float ou double.
Conversions de char en int, long, float ou double.
Conversions de int en long, float ou double.
Conversions de long en float ou double.
Conversions de float en double.

Toutes ces conversions ne font perdre aucune précision au nombre converti. Elles ne requièrent aucun
opérateur de cast, le compilateur effectue automatiquement ces conversions, mais pour clarifier votre
code, vous pouvez utiliser un cast.

Conversions entre types de base avec perte de précision

Conversions de byte en char.


Conversions de short en byte ou char.
Conversions de char en byte ou short.
Conversions de int en byte, short ou char.
Conversions de long en byte, short, char ou int.
Conversions de float en byte, short, char, int ou long.
Conversions de double en byte, short, char, int, long ou float.

Ces conversions ne sont autorisées par le compilateur que si le nombre à convertir est converti
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 10

explicitement, grâce à l'opérateur de cast qui s'utilise ainsi : nombreType1 = (type1)nombreType2.

Conversions de références d'une classe dans une autre

Toutes les conversions suivantes sont possibles sans cast explicite dans la programmation :
1. Conversion d'une référence de classe ClasseY dans une référence de classe ClasseX, si ClasseY est une
classe dérivée directement ou indirectement de ClasseX (qui peut être la classe Object). Sachant que
toute objet objetClasseY de la classe ClasseY hérite des variables et des méthodes de ClasseX,
objetClasseY peut aussi être considéré comme un objet de ClasseX.
2. Conversion d'une référence de classe ClasseX dans une référence d'interface InterfaceX, si ClasseX
implémente l'interface InterfaceX.
3. Conversion d'une référence sur une interface InterfaceZ dans une référence sur une interface
InterfaceX, si InterfaceZ est une interface dérivée de InterfaceX.
4. Conversion d'une référence sur une interface ou un tableau dans une référence sur la classe Object.
5. Conversion d'une référence sur un tableau de classe ou d'interface ClasseY (ClasseY tabY [ ]) dans une
référence sur un tableau de classe ou d'interface ClasseX (ClasseX tabX [ ]), si ClasseY peut être
converti en ClasseX suivant les critères précédents.

Il faut bien percevoir l'intérêt de ces conversions car c'est grâce à l'héritage, à la possibilité d'outrepasser des
méthodes et grâce à ces conversions que Java (comme tout langage orienté objet) permet de mettre en pratique
les concepts de la programmation orientée objet. Comme ce sont des casts implicites, on ne s'aperçoit pas
forcément qu'on les utilise...
Voici des exemples de cast implicite entre références :

interface InterfaceX
{ }

interface InterfaceZ extends InterfaceX


{ }

class ClasseX implements InterfaceX


{ }

class ClasseY extends ClasseX


{ }

class ClasseZ implements InterfaceZ


{ }

class UneClasse
{
void uneMethode ()
{
// Création d'un objet de chaque classe
ClasseX objetClasseX = new ClasseX ();
ClasseY objetClasseY = new ClasseY ();
ClasseZ objetClasseZ = new ClasseZ ();

// Création de tableaux d'un élément


ClasseX [] tabClasseX = new ClasseX [1];
ClasseY [] tabClasseY = new ClasseY [1];
ClasseZ [] tabClasseZ = new ClasseZ [1];

InterfaceZ [] tabInterfaceZ = new InterfaceZ [1];

// Cast implicite sur les objets


ClasseX objetX = objetClasseY; // 1. cast de ClasseY vers ClasseX
Object objet = objetClasseY; // cast de ClasseY vers Object

InterfaceX objetInterfaceX = objetClasseX; // 2. cast de ClasseX vers InterfaceX


objetInterfaceX = objetClasseY; // cast de ClasseY vers InterfaceX
// classeY dérive de classeX
// donc classe Y implémente aussi InterfaceX

InterfaceZ objetInterfaceZ = objetClasseZ; // 3. cast de ClasseZ vers InterfaceZ


objetInterfaceX = objetInterfaceZ; // cast de InterfaceZ vers InterfaceX

objet = objetInterfaceX; // 4. cast de InterfaceZ vers Object


objet = tabClasseY; // cast d'un tableau vers Object
objet = tabInterfaceZ; // cast d'un tableau vers Object
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 11

// 5. Les mêmes cas de cast mais sur les tableaux


ClasseX [] tabObjetsX = tabClasseY;
Object [] tabObjets = tabClasseY;

InterfaceX [] tab2InterfaceX = tabClasseX;


tabInterfaceX = tabInterfaceZ;

InterfaceZ [] tabInterfaceZ = tabClasseZ;


tabInterfaceX = tabInterfaceZ;

tabObjets = tabInterfaceX;
}
}

Les conversions suivantes ne sont par contre possibles que si vous utilisez explicitement un opérateur de cast,
de la manière suivante : objetClasseY = (ClasseY)objetClasseX.
1. Conversion d'une référence de classe ClasseX dans une référence de classe ClasseY, si ClasseX est une
super classe de ClasseY . Ceci implique notamment qu'une référence de classe Object peut être
convertie dans une référence de n'importe quelle classe.
2. Conversion d'une référence de classe ClasseX dans une référence d'interface InterfaceX, si ClasseX
n'est pas final et que ClasseX n'implémente pas l'interface InterfaceX (ceci veut dire que la conversion
sera acceptée à l'exécution si une classe dérivée de ClasseX implémente InterfaceX).
3. Conversion d'une référence sur la classe Object dans une référence sur une interface ou un tableau.
4. Conversion d'une référence d'interface InterfaceX dans une référence de classe ClasseX qui n'est pas
final, ou qui est final et implémente l'interface InterfaceX.
5. Conversion d'une référence d'interface InterfaceX dans une référence d'interface InterfaceZ, si
InterfaceX n'est pas une interface dérivée de InterfaceZ, et que les deux interfaces ne déclarent
aucune méthode ayant les mêmes paramètres mais un type de retour différent.
6. Conversion d'une référence sur un tableau de classe ou d'interface ClasseX (ClasseX tabX [ ]) dans une
référence sur un tableau de classe ou d'interface ClasseY (ClasseY tabY [ ]), si ClasseX peut être
converti en ClasseY suivant les critères précédents.

Toutes ces conversions sont acceptées par le compilateur, mais à l'exécution peuvent être refusées après
vérification et provoquer le déclenchement d'une exception ClassCastException, Cette exception est
déclenchée si la référence désignant un objet de classe ClasseX (celle utilisée à sa création par l'instruction new
ClasseX ()) n'est pas convertie dans la classe ClasseX ou dans une classe ou une interface dans laquelle une
référence de ClasseX est convertible implicitement.
Voici des exemples de cast explicite entre références (attention, les classes ne sont pas les mêmes que dans
l'exemple des conversions implicites) :

interface InterfaceX
{ }

interface InterfaceZ extends InterfaceX


{ }

class ClasseX
{ }

class ClasseY extends ClasseX implements InterfaceX


{ }

class ClasseZ implements InterfaceZ


{ }

class UneClasse
{
void uneMethode ()
{
// Création d'un objet de chaque classe
Object objet1 = new ClasseX (); // cast implicite de ClasseX vers Object
ClasseX objet2 = new ClasseY (); // cast implicite de ClasseY vers ClasseX
Object objet3 = new ClasseZ (); // cast implicite de ClasseZ vers Object

// Création de tableaux d'un élément


Object tab1 = new ClasseX [1];

// Cast explicite sur les objets


vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 12

ClasseY objetY = (ClasseY)objet2; // 1. cast de ClasseX vers ClasseY


ClasseX objetX = (ClasseX)objet1; // cast de Object vers ClasseX
objetY = (ClasseY)objet1; // cast de Object vers ClasseY
// acceptée à la compilation mais déclenche
// une exception ClassCastException à l'exécution
// car objet1 ne désigne pas un objet de classe ClasseY

InterfaceX objet InterfaceX =


(InterfaceX)objet2; // 2. cast de ClasseX vers InterfaceX
objet InterfaceX = (InterfaceX)objet1; // cast de Object vers InterfaceX
// acceptée à la compilation mais déclenche
// une exception ClassCastException à
// l'exécution car objet1 désigne un objet
// de classe ClasseX et classeX n'implémente
// pas InterfaceX

InterfaceZ objetInterfaceZ =
(InterfaceZ)objet3; // 3. cast de Object vers InterfaceZ
ClasseX [ ] tabObjetsX = (ClasseX [ ])tab1; // cast de Object vers ClasseX [ ]
tabObjetsX = (ClasseX [ ])objet1; // cast de Object vers ClasseX [] acceptée
// à la compilation mais déclenche une
// exception ClassCastException à
// l'exécution car objet1 ne désigne pas
// un objet de classe ClasseX [ ]

ClasseZ objetZ = (ClasseZ)objetInterfaceZ; // 4. cast de InterfaceZ vers ClasseZ

objet InterfaceX = objetZ; // cast implicite de ClasseZ vers InterfaceX


objetInterfaceZ =
(InterfaceZ)objet InterfaceX; // 5. cast de InterfaceX vers InterfaceZ
}
}

La figure suivante résume les conversions les plus usuelles que vous serez amené à utiliser :

figure 8. Conversions implicites/explicites

Les casts s'opèrent sur les références et non sur les objets. Dans les deux lignes suivantes :

Classe2 objetClasse2 = new Classe2 ();


Classe1 objetClasse1 = (Classe1)objetClasse2;

objetClasse1 et objetClasse2 ne sont que des références sur un objet de classe Classe2.
Le fait d'effectuer le cast (Classe1) (qu'il soit explicite ou non) ne transforme en rien l'objet de classe
Classe2. Ceci est très important, car si vous invoquez une méthode de Classe1, par l'expression
objetClasse1.methode1 () et que methode1 () est outrepassée dans Classe2, ce sera effectivement la
méthode methode1 () de Classe2 qui sera appelée, car objetClasse1 désigne un objet de Classe2.
vendredi 11 février 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 13

Les casts automatiques entre types de base sont moins nombreux en Java. Aussitôt qu'il y a perte de
précision possible (float en int, long en byte, par exemple), vous devez faire un cast explicite.
Les casts d'une référence d'un type de classe dans un autre ne sont possibles que si la classe de
destination a un lien direct d'héritage avec la classe de l'objet casté. Si la classe de conversion n'est pas
la classe réelle de l'objet ou une de ses super classes, une exception ClassCastException est
déclenchée.
Plus simplement, vous ne pouvez, comme en C, caster une référence sur un type dans n'importe quel
autre type.
Java ne permet pas de surcharger les opérateurs de cast, mais vous pouvez éventuellement utiliser les
constructeurs pour compenser cette absence : pour convertir un objet de classe Classe1 en Classe2,
vous créer un constructeur dans la classe Classe2 prenant en paramètre une référence de classe
Classe1 :

class Classe2
{
public Classe2 (Classe1 objetACaster)
{
// Retranscrire les variables de Classe1
// en celles de Classe2
}
}

Il vous suffit de créer un objet de Classe2 ainsi: Classe2 objetCaste = new Classe2
(objetDeClasse1);.
Attention, dans ce cas vous créez un autre objet, qui est réellement de classe Classe2, contrairement à
un cast qui ne crée aucun objet mais seulement convertit une référence d'une classe dans une autre.

Priorité des opérateurs

Tableau de précédence (priorité) des opérateurs. Les opérateurs sur une même ligne ont la même
priorité, et sont rangés ligne par ligne du plus prioritaire au moins prioritaire.

OPERATEUR ASSOCIATIVITE DESCRIPTION


. ( ) [ ] new de gauche à droite Opérateurs primaires
! ~ ++ -- + - (cast ) de droite à gauche Opérateurs unaires
* / % de gauche à droite Multiplication, division, reste
+ - de gauche à droite Addition, soustraction
<< >> >>> de gauche à droite Décalages
< <= > >= instanceof de gauche à droite Comparaisons
== != de gauche à droite Egalité, différence
& de gauche à droite Opérateur ET bit à bit
^ de gauche à droite Opérateur OU EXCLUSIF bit à bit
| de gauche à droite Opérateur OU bit à bit
&& de gauche à droite Opérateur ET
|| de gauche à droite Opérateur OU
? : de gauche à droite Condition
= *= /= %= += -= <<= >>= >>>= &= ^= |= de droite à gauche Affectation
L'opérateur new a la même priorité que l'opérateur . , ce qui permet d'écrire directement new Classe1
().methode1 (). En C++, il faut écrire (new Classe1 ())->methode1 ().
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 1

Les exceptions

throw, try, catch,...


La classe Throwable
Les exceptions Runtime
Les classes d'erreurs
Les autres exceptions

throw, try, catch,...

Pourquoi traiter des exceptions dès maintenant ? Parce qu'en Java, contrairement au C et au C++, les
exceptions et leur traitement font partie intégrante du langage et sont utilisées systématiquement pour
signaler toute erreur survenue pendant l'exécution d'une méthode (débordement d'indice d'un tableau,
erreur d'accès à un fichier,...). De nombreuses méthodes sont susceptibles de déclencher (throw ) des
exceptions et donc il est impératif de savoir comment les traiter ou passer outre.
Une fois acquis le principe de cette forme de traitement d'erreur, vous pourrez utiliser les classes
d'exceptions Java existantes ou créer vos propres classes pour traiter les erreurs qui peuvent survenir
dans vos méthodes.

La gestion d'erreur par exceptions permet d'écrire de manière plus claire (donc plus maintenable) un
programme, en isolant le traitement d'erreur de la suite d'instructions qui est exécutée si aucune erreur
ne survient. Généralement, dans les langages ne disposant pas des exceptions (comme le C), les
fonctions susceptibles de poser problème renvoient des valeurs que vous devez traiter immédiatement
pour vérifier si aucune erreur n'est survenue.

La gestion des erreurs se fait grâce aux exceptions en Java. Donc, il n'existe pas de variables telles que
errno... Les exceptions sont d'un abord plus difficile mais une fois compris le principe, la
programmation des erreurs se fait plus simplement et "proprement".
Les exceptions font partie du noyau du langage Java et leur gestion est obligatoire.

Syntaxe

En Java, cinq mots-clé servent à traiter les exceptions :

L'instruction throw exception1; permet de déclencher l'exception exception1. exception1 doit


être une référence sur un objet d'une classe d'exception ClasseException . Toutes les classes
d'exception sont de la classe Throwable ou de ses dérivées (les classes Error, Exception,
RuntimeException et leur dérivées). Une exception peut être déclenchée par le système ou
n'importe où dans une méthode.
Le bloc suivant l'instruction try permet d'encadrer les séries d'instructions où une ou plusieurs
exceptions sont susceptibles d'être déclenchées. Les instructions de ce bloc représentent le
traitement normal de votre programme. Il peut donc comporter des instructions déclenchant ou
non des exceptions et éventuellement des instructions throw.
Le bloc de l'instruction try doit être suivi d'une ou plusieurs instructions catch, et chacun de ces
catch doit être suivi d'un bloc d'instructions. Chaque catch spécifie quelle classe d'exception il est
capable d'intercepter, quand une exception est déclenchée dans le bloc du try précédent le catch.
Un catch capable de traiter la classe ClasseException ou une des classes dérivées de
ClasseException respecte la syntaxe suivante :

try
BlocInstructionsTry
catch (ClasseException exceptionInterceptee)
BlocInstructions
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 2

Quand une exception exception1 de classe ClasseException est déclenchée dans le bloc
BlocInstructionsTry , le contrôle passe au premier catch suivant BlocInstructionsTry qui traite
la classe d'exception ClasseException . Ce catch reçoit en paramètre l'exception déclenchée.
Si aucun de ces catch n'est capable d'intercepter exception1, le contrôle est rendu au premier
catch capable d'intercepter une exception de classe ClasseException , parmi les méthodes
mémorisées dans la pile d'exécution et exécutant un try ... catch. Si aucun catch n'est rencontré,
la Machine Virtuelle Java indique l'exception qui est survenue et arrête le thread dans laquelle elle
est survenue (ce qui généralement bloque le programme).
Le bloc instructions d'un catch peut éventuellement redéclencher l'exception interceptée
exceptionInterceptee pour la propager dans la pile d'exécution, grâce à l'instruction throw
exceptionInterceptee;.
Le bloc d'instructions du dernier catch peut être optionnellement suivi de l'instruction finally,
suivi lui aussi d'un bloc d'instructions spécifiant les instructions à exécuter à la suite du bloc try si
aucune exception n'a été déclenchée ou à la suite du traitement d'un catch.
Dans la déclaration d'une méthode methode1 (), le mot-clé throws permet de déclarer la liste des
classes d'exceptions que methode1 () est susceptible de déclencher. methode1 () peut déclencher
des exceptions dans les deux situations suivantes :
Elle appelle une ou plusieurs instructions throw exception1; et n'intercepte pas toutes ces
exceptions avec l'instruction catch.
Elle appelle d'autres méthodes susceptibles de déclencher des exceptions et n'intercepte pas
toutes ces exceptions.

Seules les classes d'exceptions RuntimeException, Error et toutes leurs classes dérivées, ne sont
pas obligées d'être citées après throws.
Ce type de déclaration permet d'être sûr qu'une exception déclenchée par une méthode sera
toujours traitée ou ignorée sciemment par le programmeur.

Chacune des instructions try, catch et finally doivent être suivi d'un bloc { ... } même si ce bloc ne
comporte qu'une seule d'instruction (Désolé, les afficionados du code compact en seront pour leurs
frais !).

Voici un exemple de traitement local d'une exception déclenchée grâce à throw dans un bloc try et
interceptée par un des catch qui suivent :

class Classe0
{
// ...
void methode1 ()
{
try
{
// ...
throw new Exception ();
}
catch (Exception exception1)
{
// Que faire en cas d'exception ?
}
}
}

Une méthode qui est susceptible de déclencher une exception peut s'écrire ainsi :

class Classe1
{
// ...
void methode1 () throws ClasseException
{
// ...
// En cas d'erreur, déclenchement d'une exception
throw new ClasseException ();
// ...
}
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 3

ClasseException est soit une classe prédéfinie dérivée de la classe Throwable, soit une classe dérivée
d'une de ces classes créé par vous.

Quand vous appelez methode1 (), vous devez soit inclure l'appel à cette méthode dans un try ... catch,
soit déclarer que la méthode qui appelle methode1 () est susceptible de déclencher une exception de la
classe ClasseException , comme dans l'exemple suivant :

class Classe2
{
Classe1 objet1 = new Classe1 ();
// ...
void methodeX ()
{
try
{
objet1.methode1 ();
// ...
}
catch (ClasseException exception1)
{
// Que faire en cas de problème ?
}
// ... Eventuellement d'autres catch (...)
finally
{
// Le bloc finally est optionnel
// Que faire après que le bloc try ou
// qu'un bloc catch aient été exécutés ?
}
}

void methodeY () throws ClasseException


{
objet1.methode1 ();
// ...
}
}

Le bloc finally est toujours exécuté, même si l'instruction return est exécutée dans les blocs try et
catch : il sert à regrouper les instructions qu'il faut exécuter pour laisser dans un état correct votre
programme qu'une exception est été déclenchée ou non. Par exemple, si le bloc try traite des accès à un
fichier (ouverture, lecture/écriture,...), il est logique de fermer ce fichier dans le bloc finally, pour qu'il
soit toujours finalement fermé.
Si une exception exception1 est déclenchée dans un bloc try et qu'aucun catch qui suit try n'intercepte
exception1, alors le bloc finally est exécuté avant que le système ne continue à propager l'exception et
trouve (ou non) un catch traitant les exceptions de la classe de exception1.
Si une exception exception2 est déclenchée dans un bloc catch, alors le bloc finally est aussi exécuté
avant que le système ne continue à propager cette exception et trouve (ou non) un catch traitant les
exceptions de la classe de exception2.
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 4

figure 9. Chemin parcouru lors du traitement d'exceptions

La figure précédente illustre les chemins différents par lesquels peut passer le contrôle dans les
méthodes methodeX () et methodeY (), suivant qu'une exception dans methode1 () est déclenchée
(chemins vert et jaune) ou non (chemins rouge et bleu).

Afin de bien comprendre la gestion des erreurs avec les exceptions, voici un programme C typique traduit
en Java où vous pourrez faire le parallèle entre constantes numériques façon C, et exception façon Java (à
recopier dans un fichier EssaiException.java compilé avec l'instruction javac EssaiException.java, pour
ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction java EssaiException) :

/* Déclaration des constantes d'erreur */ // Déclaration d'une classe d'exception


#define ERR_OK 0 class AEgalBException extends Exception
#define ERR_A_EGAL_B 1 {
public String toString ()
{
return "A égal à B !";
}
}

public class EssaiException


{
int uneMethode (int a, int b) static void uneMethode (int a, int b)
throws AEgalBException
{ {
if (a == b) if (a == b)
return ERR_A_EGAL_B; throw new AEgalBException ();
else else
{ System.out.println
printf ("%d et %d OK !\n", a, b); (a + " et " + b + " OK !");
return ERR_OK; }
}
}

int main () public static void main


(String [ ] args)
{ {
int erreur; try
if ((erreur = uneMethode (1, 2)) {
== ERR_OK) uneMethode (1, 2);
printf ("Pas d'erreur\n"); System.out.println ("Pas d'erreur");
else }
if (erreur == ERR_A_EGAL_B) catch (AEgalBException e)
printf ("Erreur\n"); {
} System.out.println ("Erreur " + e);
}
}
}
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 5

Voici un autre exemple d'application dont la méthode newObject () permet de créer un objet en
connaissant le nom de sa classe. Cette méthode simplifie le traitement des exceptions que peuvent
déclencher les méthodes forName () et newInstance () de la classe Class en renvoyant une exception
de classe IllegalArgumentException qu'il n'est pas obligatoire d'intercepter (à recopier dans un fichier
InstantiationAvecNom.java compilé avec l'instruction javac InstantiationAvecNom.java pour ensuite
l'exécuter avec java ou Java Runner , grâce à l'instruction java InstantiationAvecNom) :

public class InstantiationAvecNom


{
// Methode transformant toutes les exceptions qui peuvent survenir
// pendant l'instanciation d'une classe en une exception IllegalArgumentException.
// nomClasse doit indiquer un nom de classe avec son package
public static Object newObject (String nomClasse)
{
try
{
return Class.forName (nomClasse).newInstance ();
}
catch (ClassNotFoundException e)
{
throw new IllegalArgumentException (
"La classe " + nomClasse + " n'existe pas");
}
catch (InstantiationException e)
{
throw new IllegalArgumentException (
"La classe " + nomClasse + " est abstract");
}
catch (IllegalAccessException e)
{
throw new IllegalArgumentException (
"La classe " + nomClasse + " n'est pas public");
}
catch (NoSuchMethodError e)
{
throw new IllegalArgumentException (
"La classe " + nomClasse +
" n'a pas de constructeur par d\u00e9faut");
}
}

public static void main (String [ ] args)


{
// Essai avec différentes classes
String nomsClasses [] = {"java.lang.Object", // Ok
"java.lang.String", // Ok
"java.lang.Integer", // Pas de constructeur par défaut
"java.lang.Runnable"}; // Interface (= classe abstract)

for (int i = 0; i < nomsClasses.length; i++)


try
{
System.out.println (nomsClasses [i] + " : " + newObject (nomsClasses [i]));
}
catch (IllegalArgumentException e)
{
System.out.println (e);
System.out.println ("La classe " + nomsClasses [i]
+ " ne peut etre instancie par Class.forName (\""
+ nomsClasses [i] + "\").newInstance ();");
}
}
}

Autres exemples

Applets Chrono , ObservateurCalcul , EchoClient , PaperBoardClient , PlayApplet , CalculetteSimple ,


BoutonsNavigation , AnimationFleche , ScrollText et Horloge .
Applications LectureFichier , NumerotationLigne , ConcatenationFichiers , TestProtocole , HelloFromNet ,
EchoServer et PaperBoardServer .
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 6

Avantages des exceptions

Bien que d'un abord plus compliqué qu'une gestion d'erreur avec des constantes numériques, les
exceptions comportent de nombreux avantages que vous percevrez à l'usage :

Chaque exception est une instance d'une classe : cette classe peut comporter toute sorte de
méthodes et de variables, qui permettent une gestion d'erreur bien plus riche qu'une simple
constante numérique. De plus, vous pouvez créer une hiérarchie de classes d'exceptions, si besoin
est.
Une méthode methode1 () est obligée de déclarer la liste des classes d'exceptions qu'elle est
susceptible de déclencher, grâce à la clause throws. Ceci oblige les utilisateurs de methode1 () de
prendre en compte ces exceptions, soit en les traitant dans un try ... catch (voir methodeX ()), soit
en les ajoutant à la liste des classes d'exceptions déclenchées par leur méthode (voir methodeY ()).
Cette obligation peut paraître lourde à priori, mais elle assure une gestion correcte des erreurs qui
peuvent survenir dans un programme. (Qui peut affirmer qu'il a toujours géré toutes les erreurs
dans un programme C ?...).
Le bloc d'instructions d'un try représente la suite des instructions qui sont censées se dérouler s'il
n'y a pas d'erreur. Quand vous retournez des codes d'erreurs, vous devez les tester tout de suite
pour traiter les cas d'erreurs éventuelles : ceci peut nuire à la lisibilité du code.
Quand une exception est déclenchée, le système recherche dans la pile d'exécution la première
méthode qui traite cette exception dans un bloc catch. Comme dans l'exemple qui suit, ceci permet
éventuellement de centraliser vos traitements d'exception dans une méthode methodePrincipale
() au lieu de traiter toutes les exceptions qui peuvent survenir dans chacune des méthodes
methodeI () où pourrait survenir une exception.

class UneClasse
{
private void methodeQuiDeclencheUneException () throws Exception
{
throw new Exception ();
}

private void methode1 () throws Exception


{
methodeQuiDeclencheUneException ();
}

private void methode2 () throws Exception


{
methode1 ();
methodeQuiDeclencheUneException ();
}

private void methode3 () throws Exception


{
methode1 ();
}

public void methodePrincipale ()


{
try
{
methode2 ();
methode3 ();
}
catch (Exception exception)
{
// Que faire en cas d'exception
}
}
}
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 7

L'équivalent de la clause catch (...) du C++ est catch (Throwable exception). En effet, toutes les
classes d'exceptions Java héritent de la classe Throwable.
La clause throw; qui permet de redéclencher une exception traitée dans un catch, a pour équivalent en
Java throw exception;, où exception est l'exception reçu en paramètre par le catch.
Java introduit le mot-clé throws qui permet de spécifier la liste des classes d'exceptions que peut
déclencher une méthode, et que doit prendre en compte tout utilisateur de cette méthode (certains
compilateurs C++ utilisent throw).
Le traitement des exceptions en Java comporte une clause supplémentaire et optionnelle par rapport au
C++ : l'instruction finally. Cette instruction permet de spécifier l'ensemble des instructions à
exécuter une fois terminé le bloc d'instructions d'un try ou d'un des catch, qu'une exception ait été
déclenchée ou non.

Soit methode1 () une méthode d'une classe Classe1, déclarant avec la clause throws une liste d'exception
ClasseExceptionI qu'elle est susceptible de déclencher. Si methode1 () est outrepassée dans une classe
Classe2 dérivée de Classe1, alors cette méthode ne peut déclarer que les exceptions ClasseExceptionI
ou les exceptions dérivées de ClasseExceptionI (interdiction de déclencher des exceptions dont les
classes ne sont pas liées à celles que peut déclencher la méthode outrepassée).
Bien sûr, ceci ne s'applique que pour les classes d'exceptions différentes de RuntimeException, Error et
toutes leurs classes dérivées.
Voici un exemple vous montrant ceci :

abstract class Classe1


{
abstract Class chercherClasse (String nomClasse);
}

class Classe2 extends Classe1


{
// chercherClasse () est outrepassée
Class chercherClasse (String nomClasse)
{
try
{
if (nomClasse.equals (""))
throw new IllegalArgumentException ("Nom de classe vide");
return Class.forName (nomClasse);
}
catch (ClassNotFoundException e)
{
// nomClasse pas trouvée : la méthode forName () de la classe
// Class nous impose d'intercepter cette exception...
throw new IllegalArgumentException ("Nom de classe inconnu");
}
// IllegalArgumentException est une classe dérivée de
// RuntimeException donc il n'est pas obligatoire d'intercepter
// les exceptions de cette classe
}

// Vous auriez pu décider de ne pas intercepter l'exception de


// de class ClassNotFoundException et de déclarer par exemple :
/* Class chercherClasse (String nomClasse) throws ClassNotFoundException
{
return Class.forName (nomClasse);
}
*/
// Ceci génère une erreur car il n'est pas possible d'outrepasser
// chercherClasse () et de déclarer que cette méthode est susceptible de
// déclencher des exceptions que chercherClasse () de Classe1 ne déclare pas...
}

Par contre, une méthode methode1 () outrepassant celle d'une super classe peut ne pas déclencher
certaines des exceptions que la méthode outrepassée a déclarées dans sa clause throws, comme par
exemple :

class Classe1
{
void methode1 () throws Exception
{
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 8

// ...
throw new Exception ();
}
}

class Classe2 extends Classe1


{
void methode1 ()
{
// ...
}
}

Ceci peut être utile quand vous voulez outrepasser la méthode clone () de la classe Object dans une
classe Classe1 pour permettre dans certains cas, de cloner les objets de la classe Classe1 sans avoir à
intercepter l'exception CloneNotSupportedException :

class Classe1 implements Cloneable


{
// ...

// La méthode clone () de la classe Object peut déclencher


// une exception CloneNotSupportedException, mais ici
// dans Classe1 clone () ne le fait pas
public Object clone ()
{
try
{
Classe1 clone = (Classe1)super.clone ();
// ...
return clone;
}
catch (CloneNotSupportedException e)
{
// Ne peut survenir car cette classe implémente Cloneable
// mais obligation d'intercepter l'exception car la méthode clone ()
// de la classe Object déclare qu'elle peut déclencher une exception
// de classe CloneNotSupportedException
return null;
}
}
}

class Classe2
{
void methode (Classe1 objet1)
{
Classe1 objet2 = (Classe1)objet1.clone ();
// ...
}
}

La classe Throwable

Les classes d'exceptions Java se divisent en plusieurs catégories. Elles héritent toutes de la classe
Throwable décrite ci-dessous. Celle-ci n'est pas habituellement utilisée directement, mais toutes les
exceptions héritent de ses méthodes, qui peuvent être intéressantes à utiliser ou à outrepasser.

Constructeurs

public Throwable ()
public Throwable (String message)

Allocation d'un nouvel objet Throwable, l'un sans message et l'autre avec message décrivant l'exception
survenue. Une trace de l'état de la pile est automatiquement sauvegardé.

Méthodes

public String getMessage ()


vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 9

Renvoie le message détaillé associé à l'objet.

public void printStackTrace ()


public void printStackTrace (PrintStream s)

Imprime sur la sortie standard ou sur un stream, l'exception et la trace de l'exception dans la pile.

public Throwable fillInStackTrace ()

Réinitialise la trace de la pile d'exécution. Cette méthode est utile uniquement quand vous voulez
redéclencher une exception traitée par un catch, de la manière throw exception.fillInStackTrace ().

public String toString ()

Méthode outrepassée de la classe Object, renvoyant une description sommaire de l'exception

Les exceptions Runtime

Les catégories des exceptions Runtime (classe RuntimeException et ses dérivées) et des classes d'erreurs
(classe Error et ses dérivées) sont spéciales : contrairement aux autres exceptions, un programme n'est
pas obligé de traiter les exceptions de ce type dans un try ... catch, et ceci essentiellement pour des
raisons pratiques de programmation. En effet, en consultant la liste suivante vous vous rendrez compte
que ces exceptions peuvent survenir très souvent dans un programme : Si Java obligeait à les traiter à
chaque fois, votre programme aurait beaucoup plus de traitement d'exceptions que de code réellement
utile.
De même, si vous voulez vous servir d'une de ces classes pour déclencher avec throw une exception
dans une méthode methode1 (), vous n'êtes pas obligé de la déclarer dans la clause throws de methode1
().
La classe RuntimeException dérive de la classe Exception. Voici la liste des exceptions dérivées de
RuntimeException, qui sont susceptibles d'être déclenchées au cours de l'exécution d'un programme Java
:

ArithmeticException : Une exception est survenue sur une opération arithmétique, comme une division d'un
entier par zéro.
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.
ClassCastException : Tentative de cast d'un objet dans un type incorrecte.
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 :
IllegalThreadStateException : Un thread était dans un état inadéquat pour l'opération requise.
NumberFormatException : Tentative de convertir dans un type numérique une chaîne de caractères mal
formattée.
IllegalMonitorStateException : Le thread courant a tenté d'attendre ou de prévenir d'autres threads, sur un
objet non verrouillé par ce thread (Exception déclenchée par les méthodes wait () et notify () de la classe
Object, voir aussi la synchronisation des threads).
IndexOutOfBoundsException : Un index (sur un tableau, une chaîne) ou un intervalle défini par deux index ont
dépassé les limites inférieures ou supérieures. Les classes suivantes dérivent de cette classe d'exception :
ArrayIndexOutOfBoundsException pour les tableaux.
StringIndexOutOfBoundsException pour les chaînes de caractères.
NegativeArraySizeException : Tentative de créer un tableau ou une chaîne avec une taille négative.
NullPointerException : Tentative d'utiliser une référence null alors qu'une référence sur un objet valide était
attendue.
SecurityException : Tentative de violation de sécurité.

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 Stack vide.
java.util.NoSuchElementException : Tentative d'accéder à un élément dans un ensemble d'éléments vide.
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 10

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. En fait, quand ce type d'exception est cité, ce n'est que pour information.

A la lecture de la liste précédente, vous pouvez voir que la Machine Virtuelle Java gère de manière
fine les erreurs courantes qui peuvent survenir dans un programme. Au cours de la mise au point d'un
programme, ce type d'erreur survient souvent : par défaut, l'exception déclenchée sera interceptée par
la Machine Virtuelle qui va inscrire à l'écran l'exception en question ainsi que l'état de la pile
d'exécution au moment de son déclenchement. Grâce à ces informations, vous retrouverez
généralement très rapidement d'où provient l'erreur sans avoir à lancer un debugger.
Globalement, vous verrez qu'à l'usage cette fonctionnalité vous permet de corriger beaucoup plus vite
vos programmes et que vous vous servirez beaucoup moins souvent du debugger que dans d'autres
langages.

Les classes d'erreurs

Les classes d'erreurs dérivent de la classe Error, qui dérive elle-même de la classe Throwable. Elles sont
généralement provoquée par la Machine Virtuelle Java à l'exécution, suite à un problème sur le
chargement ou l'utilisation des classes, ou sur la Machine Virtuelle elle-même. Elles peuvent être
intéressantes à analyser à la suite d'un plantage d'un programme. La liste de ces exceptions vous est
donnée sans plus de détail :

AWTError
LinkageError
ClassCircularityError
ClassFormatError
IncompatibleClassChangeError
AbstractMethodError
IllegalAccessError
InstantiationError
NoSuchFieldError
NoSuchMethodError
NoClassDefFoundError
UnsatisfiedLinkError
VerifyError
ThreadDeath
VirtualMachineError
InternalError
OutOfMemoryError
StackOverflowError
UnknownError

Les autres exceptions

Vous devez obligatoirement prendre en compte les exceptions dont la classe dérive de Exception (sauf
RuntimeException et ses classes dérivées), soit en les traitant dans un try ... catch, soit, grâce à la clause
throws, en les ajoutant à la liste des classes d'exception susceptibles d'être renvoyées par une méthode.
Exception dérive de la classe Throwable. Les classes d'exceptions qui suivent sont donc déclenchées par
certaines méthodes que vous trouverez dans la bibliothèque Java :

ClassNotFoundException : Une classe ou une interface d'un certain nom n'a pas été trouvée.
CloneNotSupportedException : La méthode clone () de la classe Object a été appelée sur un objet dont la
classe d'appartenance n'implémente pas l'interface Cloneable.
IllegalAccessException : Tentative de charger une classe dont le nom est correct, mais qui n'est pas public
et se trouve dans un package différent.
InstantiationException : La classe spécifiée en paramètre de newInstance () est abstract, un tableau ou
vendredi 11 février 2000 Du C/C++ à Java : Les exceptions Page: 11

une interface, ceci interdisant la création d'un nouvel objet.


InterruptedException : Le thread courant était en attente et un autre thread a interrompue son attente grâce la
méthode interrupt () de la classe Thread.

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 interrompue son attente grâce la méthode interrupt () de la classe Thread.
java.io.UTFDataFormatException : Erreur de conversion d'une chaîne au format UTF-8 (ou inversement).
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.

Le package java.awt défini l'exception suivante :


java.awt.AWTException

Pour le traitement d'erreur de vos programmes, vous pouvez déclencher vous-même des exceptions en
utilisant les classes d'exceptions existantes (comme par exemple IllegalArgumentException) ou des nouvelles
classes d'exceptions.
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 1

Les threads

Définition d'un thread


La création d'un thread
Les états d'un thread
La synchronisation des threads
La classe Thread

Définition d'un thread

L'environnement de la Machine Virtuelle Java est multi-threads. L'équivalent de thread pourrait être
tâche en français, mais pour éviter la confusion avec la notion de système multitâches, nous emploierons
le mot thread plutôt que tâche. Le fait que Java permettent de faire tourner en parallèle plusieurs threads
lui donne beaucoup d'intérêt : Ceci permet par exemple de lancer le chargement d'une image sur le Web
(ce qui peut prendre du temps), sans bloquer votre programme qui peut ainsi effectuer d'autres
opérations.
Pour vous montrer les possibilités du multi-threading voici une applet qui peut lancer trois horloges
tournant en parallèle (au passage, voici une démonstration de réutilisation de la classe TimeCounter déjà
utilisée au premier chapitre).

Ne confondez pas le multi-threads avec le traitement événementiel grâce à un timer, dont le résultat
rendrait la même chose. Dans cette applet, il y a réellement trois threads différents qui gèrent chacun
une horloge mise à jour à intervalle régulier. La mise à jour n'est pas déclenchée suite à un événement
du type WM_TIMER émis régulièrement par le gestionnaire de fenêtres.

Plusieurs aspects des threads sont à étudier pour bien comprendre leur fonctionnement et leur utilisation
: la gestion par la Machine Virtuelle Java pour répartir le temps d'exécution entre les différents threads,
la manière de créer un thread, les différents états possibles d'un thread, et la synchronisation entre
threads pour le partage de données.

L'environnement Java est multi-threads, et le langage permet d'utiliser cette fonctionnalité pour créer
vos propres threads, et synchroniser des threads qui partagent des données.

Le partage du temps entre threads

Comment faire tourner plusieurs threads en même temps alors que votre ordinateur ne possède qu'un
seul microprocesseur ? La réponse vient de manière assez évidente : A tout moment, il n'y a en fait qu'un
seul thread en cours d'exécution, et éventuellement d'autres threads en attente d'exécution.
Si le système permet de partager le temps d'exécution entre les différents threads, il leur donne chacun
leur tour un petit temps d'exécution du processeur (quelques millisecondes). Sinon, chaque fois qu'un
thread a terminé d'exécuter une série d'instructions et qu'il cède le contrôle au système, le système
exécute un des threads en attente, et ainsi de suite... Si la série d'instructions qu'exécute chaque thread
prend un temps assez court, l'utilisateur aura l'illusion que tous les threads fonctionnent ensemble.
Le seul contrôle que peut avoir le programmeur sur la gestion de l'ordre dans lequel les threads en
attente s'exécuteront, s'effectue en donnant une priorité à chaque thread qu'il crée. Quand le système
doit déterminer quel thread en attente doit être exécuté, il choisit celui avec la priorité la plus grande, ou
à priorité égale, celui en tête de file d'attente.
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 2

Sur certains systèmes, la Machine Virtuelle Java ne partage pas d'elle-même le temps du processeur
entre les threads susceptibles d'être exécutés. Si vous voulez que vos programmes Java se comportent
similairement sur tous les systèmes, vous devez céder le contrôle régulièrement dans vos threads avec
les méthodes telles que sleep () ou yield (), pour permettre aux autres threads en attente, de
s'exécuter. Sinon, tant que l'exécution d'un thread n'est pas interrompue, les autres threads resteront en
attente !

La création d'un thread

Il existe deux moyens d'écrire des threads dans un programme : Les deux moyens passent par l'écriture
d'une méthode run () décrivant les instructions que doit exécuter un thread. Cette méthode run () est
soit intégrée dans une classe dérivée de la classe Thread , soit intégrée dans n'importe quelle classe qui
doit alors implémenter l'interface Runnable (cette interface ne décrit qu'une méthode : run ()). La
seconde méthode est très utile : elle permet d'ajouter les fonctionnalités des threads à une classe
existante, dans une classe dérivée.
Par exemple, elle est utilisée par la classe TimeCounter qui dérive de la classe Counter et implémente
l'interface Runnable. La classe Counter a d'abord été créée pour des besoins simples : Avoir à disposition
un compteur digital au look sympa, avec des méthodes telles que incrementCounter () ou
decrementCounter (). Puis, le besoin s'est fait sentir d'avoir un compteur de temps : il a suffi donc de
créer une classe TimeCounter dérivée de Counter, et d'y ajouter (entre autres) la méthode run (), qui
incrémente toutes les secondes le compteur.
Une autre démarche aurait pu être de créer la classe TimeCounter en la dérivant de la classe Thread, et
d'y ajouter en utilisant la composition une variable de classe Counter, et une méthode run () effectuant
les mêmes opérations. Mais, en conception orientée objet, cette seconde démarche semblait moins
naturelle : En effet, un objet TimeCounter est plutôt comme tout objet Counter, un objet graphique
auquel il a été ajouté la possibilité de s'incrémenter automatiquement toutes les secondes.

Un certain nombre de méthodes sont nécessaires pour contrôler l'exécution d'un thread. Elles sont
toutes décrites au paragraphe décrivant la classe Thread, mais en voici les principales :

La classe Thread dispose principalement de deux sortes de constructeurs : Thread () et Thread


(Runnable objetRunnable).
Quand vous créer un objet instance d'une classe ClasseThread dérivée de Thread, le premier est
appelé implicitement par le constructeur de ClasseThread.
Le second est utilisé quand vous voulez créer un objet de classe Thread dont la méthode run () à
exécuter se trouve implémentée dans un classe implémentant l'interface Runnable. Au passage,
vous noterez que le paramètre du second constructeur doit être une référence d'interface Runnable
: En fait, vous passerez en argument une référence sur un objet d'une classe ClasseRunnable
implémentant Runnable ; ceci est un exemple de conversion d'une référence d'une classe ClasseX
dans une référence d'interface InterfaceY, si ClasseX implémente l'interface InterfaceY, appliquée
avec ClasseRunnable pour ClasseX et Runnable pour InterfaceY.
start () : Cette méthode permet de démarrer effectivement le thread sur lequel elle est invoquée,
ce qui va provoquer l'appel de la méthode run () du thread. Cet appel est obligatoire pour
démarrer l'exécution d'un thread. En effet, la création d'un objet de classe Thread ou d'une classe
dérivée de Thread (par exemple, grâce à l'appel new Thread ()) ne fait que créer un objet sans
appeler la méthode run ().
stop () : Cette méthode permet d'arrêter un thread en cours d'exécution. Elle est utile
principalement pour stopper des threads dont la méthode run () ne se termine jamais (comme par
exemple, dans la classe TimeCounter, la méthode run () n'a pas de raison de s'arrêter d'elle-même
puisqu'aux dernières nouvelles, on n'a pas encore trouver le moyen d'arrêter le temps !).
sleep () : Ces méthodes static permettent d'arrêter le thread courant pendant un certain laps de
temps, pour permettre ainsi à d'autres threads en attente, de s'exécuter. Par exemple, une fois mis à
jour une horloge par un thread, celui-ci peut arrêter son exécution pendant une minute, en
attendant la prochaine mise à l'heure.
yield () : Cette méthode static permet au thread courant de céder le contrôle pour permettre à
d'autres threads en attente, de s'exécuter. Le thread courant devient ainsi lui-même en attente, et
regagne la file d'attente. De manière générale, vos threads devraient s'arranger à effectuer des
séries d'instructions pas trop longues ou à entrecouper leur exécution grâce à des appels aux
méthodes sleep () ou yield ().
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 3

Il faut éviter de programmer des séries d'instructions interminables sans appel à sleep () ou yield
(), en pensant qu'il n'y aura pas d'autres threads dans votre programme. La Machine Virtuelle Java
peut avoir elle aussi des threads système en attente et votre programme s'enrichira peut-être un
jour de threads supplémentaires...
setPriority () : Permet de donner une priorité à un thread. Le thread de plus grande priorité sera
toujours exécuté avant tous ceux en attente.

Pour illustrer l'utilisation des threads, voici un exemple d'un chronomètre affichant les 1/10 de seconde
(Utiliser le bouton Arrêter pour l'arrêter et la redémarrer) :

et le programme Java correspondant (à copier dans un fichier dénommé Chrono.java et invoqué à partir
d'un fichier HTML) :

import java.awt.*;
import java.applet.Applet;

public class Chrono extends Applet implements Runnable


{
private Thread chronometre;
private int dixiemeseconde = 0;

// Méthode appelée par le système au démarrage de l'applet


public void start ()
{
// Au début de l'applet, création et démarrage du chronomètre
chronometre = new Thread (this);
chronometre.start ();
}

public void run ()


{
try
{
while (chronometre.isAlive ())
{
// Dessine le compteur (appel indirect à la méthode paint ()),
// puis l'augmente de 1
repaint ();
dixiemeseconde++;
// Arrête le thread pendant 1/10 s (100 ms)
Thread.sleep (100);
}
}
catch (InterruptedException e) { }
}

// Méthode appelée par le système à l'arrêt de l'applet


public void stop ()
{
// A la fin de l'applet, arrêt du chronometre
chronometre.stop ();
}

// Méthode appelée par le système pour mettre à jour le dessin de l'applet


public void paint (Graphics gc)
{
// Dessine le temps écoulé sous forme de hh:mm:ss:d en noir et helvetica
gc.setColor (Color.black);
gc.setFont (new Font ("Helvetica", Font.BOLD, size ().height));
gc.drawString (dixiemeseconde / 36000
+ ":" + (dixiemeseconde / 6000) % 6 + (dixiemeseconde / 600) % 10
+ ":" + (dixiemeseconde / 100) % 6 + (dixiemeseconde / 10) % 10
+ ":" + dixiemeseconde % 10,
2, size ().height - 2);
}
}

Ce programme s'utilisant sous forme d'applet, la classe Chrono dérive de Applet, et implémente
l'interface Runnable. Comme décrit au chapitre sur les applets et aux chapitres suivants, la méthode paint
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 4

() de la classe Applet est appelée pour mettre à jour le dessin apparaissant dans la fenêtre d'une applet :
Ici, elle est outrepassée pour dessiner le chronomètre.
Quand l'applet est créée, une instance de la classe Chrono est allouée et la méthode start () créant le
thread chronometre, est appelée. Si vous observez bien le comportement de cette applet, vous vous
rendrez facilement compte que le chronomètre à une tendance à retarder... Ceci est normal : En effet, à
chaque tour de boucle while (), le thread est arrêté pendant un dixième de seconde grâce à l'appel
Thread.sleep (100), après le redessin de l'applet avec la méthode repaint () dans run (). Mais, le fait
de redessiner le chronomètre prend un faible délai qui s'additionne au 1/10 de seconde d'arrêt du thread
chronometre. Une programmation plus précise devrait notamment tenir compte de ce délai pour le
soustraire de la valeur de 100 millisecondes passée à la méthode sleep (). La classe System déclare une
méthode currentTimeMillis (), donnant le temps courant, qui peut vous y aider. A vous de jouer !...

Les états d'un thread

Pendant son existence, un thread passe par plusieurs états qu'il est utile de connaître pour bien
comprendre les threads et leurs possibilités. La figure suivante représente l'ensemble des états possibles
d'un thread, et les transitions existantes pour passer d'un état dans l'autre. Ne vous inquiétez pas par sa
complexité, vous aurez besoin essentiellement des méthodes décrites dans le paragraphe précédent
(start (), stop (), yield () et sleep ()).

figure 10. Etats d'un thread

Quand un nouveau thread est créé, il est dans l'état nouveau thread et ne devient exécutable qu'après avoir
appelé la méthode start () sur ce nouveau thread.

Parmi tous les threads dans l'état exécutable , le système donne le contrôle au thread de plus grande
priorité, ou à priorité égale, celui en tête de file d'attente, parmi les threads dans l'état exécutable . Le
thread qui a le contrôle à un moment donné est le thread courant . Le thread courant en cours d'exécution
cède le contrôle à un autre thread exécutable dans l'une des circonstances suivantes :

A la fin de la méthode run (), le thread passe dans l'état mort .


A l'appel de yield (), le thread passe dans l'état exécutable et rejoint la fin de la file d'attente.
Sur les systèmes permettant de partager le temps d'exécution entre différents threads, le thread
passe dans l'état exécutable après qu'un certain laps de temps se soit écoulé.
En attentant que des opérations d'entrée/sortie (IO) se terminent, le thread passe dans l'état
bloqué .
A l'appel de sleep (), le thread passe dans l'état bloqué pendant le temps spécifié en argument,
puis repasse à l'état exécutable , une fois ce délai écoulé.
A l'appel d'une méthode synchronized sur un objet Objet1, si Objet1 est déjà verrouillé par un
autre thread, alors le thread passe dans l'état bloqué tant que Objet1 n'est pas déverrouillé (voir la
synchronisation).
A l'invocation de wait () sur un objet, le thread passe dans l'état en attente pendant le délai
spécifié en argument ou tant qu'un appel à notify () ou notifyAll () n'est pas survenu (voir la
synchronisation). Ces méthodes sont déclarées dans la classe Object.
A l'appel de stop (), le thread passe dans l'état mort .
A l'appel de suspend (), le thread passe dans l'état bloqué , et ne redevient exécutable qu'une fois
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 5

que la méthode resume () a été appelée.

Les deux derniers méthodes ne sont pas static et peuvent être invoquées aussi sur les threads
exécutables qui ne sont en cours d'exécution.

La synchronisation des threads

Tous les threads d'une même Machine Virtuelle partage le même espace mémoire, et peuvent donc avoir
accès à n'importe quelles méthode ou variable d'objets existants. Ceci est très pratique, mais dans certains
cas, vous pouvez avoir besoin d'éviter que deux threads n'aient accès n'importe quand à certaines
données. Si par exemple, un thread threadCalcul a pour charge de modifier une variable var1 qu'un
autre thread threadAffichage a besoin de lire pour l'afficher, il semble logique que tant que
threadCalcul n'a pas terminé la mise à jour ou le calcul de var1, threadAffichage soit interdit d'y avoir
accès. Vous aurez donc besoin de synchroniser vos threads.

Utilisation de synchronized

La synchronisation des threads se fait grâce au mot clé synchronized, employé principalement comme
modifieur d'une méthode. Soient une ou plusieurs méthodes methodeI () déclarées synchronized, dans
une classe Classe1 et un objet objet1 de classe Classe1 : Comme tout objet Java comporte un verrou
(lock en anglais) permettant d'empêcher que deux threads différents n'aient un accès simultané à un
même objet, quand l'une des méthodes methodeI () synchronized est invoquée sur objet1, deux cas se
présentent :

Soit objet1 n'est pas verrouillé : le système pose un verrou sur cet objet puis la méthode methodeI
() est exécutée normalement. Quand methodeI () est terminée, le système retire le verrou sur
Objet1.
La méthode methodeI () peut être récursive ou appeler d'autres méthodes synchronized de
Classe1 ; à chaque appel d'une méthode synchronized de Classe1, le système rajoute un verrou
sur Objet1, retiré en quittant la méthode. Quand un thread a obtenu accès à un objet verrouillé, le
système l'autorise à avoir accès à cet objet tant que l'objet a encore des verrous (réentrance des
méthodes synchronized).
Soit objet1 est déjà verrouillé : Si le thread courant n'est pas celui qui a verrouillé objet1, le
système met le thread courant dans l'état bloqué , tant que objet1 est verrouillé. Une fois que
objet1 est déverrouillé, le système remet ce thread dans l'état exécutable , pour qu'il puisse essayer
de verrouiller objet1 et exécuter methodeI ().

Si une méthode synchronized d'une classe Classe1 est aussi static, alors à l'appel de cette méthode, le
même mécanisme s'exécute mais cette fois-ci en utilisant le verrou associé à la classe Classe1.
Si Classe1 a d'autres méthodes qui ne sont pas synchronized, celles-ci peuvent toujours être appelées
n'importe quand, que objet1 soit verrouillé ou non.

Voici un exemple illustrant l'utilisation de méthodes synchronized, avec une applet affichant les résultats
d'un calcul de courbes, quand ceux-ci sont valables. Comme cette applet fait des calculs continuels dans
des boucles sans fin, elle n'est pas incluse dans cette page pour éviter de bloquer votre navigateur, mais
essayez-là sur votre machine pour découvrir tout l'intérêt de la synchronisation des threads.

Voici le programme Java (à copier dans un fichier dénommé AfficheurDeCalcul.java et invoqué à partir
d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class AfficheurDeCalcul extends Applet


{
private Thread calculateur;
private Thread afficheur;
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 6

// Méthode appelée par le système au démarrage de l'applet


public void start ()
{
setBackground (Color.white);
// Démarrage de deux threads l'un pour calculer une courbe,
// l'autre pour l'affichage
calculateur = new Calculateur (this);
afficheur = new Afficheur (this);
calculateur.start ();
afficheur.start ();
}

// Méthode appelée par le système à l'arrêt de l'applet


public void stop ()
{
// Arrêt des deux threads
afficheur.stop ();
calculateur.stop ();
}

private int [ ] courbe;

// calculerCourbe () et paint () sont


// synchronized ce qui les empêche de fonctionner simultanément
synchronized public void calculerCourbe ()
{
// Création d'un tableau de la largeur de l'applet
courbe = new int [size ().width];
// Calcul des points d'une sinusoïde avec fréquence aléatoire
double pasCalcul = 2 * Math.PI / courbe.length / Math.random ();
for (int i = 0; i < courbe.length; i++)
courbe [i] = (int)( (Math.sin (i * pasCalcul) + 1)
* size ().height / 2);
}

synchronized public void dessinerCourbe ()


{
update (getGraphics ()); // update () efface le fond puis appelle paint ()
}

// Méthode appelée par le système pour mettre à jour le dessin de l'applet


synchronized public void paint (Graphics gc)
{
if (courbe != null)
{
// Dessin de la courbe en noir en reliant les points un à un
gc.setColor (Color.black);
for (int i = 1; i < courbe.length; i++)
gc.drawLine (i - 1, courbe [i - 1], i, courbe [i]);
}
}
}

class Calculateur extends Thread


{
private AfficheurDeCalcul applet;

public Calculateur (AfficheurDeCalcul applet)


{
this.applet = applet;
}

public void run ()


{
while (isAlive ())
applet.calculerCourbe (); // Lance les calculs indéfiniment
}
}

class Afficheur extends Thread


{
private AfficheurDeCalcul applet;

public Afficheur (AfficheurDeCalcul applet)


{
this.applet = applet;
}
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 7

public void run ()


{
while (isAlive ())
applet.dessinerCourbe (); // Lance les affichages indéfiniment
}
}

Dans cette exemple, les méthodes calculerCourbe () et dessinerCourbe () de la classe


AfficheurDeCalcul sont synchronized. Quand calculerCourbe () préparent les résultats dans le tableau
courbe de l'applet, il ne faut pas qu'un autre thread puisse dessiner la courbe de cette applet en appelant
dessinerCourbe (), et inversement !
L'objet représentant l'applet est verrouillé à l'appel de ces méthodes, pour bloquer tout autre thread
tentant d'invoquer calculerCourbe () ou dessinerCourbe () sur cet objet. Les deux threads
calculateur et afficheur s'interdisent ainsi mutuellement d'exécuter simultanément calculerCourbe ()
ou dessinerCourbe () sur l'objet calculateur. Si, en recopiant l'exemple, vous essayez de supprimer
l'un ou les deux synchronized, vous verrez tout à coup que les courbes affichées ne sont plus très
régulières, preuve que la courbe est modifiée pendant son affichage !...
A l'usage, vous vous rendrez compte que les boucles sans fin (ou presque !) des méthodes run ()
fonctionnent généralement correctement mais ont une forte tendance à plomber les performances du
système, car elles tournent continuellement sans s'arrêter... Le programme se présente ainsi par soucis de
simplification et pour que vous puissiez vous rendre compte que deux threads qui s'ignorent (ils ne
s'appellent jamais l'un l'autre) peuvent se synchroniser pour accéder à un même objet pour obtenir un
résultat correct.

Le système ne crée pas de file d'attente pour les threads bloqués : Quand un objet est déverrouillé,
n'importe quel thread bloqué sur cet objet est susceptible d'être débloqué pour avoir accès à une de ses
méthodes synchronized. Dans l'exemple précédent, rien n'empêche en effet les méthodes
calculerCourbe () ou dessinerCourbe () de s'exécuter plusieurs fois de suite, avant que l'autre
méthode ne verrouille l'objet représentant l'applet et puisse s'exécuter. Pour vous le prouver, il vous
suffit d'ajouter des appels à System.out.println (...) dans ces deux méthodes...

Il existe une autre syntaxe d'utilisation de synchronized :

class Classe1
{
// ...
void methode1 ()
{
// ...
synchronized (objet1)
{
// objet1 est verrouillé
// jusqu'à la fin du bloc
}
}
}

Un thread peut accéder à un bloc synchronized, si objet1 n'est pas verrouillé par un autre thread. Si
c'est le cas, comme pour une méthode synchronized, le thread est bloqué tant que objet1 n'est pas
déverrouillé.

La Machine Virtuelle Java n'a pas de moyen de repérer les deadlocks (impasses) : Ceux-ci peuvent
survenir quand par exemple, deux threads thread1 et thread2 restent dans l'état bloqué parce que
thread1 attend qu'un objet objetX soit déverrouillé par thread2, alors que thread2 attend qu'un objetY
soit déverrouillé par thread1. C'est à vous de faire attention dans votre programmation pour qu'un
deadlock ne survienne pas.

Synchronisation avec wait () et notify ()

Comme il est expliqué dans l'exemple précédent, synchronized permet d'éviter que plusieurs threads
aient accès en même temps à même objet, mais ne garantit pas l'ordre dans lequel les threads ces
méthodes seront exécutées. Pour cela, il existe plusieurs méthodes de la classe Object qui permettent de
mettre en attente volontairement un thread sur un objet (avec les méthodes wait ()), et de prévenir des
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 8

threads en attente sur un objet que celui-ci est à jour (avec les méthodes notify () ou notifyAll ()).
Ces méthodes ne peuvent être invoquées que sur un objet verrouillé par le thread courant , c'est-à-dire
que le thread courant est en train d'exécuter une méthode ou un bloc synchronized, qui a verrouillé cet
objet. Si ce n'est pas le cas, une exception IllegalMonitorStateException est déclenchée.

Quand wait () est invoquée sur un objet objet1 (objet1 peut être this), le thread courant perd le
contrôle, est mis en attente et l'ensemble des verrous d'objet1 est retiré. Comme chaque objet Java
mémorise l'ensemble des threads mis en attente sur lui, le thread courant est ajouté à la liste des threads
en attente de objet1. objet1 étant déverrouillé, un des threads bloqués parmi ceux qui désiraient
verrouiller objet1, peut passer dans l'état exécutable et exécuter une méthode ou un bloc synchronized
sur objet1.

Un thread thread1 mis en attente est retiré de la liste d'attente de objet1, quand une des trois raisons
suivantes survient :

thread1 a été mis en attente en donnant en argument à wait () un délai qui a fini de s'écouler.
Le thread courant a invoqué notify () sur objet1, et thread1 a été choisi parmi tous les threads en
attente .
Le thread courant a invoqué notifyAll () sur objet1.

thread1 est mis alors dans l'état exécutable , et essaye de verrouiller objet1, pour continuer son
exécution. Quand il devient le thread courant , l'ensemble des verrous qui avait été enlevé d'objet1 à
l'appel de wait (), est remis sur objet1, pour que thread1 et objet1 se retrouvent dans le même état
qu'avant l'invocation de wait ().

Un thread mis en attente en utilisant la méthode wait () sans argument sur un objet, ne peut redevenir
exécutable qu'une fois qu'un autre thread a invoqué notify () ou notifyAll () sur ce même objet.
Donc, wait () doit toujours être utilisé avec notify (), et être invoqué avant cette dernière méthode.

Par exemple, si dans l'exemple précédent, vous ajoutez à la fin de la méthode calculerCourbe (), vous
ajoutez notifyAll () et au début de dessinerCourbe (), vous ajoutez wait (), vous obtiendrez ceci :

public class AfficheurDeCalcul extends Applet


{
// ...

synchronized public void calculerCourbe ()


{
courbe = new int [size ().width];
double pasCalcul = 2 * Math.PI / courbe.length / Math.random ();
for (int i = 0; i < courbe.length; i++)
courbe [i] = (int)( (Math.sin (i * pasCalcul) + 1)
* size ().height / 2);

// Prévenir les autres threads du calcul d'une nouvelle courbe


notifyAll ();
}

synchronized public void dessinerCourbe ()


{
try
{
// Attendre qu'une nouvelle courbe soit calculée
wait ();
update (getGraphics ());
}
catch (InterruptedException e) { }
}

// ...
}

A l'appel de wait () (ici sur l'objet this), le thread afficheur qui appelle la méthode dessinerCourbe ()
est mis en attente jusqu'à ce que ce que calculerCourbe () appelle notifyAll () pour prévenir les
threads en attente qu'une nouvelle courbe est maintenant disponible. Ceci évite que dessinerCourbe ()
soit éventuellement exécutée plusieurs fois de suite alors qu'aucune nouvelle courbe n'a été calculée.
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 9

Il vaut mieux utiliser notifyAll () que notify (), car il est possible d'enrichir ce programme en créant
par exemple des threads qui devront appeler dessinerCourbe () pour mettre à jour des fenêtres
supplémentaires, ou en créant un autre thread appelant une méthode synchronized qui enregistre la
courbe dans un fichier. Si la méthode notify () était appelée, un seul thread serait prévenu et mis à jour
en ignorant les autres.

Cette modification du programme n'empêche toujours pas la méthode calculerCourbe () de s'exécuter


plusieurs fois de suite, car le thread calculateur qui appelle cette méthode, prévient en invoquant
notifyAll () les threads en attente qu'une nouvelle courbe a été calculée, mais n'est jamais mis en
attente pour laisser les autres threads utiliser ces nouveaux résultats. Une dernière modification des
méthodes calculerCourbe () et dessinerCourbe () permet au thread afficheur appelant
dessinerCourbe () de prévenir le thread calculateur en attente qu'il a terminé de dessiner la nouvelle
courbe :

public class AfficheurDeCalcul extends Applet


{
// ...

synchronized public void calculerCourbe ()


{
try
{
// Attendre que les autres threads aient utilisé une courbe
if (courbe != null)
wait ();
}
catch (InterruptedException e) { }

courbe = new int [size ().width];


double pasCalcul = 2 * Math.PI / courbe.length / Math.random ();
for (int i = 0; i < courbe.length; i++)
courbe [i] = (int)( (Math.sin (i * pasCalcul) + 1)
* size ().height / 2);

notifyAll ();
}

synchronized public void dessinerCourbe ()


{
try
{
// Attendre qu'une courbe soit créée
if (courbe == null)
wait ();
update (getGraphics ());

// Prévenir que la courbe a été utilisée


courbe = null;
notify ();
}
catch (InterruptedException e) { }
}

// ...
}

Ici, en affectant la variable courbe à null ou avec un nouveau tableau, un thread peut savoir si l'autre à
terminer son traitement ou non. Si ce traitement n'est pas terminé, le thread se met en attente . Il
redeviendra exécutable quand l'autre thread le préviendra qu'il a fini en appelant notify ().
Vous noterez que dessinerCourbe () mettant à null la variable courbe avant d'appeler notify (), il
faudrait utiliser une autre logique de programmation pour que cette courbe reste disponible pour d'autres
threads qui seraient créés.

Cet exemple, utilisant deux boucles (sans fin) pour le calcul et l'affichage des courbes, entraîne une
programmation d'un abord assez complexe. Mais, ceci n'est utilisé que pour des moyens de
démonstration : Généralement, dans ce type de programme, un calcul est effectué ponctuellement sur
commande et pas en boucle, et l'affichage attend la fin du calcul pour démarrer. Donc, la programmation
avec uniquement synchronized comme dans la première version de cet exemple, suffira dans la plupart
des cas pour synchroniser l'accès aux données.
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 10

La programmation de la synchronisation des threads est une tâche ardue, sur laquelle vous passerez
sûrement du temps... Si vous désirez l'utiliser, il faut bien s'imaginer par quels états vont passer les
threads, quelle implication aura l'utilisation des méthodes wait () et notify () sur l'ordre d'exécution
des instructions du programme, tout en gardant bien à l'esprit que ces méthodes ne peuvent être
invoquées que sur des objets verrouillés.
Le piège le plus classique est de se retrouver avec un deadlock parce que les threads sont tous en
attente après avoir appelé chacun d'eux la méthode wait ().

La classe Thread

Variables

public final static int MIN_PRIORITY


public final static int NORM_PRIORITY
public final static int MAX_PRIORITY

Ces variables (qui sont en fait des constantes), sont utilisées en argument de la méthode setPriority (),
pour donner une priorité à vos threads (MIN_PRIORITY est égal à 1, NORM_PRIORITY à 5 et MAX_PRIORITY à
10). NORM_PRIORITY est la priorité par défaut d'un thread.

Constructeurs

public Thread ()

public Thread (Runnable target)

Construit un nouveau thread à partir de la classe target implémentant l'interface Runnable. target doit
implémenter la méthode run () qui sera la méthode exécutée au démarrage du nouveau thread créé.

public Thread (ThreadGroup group, Runnable target)


throws SecurityException, IllegalThreadStateException

Construit un nouveau thread à partir de la classe target avec comme groupe de thread group. La classe
ThreadGroup permet de regrouper un ensemble de threads, et d'exécuter (stop (), suspend (),...) des
méthodes sur tous les threads d'un groupe.

public Thread (String name)


public Thread (ThreadGroup group, String name)
throws SecurityException, IllegalThreadStateException
public Thread (Runnable target, String name)
public Thread (ThreadGroup group, Runnable target, String name)
throws SecurityException, IllegalThreadStateException

Mêmes constructeurs que précédemment avec un nom name.

Méthodes

La classe Thread compte un grand nombre de méthodes, dont voici la description des plus intéressantes.
Pour pouvoir manipuler le thread courant que vous ne connaissez pas forcément, certaines de ces
méthodes sont des méthodes de classe :

public static Thread currentThread ()

Renvoie une référence sur le thread actuellement en cours d'exécution.

public synchronized void start () throws IllegalThreadStateException

Provoque le démarrage du thread sur lequel start () est invoqué puis l'appel à la méthode run ().
Cette méthode rend la main immédiatement (le nouveau thread est lancé en parallèle au thread courant).

public void run ()

run () est la méthode où sont décrites les instructions que doit exécuter un thread. Elle est appelée une
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 11

fois que le thread a démarré. run () doit être outrepassée dans une classe dérivée de Thread ou une
classe implémentant l'interface Runnable.

public final synchronized void stop () throws SecurityException

Arrête le thread sur lequel stop () est invoqué.

public final void stop (Throwable exception)


throws SecurityException

Arrête le thread sur lequel stop () est invoqué en déclenchant l'exception exception.

public final boolean isAlive ()

Renvoie true si un thread est vivant, c'est-à-dire que ce thread a démarré avec start () et n'a pas été
arrêté soit avec stop (), soit parce qu'il a terminé d'exécuter toutes les instructions de la méthode run
().

public static void yield ()

Permet de suspendre le thread courant pour laisser la main à d'autres threads en attente d'exécution.

public static void sleep (long millis) throws InterruptedException


public static void sleep (long millis,
int nanos) throws InterruptedException

Provoque l'arrêt du thread courant pendant millis millisecondes, ou pendant millis millisecondes et
nanos nanosecondes. Ces méthodes sont susceptibles de déclencher une exception
InterruptedException, qui n'est pas utilisée dans Java 1.0, mais vous oblige quand même à inclure
l'appel à sleep () dans un try ... catch.

public final void suspend () throws SecurityException

Suspend l'exécution d'un thread vivant. Si plusieurs suspend () ont été invoquées sur un même thread,
un seul resume () est nécessaire pour que ce thread reprenne son activité.

public final void resume () throws SecurityException

Reprend l'exécution d'un thread, après une suspension avec suspend (). Si ce thread n'a pas été
suspendu, le thread poursuit son exécution.

public void checkAccess ()

Vérifie si le thread courant peut modifier le thread sur lequel checkAccess () est invoqué. Si cela lui est
interdit, une exception SecurityException est déclenchée.

public final void setPriority (int newPriority)


throws SecurityException, IllegalArgumentException
public final int getPriority ()

Modifie ou renvoie la priorité d'un thread. newPriority doit avoir une valeur comprise entre
MIN_PRIORITY et MAX_PRIORITY.

public final void setName (String name) throws SecurityException


public final String getName ()

Modifie ou renvoie le nom d'un thread.

public final void setDaemon (boolean on)


throws SecurityException, IllegalThreadStateException
public final boolean isDaemon ()

Permet de spécifier ou de savoir si un thread est un thread qui tourne en tâche de fond, pour rendre en
général des services aux autres threads (daemon thread en anglais). Quand il n'a plus que des threads qui
tournent en tâche de fond dans le système, la Machine Virtuelle Java s'arrête.
vendredi 11 février 2000 Du C/C++ à Java : Les threads Page: 12

public final synchronized void join (long millis) throws InterruptedException


public final synchronized void join (long millis, int nanos)
throws InterruptedException
public final void join () throws InterruptedException

Ces méthodes provoquent l'arrêt du thread courant jusqu'à la mort du thread sur lequel est invoqué join
(), et pendant un délai maximum de de millis millisecondes, ou millis millisecondes et nanos
nanosecondes.

public static void dumpStack ()

Imprime sur System.err l'état de la pile d'exécution du thread courant.

public final ThreadGroup getThreadGroup ()

Renvoie le groupe de threads auquel appartient un thread.

public static int activeCount ()


public static int enumerate (Thread tarray [ ])

Ces méthodes renvoient le nombre de threads actifs dans le groupe auquel appartient le thread courant,
et la liste des threads actifs de ce groupe dans le tableau tarray ( tarray doit exister et être de taille
supérieure ou égale à la valeur renvoyée par activeCount ()).

public void interrupt ()

Cette méthode et les deux suivantes ne sont implémentées qu'à partir de Java 1.1. Cette méthode permet
d'interrompre un thread. Si ce thread est en attente, il est exécuté et une exception
InterruptedException est déclenchée.

public static boolean interrupted ()


public boolean isInterrupted ()

public String toString ()

Renvoie une chaîne de caractère représentant un thread (comprenant son nom, sa priorité et le nom du
groupe de thread auquel il appartient). Cette méthode outrepasse celle de la classe Object.

public void destroy ()


public int countStackFrames () throws IllegalThreadStateException

Exemples

Application PaperBoardServer .
Applets Chrono , AfficheurDeCalcul , ObservateurCalcul , PaperBoardClient , AnimationFleche , ScrollText et
Horloge .
vendredi 11 février 2000 Du C/C++ à Java : Les classes internes Page: 1

Les classes internes

Les classes internes


Les classes anonymes
Autres nouveautés Java 1.1

Ce chapitre décrit les ajouts apportés dans le langage Java à partir de Java 1.1, et particulièrement les
classes internes (inner classes ).

Les classes internes

Syntaxe

Avec Java 1.0, il n'est possible de créer des classes public ou non qu'au niveau le plus haut dans un
fichier .java , c'est à dire des classes externes dépendant directement d'un package. Java 1.1 introduit la
possibilité de créer des classes internes ou interfaces internes qui sont déclarées à l'intérieur d'une classe
ou d'une interface, en plus des méthodes et des variables :

class ClasseExterne
{
// Déclararation d'une classe interne
ModifieurClasseInterne class ClasseInterne
{
// Corps de ClasseInterne :
// Déclaration des variables, des méthodes, des constructeurs,...
}

// Déclaration d'une classe interne dérivant d'une super classe


// et implémentant une interface
ModifieurClasseInterne class ClasseInterne2 extends nomDeSuperClasse
implements nomInterface //, nomInterface2, ...
{
// ...
}

// Déclararation d'une interface interne


ModifieurInterfaceInterne interface InterfaceInterne
{
}

// Autres déclarations
}

ModifieurClasseInterne est optionnel et peut être un ou plusieurs des mots-clés suivants :

public, protected ou private : Ces mots clés indiquent le contrôle d'accès de la classe interne et
ont le même sens que pour la déclaration des variables d'une classe.
final : Comme pour une classe externe, une classe interne déclarée final ne peut être dérivée.
abstract : Comme pour une classe externe, il est impossible de créer une instance d'une classe
interne déclarée abstract.
static : Les classes internes déclarées sont très différentes si elles sont déclarées static ou non.
Comme pour les variables static, une classe interne static ou ses instances ne dépend d'aucune
instance de la classe externe dans laquelle est elle est définie. Dans ce cas, il est possible de créer
une instance d'une classe interne simplement par new ClasseExterne.ClasseInterne () par
exemple.
L'instance d'une classe interne non static stocke automatiquement une référence vers l'instance de
la classe externe ClasseExterne dont elle dépend, ce qui permet d'utiliser directement toutes les
méthodes et les variables de la classe ClasseExterne. Cette référence doit être donnée à la création
d'un objet de cette classe interne comme par exemple objetClasseExterne.new ClasseInterne ()
pour créer un objet de classe ClasseInterne dépendant de l'objet objetClasseExterne de classe
vendredi 11 février 2000 Du C/C++ à Java : Les classes internes Page: 2

ClasseExterne.

ModifieurInterfaceInterne peut prendre toutes les valeurs que ModifieurClasseInterne , sauf final
(abstract est implicite).
Une classe interne non static ne peut pas déclarer des variables et des méthodes static.

De même dans un bloc, il est possible de déclarer des classes internes locales dont la portée est limitée
au bloc. Dans ce cas, ModifieurClasseInterne ne peut être prendre comme valeur que final ou
abstract.

Une classe interne peut déclarer elle-même d'autres classes internes.

Pour chacune des classes internes déclarées est généré un fichier .class à la compilation. Pour assurer
l'unicité du nom de ces fichiers, la syntaxe suivante est utilisée : La classe interne ClasseInterne
déclarée à l'intérieur d'une classe externe ClasseExterne, sera stockée dans le fichier
ClasseExterne$ClasseInterne.class .
Pour les classes internes déclarées dans un bloc, le nom de fichier comporte en plus un identifiant
numérique généré par le compilateur donnant comme nom de fichier par exemple
ClasseExterne$1$ClasseInterne.class .

Java 1.1 permet de déclarer des classes internes (inner classes ). Les classes internes static
correspondent aux classes internes du C++ (nested classes ). Par contre les classes internes non static
sont un concept inexistant en C++ et permettent aux instances de ces classes de garder implicitement
un lien avec l'instance de la classe externe dont elles dépendent.

Le mécanisme utilisé par le compilateur Java 1.1 pour générer les classes internes est entièrement
compatible avec la Machine Virtuelle Java 1.0.
Donc même si vous faites fonctionner vos applets avec Java 1.0, vous pouvez leur permettre quand
même d'utiliser les classes internes en les compilant avec un compilateur Java 1.1 en donnant comme
classpath la librairie des classes de Java 1.0.

Utilisation

Bien qu'il ne soit pas obligatoire de s'en servir, les classes internes apportent un plus pour l'organisation
et la programmation des classes de votre programme :

L'existence de certaines classes n'a de sens que dans le contexte d'une autre classe. Dans ce cas, il peut être
intéressant de les déclarer comme classes internes pour montrer aux utilisateurs de ces classes dans quel
contexte elles s'utilisent.
Les classes externes peuvent avoir un contrôle d'accès public ou friendly . Toutes les classes non public d'un
package donné sont accessibles à toutes les autres classes de ce package, ce qui n'a pas toujours l'effet désiré.
Une classe interne dont le contrôle d'accès est private n'est accessible que par la classe externe dans laquelle
elle est déclarée.
Le nommage des classes est simplifié : certaines classes utilitaires de même genre peuvent avoir à être
déclarées dans des contextes différents. Si ces classes sont déclarées comme classes internes, elles pourront
porter le même nom sans interférer entre elles, et vous n'aurez pas à inventer des noms à rallonge pour les
différencier.
Comme une classe interne peut être déclarée n'importe où dans une classe, il est permis de la rapprocher de
l'endroit où elle est le plus utilisée, pour améliorer la lisibilité du programme.
La possibilité d'utiliser directement toutes les variables et les méthodes de la classe externe dans une classe
interne non static simplifie dans de nombreux cas la programmation, comme dans le cas suivant repris de
l'applet AfficheurDeCalcul : Cette applet déclare les deux classes non public Calculateur et Afficheur
dérivant de la classe Thread. Ces deux classes ont besoin d'une référence sur l'instance de l'applet et
l'utilisation des classes internes permet d'en simplifier la programmation car elles gardent cette référence
automatiquement :
vendredi 11 février 2000 Du C/C++ à Java : Les classes internes Page: 3

Sans classe interne Avec classe interne


import java.applet.Applet; import java.applet.Applet;
import java.awt.*; import java.awt.*;

public class AfficheurDeCalcul extends Applet public class AfficheurDeCalcul extends Applet
{ {
private Thread calculateur; private Thread calculateur;
private Thread afficheur; private Thread afficheur;

public void start () public void start ()


{ {
setBackground (Color.white); setBackground (Color.white);
// Création de deux instances des classes // Création de deux instances des classes
// Calculateur et Afficheur // internes Calculateur et Afficheur
calculateur = new Calculateur (this); calculateur = new Calculateur ();
afficheur = new Afficheur (this); afficheur = new Afficheur ();

calculateur.start (); calculateur.start ();


afficheur.start (); afficheur.start ();
} }

// ... // ...
// Les méthodes stop (), calculerCourbe () // Les méthodes stop (), calculerCourbe ()
// dessinerCourbe () et paint () // dessinerCourbe () et paint ()
// sont inchangées // sont inchangées

} // Fin de AfficheurDeCalcul

// Classe friendly dérivant de Thread // Classe interne dérivant de Thread


class Calculateur extends Thread private class Calculateur extends Thread
{ {
private AfficheurDeCalcul applet; public void run ()
{
public Calculateur (AfficheurDeCalcul applet) while (isAlive ())
{ // calculerCourbe (), méthode de
this.applet = applet; // la classe externe AfficheurDeCalcul
} // peut être appelée directement
calculerCourbe ();
public void run () }
{ }
while (isAlive ())
applet.calculerCourbe ();
}
}

// Classe friendly dérivant de Thread // Classe interne dérivant de Thread


class Afficheur extends Thread private class Afficheur extends Thread
{ {
private AfficheurDeCalcul applet; public void run ()
{
public Afficheur (AfficheurDeCalcul applet) while (isAlive ())
{ // dessinerCourbe (), méthode de
this.applet = applet; // la classe externe AfficheurDeCalcul
} // peut être appelée directement
dessinerCourbe ();
public void run () }
{ }
while (isAlive ())
applet.dessinerCourbe ();
}
}
} // Fin de AfficheurDeCalcul

Les classes anonymes

Par extension des classes internes locales, vous pouvez déclarer aussi des classes "anonymes" en Java.
C'est un ajout à la syntaxe de l'opérateur new : Après l'instruction new Classe1 (), il est possible
d'ajouter un bloc permettant de modifier le comportement de Classe1, en outrepassant telle ou telle
méthode de Classe1.
Résultat : un objet d'une classe "anonyme" dérivée de Classe1 est créé, puis un cast implicite de cette
classe vers Classe1 est effectué.
vendredi 11 février 2000 Du C/C++ à Java : Les classes internes Page: 4

SuperClasse objet = new SuperClasse (/* argument1, argument2, ...*/)


{
// Méthodes de SuperClasse outrepassées
// pour modifier le comportement de SuperClasse
};

Dans la même logique, il est possible de créer une instance d'une classe anonyme implémentant une
interface InterfaceX, grâce à l'instruction :

InterfaceX objet2 = new InterfaceX ()


{
// Implémentation de toutes les méthodes de InterfaceX
};

Dans ce cas, le bloc qui suit new InterfaceX () doit implémenter toutes les méthodes de InterfaceX
pour qu'il soit possible de créer une instance d'une telle classe.
Comme toute classe interne, une classe anonyme peut déclarer un ensemble de variables et de méthodes
d'instances.

Pour chacune des classes anonymes déclarées est généré un fichier .class à la compilation. Pour assurer
l'unicité du nom de ces fichiers, le nom de chaque fichier est constitué du nom de la classe externe suivi
du symbole $ et d'un identifiant numérique généré par le compilateur, comme par exemple
ClasseExterne$1.class .

Bien que les classes anonymes peuvent en apparence obscurcir la lisibilité d'un programme, il existe un
ensemble de circonstances où il est intéressant de les utiliser :

Pour créer une instance d'un objet d'une classe dont on veut outrepasser juste une ou deux
méthodes.
Implémenter localement une interface telle qu'un listener . Ce type d'interface est utilisé dans la
gestion de l'Interface Utilisateur AWT à partir de Java 1.1 pour déclarer les méthodes qu'une classe
doit implémenter pour être rappelées quand un événement survient.

Par exemple, il est possible d'encore simplifier l'applet AfficheurDeCalcul , en remplaçant les classes
internes Calculateur et Afficheur par des classes anonymes :

import java.applet.Applet;
import java.awt.*;

public class AfficheurDeCalcul extends Applet


{
private Thread calculateur;
private Thread afficheur;

public void start ()


{
setBackground (Color.white);
// Création de deux instances de classes
// anonymes implémentant la méthode run ()
// de la classe Thread
calculateur = new Thread ()
{
public void run ()
{
while (isAlive ())
// calculerCourbe (), méthode de
// la classe externe AfficheurDeCalcul
// peut être appelée directement
calculerCourbe ();
}
};
afficheur = new Thread ()
{
public void run ()
{
while (isAlive ())
// dessinerCourbe (), méthode de
// la classe externe AfficheurDeCalcul
vendredi 11 février 2000 Du C/C++ à Java : Les classes internes Page: 5

// peut être appelée directement


dessinerCourbe ();
}
};

calculateur.start ();
afficheur.start ();
}

// ...
// Les méthodes stop (), calculerCourbe ()
// dessinerCourbe () et paint ()
// sont inchangées
}

Les classes anonymes permettent de transformer facilement un programme existant pour exécuter un
bloc d'instructions dans un thread isolé en ajoutant sur place les quelques instructions suivantes :

Avant Après
// ... // ...
new Thread ()
{
public void run ()
{ {
// Bloc d'instructions // Bloc d'instructions
} }
}.start ();

Pour éviter toute confusion avec le reste des instructions, utilisez des règles d'écriture et une
indentation claires pour l'écriture des classes anonymes.

Autres nouveautés Java 1.1

Initialisations d'instance

En plus des initialisations static, il est possible d'ajouter à une classe des blocs d'initialisations
d'instance. Ces blocs d'instructions sont exécutés à la création d'un objet juste après le constructeur de
sa super classe et avant tout constructeur de sa classe.
Ces initialisations sont surtout utiles pour les classes anonymes qui ne peuvent pas déclarer de
constructeurs.

Sauf pour les classes anonymes, les blocs d'initialisations d'instance d'une classe Classe1 ne peuvent
déclencher d'exceptions que si tous les constructeurs de Classe1 déclarent qu'ils sont susceptibles de
déclencher ces classe d'exceptions avec la clause throws.

Initialisation de tableaux

Les tableaux peuvent être initialisés à leur création en faisant suivre l'instruction de création du tableau
new Classe0 [], par la liste des éléments à stocker, comme dans l'exemple suivant :

class Classe1
{
// Les deux instructions suivantes sont équivalentes
int [ ] tab1 = {1, 2};
int [ ] tab2 = new int [] {1, 2};

void methode1 (String [] tab)


{
}

void methode2 ()
vendredi 11 février 2000 Du C/C++ à Java : Les classes internes Page: 6

{
// Possibilité de créer des tableaux, envoyés
// directement en paramètre à une méthode
methode1 (new String [] {"valeur1", "valeur2});
}
}

Comme vous pouvez le voir, c'est surtout pratique pour envoyer un tableau en paramètre sans avoir à le
déclarer dans une instruction séparée.

Utilisation du mot-clé class

Toute classe ou interface peut être suivie du mot-clé class : ceci produit le même effet que l'utilisation
de la méthode forName () de la classe Class.
L'instruction String.class équivalente à Class.forName ("java.lang.String") est bien plus pratique à
utiliser car vous n'êtes pas obligé de donner le package complet de la classe String et d'intercepter
l'exception ClassNotFoundException que peut déclencher la méthode forName ().

Cette nouvelle syntaxe peut être aussi utilisée pour tous les types de base et void, de la manière suivante
:

byte.class
short.class
int.class
long.class
float.class
double.class
char.class
boolean.class

void.class

Ceci est utilisé en particulier par les classes du package java.lang.reflect pour manipuler tous les
types Java (que ce soient des classes ou des types de base) sous forme d'un objet de classe Class.

Variables locales et paramètres final

Les variables locales et les paramètres d'une méthode ou d'un constructeur peuvent être déclarés final.

TypeRetour methode1 (final TypeParam1 param1Name /*,... */)


{
final TypeVariable variableLocale1 = valeur ;
// ...
}

Il n'est pas obligatoire d'initialiser une variable locale final dès sa déclaration, mais par contre il n'est
possible de lui assigner qu'une seule fois une valeur.
Tout paramètre ou toute variable locale que vous voulez utiliser dans une classe anonyme doivent être
déclarés final. Ceci permet à cette classe anonyme d'utiliser ces variables temporaires sans risque
qu'elles soient modifiées ultérieurement.
Par contre, toutes les variables d'instance ou de classe existent de façon permanente et peuvent être
utilisées dans une classe anonyme qu'elles soient final ou non.

Comme avec const en C/C++, les paramètres d'une méthode peuvent être déclarés constants grâce à
final en Java. Mais ceci interdit uniquement à une méthode de modifier la valeur d'un paramètre. Si un
paramètre param1 final est une référence sur un objet, il impossible de modifier param1 mais l'objet
désigné par param1 lui peut être modifié.
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 1

Conventions d'écriture et portage

Conventions d'écriture
Portage de programmes écrits en C/C++

Ce chapitre vous indique les conventions généralement utilisées dans l'écriture des programmes et des
suggestions pour vous aider à récupérer des programmes écrits en C ou C++.
Ces suggestions et les exemples qui sont donnés peuvent vous servir aussi comme complément
d'information avant de passer à l'étude de la bibliothèque Java.

Conventions d'écriture

Par convention, l'écriture de vos classes devrait respecter les conventions suivantes :

Si un fichier .java définit une classe ou une interface public, cette classe et le nom du fichier
doivent avoir le même nom.
Le nom des classes commence par une majuscule, et représente une suite d'un ou plusieurs mots
en minuscules, avec chaque début de mot en majuscule (par exemple MaClasse).
Le nom des interfaces commence par une majuscule, et utilise généralement un adjectif qualificatif
(se terminant souvent par able).
Le nom des méthodes commence par une minuscule, et représente une action :
Les méthodes utilisées pour interroger ou modifier la valeur d'une variable var, doivent
s'appeler getVar () et setVar () respectivement, ou éventuellement isVar () et setVar ()
si var est du type boolean. Cette convention n'est utile que si vous voulez utiliser facilement
une classe comme JavaBean.
Le nom des méthodes renvoyant une longueur se dénomme length ().
Le nom des méthodes convertissant un objet en autre chose, commence par to (par exemple
toString ()).
Le nom des variables non final commence par une minuscule. Ce type de variable est rarement
déclaré public ou protected ; il vaut mieux utiliser des variables private accessibles par des
méthodes comme expliqué précédemment.
Le nom des variables final (constantes) représente une suite d'un ou plusieurs mots en
majuscules, séparés par le caractère _ (par exemple MAX_PRIORITY).
Le nom des packages représentant des sous-répertoires est en minuscules, et java est un nom de
package réservé pour les classes standards Java.

Comme vous pouvez le voir, ces conventions ne laissent que peu de choix sur la langue à utiliser dans
un programme Java. Mais, comme toute convention, vous pouvez l'ignorer et créer votre propre
convention d'écriture.

Documentation des fichiers .java

Java définit des règles de syntaxe pour l'écriture des commentaires destinés à des fins de documentation.
Si vous les respectez, vous pourrez les extraire pour fabriquer un fichier HTML, en utilisant la
commande javadoc .

Portage de programmes écrits en C/C++

Vous avez développé un certains nombres de routines en C ou de classes en C++, et vous aimeriez les
porter en Java... Les remarques signalées par les symboles et des chapitres précédents et les
suggestions qui suivent devraient vous guider pour mener à bien cette tâche qui peut prendre un certain
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 2

temps suivant les fonctionnalités du C/C++ que vous avez utilisé.

Les suggestions décrites ici ne concernent que les routines C/C++ utilisant la bibliothèque standard
du C (stdio.h , stdlib.h ,...) et pas les applications utilisant les routines de l'Interface Graphique de tel ou
tel système (Windows, XWindow/Motif,...).

Elles ne représentent pas forcément la solution idéale à tel ou tel problème, mais plutôt une solution
pragmatique et rapidement réalisable.
Rien ne vous empêche de revoir complètement l'architecture de vos routines pour qu'elles respectent
les principes de la programmation orientée objets à la lettre.

Conception des classes

Tout d'abord, essayez d'éventuellement modifier et de rassembler vos routines pour qu'elles puissent
former des classes cohérentes.
Si la conception de vos routines n'utilise pas de principes de la programmation orientée objets comme
l'encapsulation, n'ayez pas de scrupules à transposer toutes vos variables globales et vos fonctions en
variables et méthodes de classe static. Une fois votre première version de portage terminée, vous
pourrez éventuellement utiliser plus amplement la programmation objet.
N'oubliez d'ajouter le nom de la classe et l'opérateur point (.) devant les variables et les méthodes
static d'une classe, quand vous voulez y accéder en dehors de cette classe (comme par exemple
System.arraycopy ()).

Java n'utilisant pas de fichiers header .h , il faut rassembler les déclarations de constantes et de types
avec les fonctions dans des classes déclarées dans un ou plusieurs fichiers .java .

Remplacement des définitions de type typedef

typedef peut être utilisé en C de deux manières différentes :

Pour donner un nom aux types structurés struct, aux unions union et éventuellement aux types pointeurs sur
ces types. Tous les types structurés sont transformés en classes.
Pour renommer un type de base C : Dans ce cas, soit vous créez une classe ne contenant qu'une variable de
ce type, soit vous remplacez le nom de ce type par le type de base Java correspondant partout où il est utilisé.

Remplacement des instructions de précompilation #define

#define s'utilise de deux manières différentes en C, l'une pour déclarer des constantes, l'autre pour créer des
macros :
Les macros doivent être transformées en méthodes : ceci oblige à typer les paramètres d'une macro, mais grâce
à la surcharge des méthodes, vous pouvez créer plusieurs méthodes de même nom mais avec des paramètres
de type différent.
Les constantes doivent être remplacées par des variables static final et ajoutées soit aux classes auxquelles
elles se rapportent, soit éventuellement à une ou plusieurs interfaces implémentées dans les classes qui ont
besoin d'y avoir accès.

Remplacement des instructions de précompilation #ifdef, #else, #endif

#ifdef s'utilise en C pour indiquer au compilateur qu'il doit compiler ou non une partie du code selon qu'un
symbole est défini ou non. Il est très souvent utilisé pour ajouter des instructions spéciales de suivi lorsqu'un
programme est en phase de mise au point (ou de debugging ).
L'utilisation de constantes Java (variables static final) permettent de réaliser un effet comparable comme
dans l'exemple suivant :
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 3

class Classe1
// Définit un symbole DEBUG {
#define DEBUG static final int DEBUG = true;

void methode () void methode ()


{ {
//... //...
#ifdef DEBUG if (DEBUG)
printf ("OK"); System.out.println ("OK");
#endif }
} }

Un des avantages de l' instruction #ifdef est notamment qu'il permet d'alléger le code compilé (le fichier .obj )
des instructions qui sont définis dans le bloc #ifdef si la condition est fausse. Le compilateur Java fait de
même : c'est-à-dire que dans l'exemple précédent si vous définissez DEBUG comme étant égal à false,
l'instruction System.out.println ("OK"); ne sera pas dans le fichier compilé Classe1.class. Comme la
condition if (DEBUG) est forcément fausse, le compilateur utilise dès le départ une optimisation qui exclut
l'instruction qui suit.

Remplacement des énumérations enum

Les énumérations enum définissent un ensemble cohérent de constantes dont la valeur est entière. Vous
pouvez les remplacer soit par des classes déclarant un ensemble de constantes public, soit par des interfaces
implémentées dans les classes qui en ont besoin.

Remplacement des unions union

En C, les variables d'un type union permettent de typer de manière différente leur zone mémoire. Dans
l'exemple :

union
{
double x;
int a;
}
varUnion;

varUnion a pour taille, la taille du plus grand de ses éléments, ici la taille d'un double (8 octets), et mémorise
soit un double par varUnion.x ou soit un int par varUnion.a.

Le portage le plus rapide consiste à remplacer les variables C de type union par une référence de classe
Object. Une variable de classe Object permet de désigner n'importe quel objet Java, car toutes les classes
héritent de cette classe. De plus, l'opérateur instanceof permet de tester qu'elle est la classe de l'objet
mémorisé par une telle référence. Si comme dans l'exemple précédent l'union C utilise des types de base,
utilisez éventuellement à la place les classe d'emballages Java (Boolean, Character, Integer, Long, Float,
Double).

Voici un programme C et un programme Java mis en parallèle pour illustrer une manière de transformer une
union de cette manière. Ces programmes permettent d'évaluer une expression comportant des nombres double
et les opérateurs +, -, * et /, mémorisée sous forme d'un arbre :

#include <stdio.h> // Fichier TestExpression.java


#include <stdlib.h> // A exécuter par la commande
// java TestExpression
struct _Expression;
/* Structure Operation mémorisant */ // Classe Operation mémorisant
/* une expression avec opérateur binaire */ // une expression avec opérateur binaire
typedef struct class Operation
{ {
char operateur; char operateur;
struct _Expression *expr1, Object expr1,
*expr2; expr2;
} }
Operation;
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 4

#define TYPE_VALEUR 0
#define TYPE_OPERATION 1

/* Structure _Expression mémorisant */ // Pas besoin d'une classe Expression :


/* une union entre un double et une */ // une expression est mémorisée par une
/* valeur de type Operation */ // référence de classe Object
/* et un champ type permettant de */ // contenant soit un objet de classe
/* connaître le type dans l'union */ // Double, soit une objet de classe
typedef struct _Expression // Operation
{
int type;
union
{
double valeur;
Operation operation;
}
expr;
} // Classe TestExpression
*Expression; public class TestExpression
{
double calculerOperation (Operation oper);
double calculerExpression static double calculerExpression
(Expression expr) (Object expr)
{ {
/* Calcul du résultat suivant le type */ // Calcul du résultat suivant la classe
/* d'expression mémorisée dans l'union */ // de l'objet expr
switch (expr->type)
{
case TYPE_VALEUR : if (expr instanceof Double)
return expr->expr.valeur; return ((Double)expr).doubleValue ();
case TYPE_OPERATION : if (expr instanceof Operation)
return calculerOperation return calculerOperation
(expr->expr.operation); ((Operation)expr);
}
return 0; throw new IllegalArgumentException ();
} }

/* Calcul d'une opération binaire */ // Calcul d'une opération binaire


double calculerOperation (Operation oper) static double calculerOperation
(Operation oper)
{ {
/* Evaluation des deux opérandes */ // Evaluation des deux opérandes
double val1 = calculerExpression double val1 = calculerExpression
(oper.expr1); (oper.expr1);
double val2 = calculerExpression double val2 = calculerExpression
(oper.expr2); (oper.expr2);
switch (oper.operateur) switch (oper.operateur)
{ {
case '+' : return val1 + val2; case '+' : return val1 + val2;
case '-' : return val1 - val2; case '-' : return val1 - val2;
case '*' : return val1 * val2; case '*' : return val1 * val2;
case '/' : return val1 / val2; case '/' : return val1 / val2;
} }
return 0; throw new IllegalArgumentException ();
} }

void main () public static void main (String [] arg)


{ {
/* Allocation de 5 expressions */
Expression expr = malloc
(5 * sizeof (struct _Expression));

/* Construction de l'arbre représentant */ // Construction de l'arbre représentant


/* l'expression 2 + 3 / 4 */ // l'expression 2 + 3 / 4
expr [0].type = Operation expr1 = new Operation ();
expr [1].type = TYPE_OPERATION; expr1.operateur = '/';
expr [0].expr.operation.operateur = '+'; expr1.expr1 = new Double (3);
expr [0].expr.operation.expr1 = &expr [2]; expr1.expr2 = new Double (4);
expr [0].expr.operation.expr2 = &expr [1];
expr [1].expr.operation.operateur = '/'; Operation expr0 = new Operation ();
expr [1].expr.operation.expr1 = &expr [3]; expr0.operateur = '+';
expr [1].expr.operation.expr2 = &expr [4]; expr0.expr1 = new Double (2);
expr0.expr2 = expr1;
expr [2].type =
expr [3].type =
expr [4].type = TYPE_VALEUR;
expr [2].expr.valeur = 2;
expr [3].expr.valeur = 3;
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 5

expr [4].expr.valeur = 4;

printf ("Resultat de 2 + 3/4 = %g", System.out.println


calculerExpression (&expr [0])); ( "Resultat de 2 + 3/4 = "
+ calculerExpression (expr0));
} }
}

Enfin, voici une version plus orientée objet du programme TestExpression.java :

// Déclaration de l'interface Calculable


// (Aurait pu être aussi une classe abstract)
interface Calculable
{
double calculer ();
}

// Classe Nombre mémorisant un double


// Le résultat de calculer () est ce nombre
class Nombre implements Calculable
{
private double nombre;

// Constructeur
Nombre (double nombre)
{
this.nombre = nombre;
}

// Implémentation de la méthode calculer ()


public double calculer ()
{
return nombre;
}
}

// Classe Operation mémorisant une expression avec opérateur binaire


// Le résultat de calculer () est l'évaluation de cette expression
class Operation implements Calculable
{
private char operateur;
private Calculable expr1,
expr2;

// Constructeur
Operation (char operateur, Calculable expr1, Calculable expr2)
{
this.operateur = operateur;
this.expr1 = expr1;
this.expr2 = expr2;
}

// Implémentation de la méthode calculer ()


public double calculer ()
{
// Evaluation des deux opérandes
double val1 = expr1.calculer ();
double val2 = expr2.calculer ();
switch (operateur)
{
case '+' : return val1 + val2;
case '-' : return val1 - val2;
case '*' : return val1 * val2;
case '/' : return val1 / val2;
}
throw new IllegalArgumentException ("Operateur inconnu");
}
}

// Classe de test TestExpression


public class TestExpression
{
public static void main (String [] arg)
{
// Création de l'expression 2 + 3 / 4
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 6

Calculable expr =
new Operation ('+', new Nombre (2),
new Operation ('/', new Nombre (3),
new Nombre (4)));
System.out.println
("Resultat de 2 + 3/4 = " + expr.calculer ());
}
}

Passage des valeurs par adresse

Comme il est signalé au chapitre sur la création des classes, tous les paramètres des fonctions sont passés par
valeur en Java. Si certaines de vos fonctions requièrent de leur passer des paramètres d'un type de base par
adresse, comme dans l'exemple suivant :

void fonction1 (int *param1);


int fonction2 (double *param1);

vous devrez transformer vos fonctions d'une des trois manières suivantes :
Pour une fonction void du style de fonction1 (), vous pouvez la transformer en int fonction1 (int
param1), et renvoyer dans fonction1 () la valeur modifiée de param1. L'inconvénient de cette méthode
est qu'elle vous oblige à transformer tous les appels à fonction1 () pour en récupérer la valeur de
retour.
Pour une fonction du style fonction2 (), vous pouvez créer une classe DoubleValue mémorisant une
variable value de type double et passer une instance de DoubleValue à fonction2 (). Ainsi, si vous
modifiez la valeur de la variable value dans fonction2 (), l'appelant de fonction2 () aura accès à cette
variable modifiée. Notez que cette méthode est applicable aussi à fonction1 ().
Dans les deux cas, la manière la plus rapide mais la moins orientée objet consiste à créer un tableau
contenant un seul élément. Comme pour tout objet Java, si vous modifiez l'élément que contient le
tableau dans la fonction appelée, l'appelant aura accès à cet elément modifié.

Voici un programme C et un programme Java mis en parallèle pour illustrer ces trois types de transformation :
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 7

class VotreClasse
{
void fonction1 (int *param1) int fonction1 (int param1)
{ {
*param1 = 1; param1 = 1;
/* ... */ // ...
} return param1;
}

int fonction2 (double *param1) int fonction2 (DoubleValue param1)


{ {
*param1 = 1; param1.value = 1;
/* ... */ // ...
return 0; return 0;
} }

void fonction3 (float *param1) int fonction3 (float [] param1)


{ {
*param1 = 1; param1 [0] = 1;
/* ... */ // ...
return 0; return 0;
} }

void fonctionAppelante () void fonctionAppelante ()


{ {
int argInt = 0; int argInt = 0;
double argDouble = 0; DoubleValue argDouble =
float argFloat = 0; new DoubleValue (0);
int valRetour;
int valRetour; float [] argFloat = {0};

fonction1 (&argInt); argInt = fonction1 (argInt);


valRetour = fonction2 (&argDouble); valRetour = fonction2 (argDouble);
fonction3 (&argFloat); fonction3 (argFloat);

/* ... */ // ...
} }
}

// Classe d'emballage du type double


// avec variable d'accès public
class DoubleValue
{
public double value;

public DoubleValue (double value)


{
this.value = value;
}
}

Les classes d'emballage des types de base Boolean, Character, Integer, Long, Float, Double ne
permettent pas de modifier la valeur de type de base qu'elle mémorise, donc elles ne peuvent pas être
utilisées dans ce cas.
Si vous utilisez très souvent le passage de valeurs par adresse, il est conseillé de créer des le départ
vos propres classes d'emballage mémorisant une variable public de type de base.

Allocation dynamique

Java ne permet de créer dynamiquement que des instances de classes, grâce à l'opérateur new. Tous les appels
au fonctions malloc () et calloc () doivent être remplacés par new Classe1 () pour créer une instance de
Classe1 et par new Classe1 [nbreElements] pour créer un tableau mémorisant nbreElements références de
classe Classe1 (il est rappelé que vous devez individuellement créer ou affecter chacun des nbreElements
références du tableau).
Java n'a pas d'équivalent de la fonction realloc () et ne permet pas d'agrandir un tableau existant : il faut
créer un second tableau, puis copier le premier dans le second grâce à la méthode arraycopy () de la classe
System. La classe Vector peut éventuellement vous servir pour utiliser des tableaux de taille variables, mais
ceci peut vous obliger à transformer beaucoup d'instructions de votre programme : Comme Java n'autorise pas
la surcharge de l'opérateur [ ], l'accès aux éléments d'une instance de Vector ne se fait que par des méthodes.
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 8

Tous les appels à free (ptr) peuvent être directement supprimés ou éventuellement remplacés par ptr =
null; dans les cas où vous voulez que l'objet désigné par ptr ne soit plus référencé par ptr.

Plus vous maîtriserez le fonctionnement du Garbage Collector et la notion de référence, plus vous n'hésiterez
plus à supprimer les instructions free () (C'est un des plus grand plaisirs du portage !).

Utilisation des chaînes de caractères

En Java, les chaînes de caractères sont représentées par les classes String et StringBuffer. Il faut remplacer
tous les tests de caractère nul ('\0') permettant de savoir si on a atteint la fin d'une chaîne de caractères en C,
par des appels aux méthodes length () de ces classes.

Arithmétique des pointeurs

L'arithmétique des pointeurs est une notion absente en Java. Vous devrez remplacer son usage par l'utilisation
d'un indice courant associé à un tableau, ou pour les chaînes de caractères utiliser éventuellement les
méthodes de la classe String comme substring (), pour créer des chaînes de caractères à la volée sans se
soucier de la libération de ces chaînes puisque le Garbage Collector est là pour ça.

Voici un programme C et un programme Java mis en parallèle pour illustrer une manière de transformer une
fonction chercherSousChaine (str1, str2) , qui recherche dans la chaîne de caractères str1 une chaîne str2
(équivalent de la fonction C strstr (char *str1, char *str2) et de la méthode Java indexOf (String str)
de la classe String) :

#include <string.h> class ClasseChaine


{
char *chercherSousChaine (char *str1, public int chercherSousChaine (String str1,
char *str2) String str2)
{ {
while (strlen (str1) >= strlen (str2)) int i = 0;
if (!strncmp (str1, str2, strlen (str2))) while (str1.length () >= str2.length ())
return str1; if (str1.startsWith (str2))
else return i;
str1++; else
return NULL; // Pas trouvé {
} str1 = str1.substring (1);
i++;
}
return -1; // Pas trouvé
}
}

Transformation des pointeurs sur fonctions

En C, les pointeurs sur fonctions sont très pratiques et beaucoup de bibliothèques C en font l'usage. Son
utilisation se regroupe principalement en deux catégories :
Comme paramètre d'une fonction fct (), elle est utilisée souvent comme fonction à rappeler (call-back en
anglais), pour modifier le comportement de fct (). Par exemple, la fonction de comparaison passée en
paramètre à la fonction qsort () permet de trier un tableau de n'importe quel type d'élément, ou la procédure
passée en paramètre à la fonction CreateDialog () de Windows permet de modifier le comportement d'une
boite de dialogue.
Ce type de pointeurs de fonctions peut être transformé par l'utilisation de méthodes outrepassées et a été
abordée au chapitre sur la création des classes.
Comme élément d'un tableau regroupant un ensemble de pointeurs sur fonctions ayant toutes le même
prototype. Ce procédé est souvent utilisé par les automates, pour qu'ils puissent exécuter une action pour un
état donné.
Dans ce cas, soit vous utilisez le procédé précédent en mémorisant des objets à place des fonctions dans le
tableau, comme dans l'exemple suivant :
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 9

/* Déclaration d'un type pointeur sur */ // Déclaration d'une interface déclarant une
/* fonction prenant en paramètre un int */ // méthode prenant en paramètre un int
typedef void (* Action) (int param); interface Action
{
void action (int param);
}

/* Déclaration de deux fonctions */ // Déclaration de deux classes implémentant


/* du même type que Action () */ // la méthode action () de cette interface
class Action1 implements Action
{
void action1 (int param) public void action (int param)
{ /* Corps de action1 () */ } { /* Corps de action () */ }
}

class Action2 implements Action


{
void action2 (int param) public void action (int param)
{ /* Corps de action2 () */ } { /* Corps de action () */ }
}

// Déclaration de la classe Automate


class Automate
{
// Déclaration d'un tableau mémorisant
/* Déclaration d'un tableau mémorisant */ // différents objets dont la classe
/* différents pointeurs de type Action */ // implémente l'interface Action
Action tabAction [] = {action1, NULL, Action tabAction [] =
action2, action1}; {new Action1 (), null,
new Action2 (), new Action1 ()};

void ExecuterAction (int etat, int param) void ExecuterAction (int etat, int param)
{ {
/* Appel de fonction suivant etat */ // Appel de action () en fonction de etat
if (tabAction [etat] != NULL) if (tabAction [etat] != null)
tabAction [etat] (param); tabAction [etat].action (param);
} }

/* ... */ // ...
}

soit vous associez une constante à chacune des fonctions, vous remplacez les pointeurs du tableau par ces
constantes et choisissez la bonne fonction à appeler grâce à l'instruction switch (), comme dans l'exemple
suivant :
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 10

/* Déclaration d'un type pointeur sur */ // Déclaration de la classe Automate


/* fonction prenant en paramètre un int */
typedef void (* Action) (int param); class Automate
{
/* Déclaration de deux fonctions */ // Déclaration de deux fonctions
/* du même type que Action () */ // d'action
void action1 (int param) void action1 (int param)
{ /* Corps de action1 () */ } { /* Corps de action1 () */ }

void action2 (int param) void action2 (int param)


{ /* Corps de action2 () */ } { /* Corps de action2 () */ }

// Déclaration des deux constantes


// représentant les deux méthodes
final static int ACTION1 = 1;
final static int ACTION2 = 2;

/* Déclaration d'un tableau mémorisant */ // Déclaration d'un tableau mémorisant


/* différents pointeurs de type Action */ // différentes constantes d'action
Action tabAction [] = {action1, NULL, int tabAction [] = {ACTION1, 0,
action2, action1}; ACTION2, ACTION1};

void ExecuterAction (int etat, int param) void ExecuterAction (int etat, int param)
{ {
/* Appel de fonction suivant etat */ // Appel de la bonne méthode
if (tabAction [etat] != NULL) // en fonction de etat
tabAction [etat] (param); switch (etat)
} {
case ACTION1 : action1 (param);
/* ... */ break;
case ACTION2 : action2 (param);
break;
}
}
}

Remplacement de l'héritage multiple

Java ne permet pas l'héritage multiple du C++. Si vous voulez néammoins conserver une architecture de
classes qui utilise l'héritage multiple, vous pouvez utiliser les interfaces, en utilisant par exemple la solution
suivante :
vendredi 11 février 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 11

class Type1 class Type1


{ {
public : public void methode1 () { /* ... */ }
void methode1 () { /* ... */ } }
};
// Utilisation d'une interface à la place
// de la seconde classe
class Type2 interface Type2
{ {
public : void methode2 ();
void methode2 () { /* ... */ } }
};
class Classe2 implements Type2
{
public void methode2 () { /* ... */ }
}

class ClasseDerivee : public Type1, public class ClasseDerivee extends Type1


public Type2 implements Type2
{ {
// La classe ClasseDerivee hérite des // Création d'une instance de Classe2
// méthodes methode1 () et methode2 () // pour remplacer le second lien d'héritage
}; Type2 objetClasse2 = new Classe2 ();

// Implémentation de methode2 () pour


// qu'elle appelle la méthode de Classe2
public void methode2 ()
{
objetClasse2.methode2 ();
}

void main () public static void main (String [] args)


{ {
Type1 *objet1 = new Type1 (); Type1 objet1 = new Type1 ();
Type2 *objet2 = new Type2 (); Type2 objet2 = new Classe2 ();

ClasseDerivee *objet3 = new ClasseDerivee (); ClasseDerivee objet3 = new ClasseDerivee ();
objet3->methode1 (); objet3.methode1 ();
objet3->methode2 (); objet3.methode2 ();

// Cast implicite vers la super classe


// Cast implicite vers les super classes // ou l'interface Type2
objet1 = objet3; objet1 = objet3;
objet2 = objet3; objet2 = objet3;
} }
}

Cette solution peut être longue à programmer si les super classes définissent un nombre important de
méthodes. Par contre, cette solution permet de modifier aisément le programme qui utilise les classes
transformées en interfaces : vous n'avez qu'à modifier le nom de la classe utilisée à l'instantiation des objets
(ici transformer new Type2 () en new Classe2 ()).

Autres problèmes propres au C++

Paramètres passés par référence en C++ : si le paramètre est d'un type de base, vous devrez le traiter comme
s'il était passé par adresse. Si le paramètre est une instance d'une classe ou une structure, supprimer
simplement le symbole & de la déclaration.
Les appels à l'opérateur delete du C++ pour des destructeurs n'effectuant que des libérations de mémoire
deviennent normalement inutiles en Java. Pour les autres vous pouvez créer une méthode delete () dans vos
classes, et remplacer les appels delete objet; par objet.delete ();.
vendredi 11 février 2000 Du C/C++ à Java : La bibliothèque Java Page: 1

La bibliothèque Java 1.0

Les packages de la bibliothèque Java


Hiérarchie des classes Java 1.0

Maintenant que vous connaissez le langage Java, il vous reste à apprendre les classes Java fournies en
standard avec la Machine Virtuelle Java.

Il est fourni avec le JDK le code source des classes des packages Java standards. Alors, n'hésitez pas à
le consulter si vous recherchez plus d'informations sur le fonctionnement d'une classe ou d'une
méthode.

Dans certaines classes, vous pouvez être étonné que certaines méthodes synchronized semblent
similaires à d'autres qui ne le sont pas. Ceci tient généralement du fait que certaines méthodes s'appellent
entre elles, et seule la méthode finalement appelée est synchronized.

Les packages de la bibliothèque Java 1.0

Les packages ci-après sont cités dans l'ordre des chapitres du manuel.

Classes de base : le package java.lang

Ce package est développé dans le chapitre suivant et rassemble les classes de base de Java. Toutes les
classes et interfaces de java.lang sont automatiquement importées par le compilateur.

Gestion de données et utilitaires : le package java.util

Ce package est développé dans le chapitre sur les outils Java et rassemble des classes d'utilitaires
(gestion des collections de données, génération de nombres aléatoires, énumération, date,...). Il définit
aussi les classes d'exceptions EmptyStackException et NoSuchElementException.

Les entrées-sorties : le package java.io

Ce package est développé dans le chapitre sur la gestion des fichiers et des flux de données et rassemble
les classes permettant de gérer les entrées-sorties (accès fichiers, gestion de répertoires,...). Il définit
aussi les classes d'exceptions IOException, EOFException, FileNotFoundException,
InterruptedIOException et UTFDataFormatException.

Les accès réseau : le package java.net

Ce package est développé dans le chapitre sur les accès au réseau et rassemble les classes permettant de
gérer les accès réseau. Il définit aussi les classes d'exceptions MalformedURLException,
ProtocolException, SocketException, UnknownHostException et UnknownServiceException.

Gestion des applets : le package java.applet

Ce package est développé dans le chapitre sur les applets. La classe Applet et les interfaces de ce
package permettent de programmer une applet Java et d'intégrer une applet dans un navigateur.

Interface utilisateur : le package java.awt


vendredi 11 février 2000 Du C/C++ à Java : La bibliothèque Java Page: 2

Ce package est développé dans le chapitre sur les composants AWT et les suivants. Les classes de ce
package permettent de programmer l'interface graphique d'un programme ou d'une applet Java. Il définit
aussi les classes d'exceptions AWTException et AWTError.

Manipulation d'images : le package java.awt.image

Ce package est développé dans le chapitre sur les images. Les classes de ce package permettent de
manipuler les images (gestion du chargement des images, filtres, gestion des couleurs,...).

Liaison avec l'interface utilisateur du système : le package java.awt.peer

Ce package est abordé dans le chapitre sur les composants. Il définit un ensemble d'interfaces permettant
aux composants graphiques Java (boutons, fenêtres,...) d'être représentés à l'écran par les composants du
système sur lequel fonctionne une Machine Virtuelle Java.

Hiérarchie des classes Java 1.0

Voici la hiérarchie des classes Java dans sa version 1.0.

Classe java.lang.Object
Interface java.applet.AppletContext
Interface java.applet.AppletStub
Interface java.applet.AudioClip
Classe java.util.BitSet (implémente java.lang.Cloneable)
Classe java.lang.Boolean
Classe java.awt.BorderLayout (implémente java.awt.LayoutManager)
Classe java.awt.CardLayout (implémente java.awt.LayoutManager)
Classe java.lang.Character
Classe java.awt.CheckboxGroup
Classe java.lang.Class
Classe java.lang.ClassLoader
Interface java.lang.Cloneable
Classe java.awt.Color
Classe java.awt.image.ColorModel
Classe java.awt.image.DirectColorModel
Classe java.awt.image.IndexColorModel
Classe java.lang.Compiler
Classe java.awt.Component (implémente java.awt.image.ImageObserver)
Classe java.awt.Button
Classe java.awt.Canvas
Classe java.awt.Checkbox
Classe java.awt.Choice
Classe java.awt.Container
Classe java.awt.Panel
Classe java.applet.Applet
Classe java.awt.Window
Classe java.awt.Dialog
Classe java.awt.FileDialog
Classe java.awt.Frame (implémente java.awt.MenuContainer)
Classe java.awt.Label
Classe java.awt.List
Classe java.awt.Scrollbar
Classe java.awt.TextComponent
Classe java.awt.TextArea
Classe java.awt.TextField
Classe java.net.ContentHandler
Interface java.net.ContentHandlerFactory
Interface java.io.DataInput
vendredi 11 février 2000 Du C/C++ à Java : La bibliothèque Java Page: 3

Interface java.io.DataOutput
Classe java.net.DatagramPacket
Classe java.net.DatagramSocket
Classe java.util.Date
Classe java.util.Dictionary
Classe java.util.Hashtable (implémente java.lang.Cloneable)
Classe java.util.Properties
Classe java.awt.Dimension
Interface java.util.Enumeration
Classe java.awt.Event
Classe java.io.File
Classe java.io.FileDescriptor
Interface java.io.FilenameFilter
Classe java.awt.image.FilteredImageSource (implémente java.awt.image.ImageProducer)
Classe java.awt.FlowLayout (implémente java.awt.LayoutManager)
Classe java.awt.Font
Classe java.awt.FontMetrics
Classe java.awt.Graphics
Classe java.awt.GridBagConstraints (implémente java.lang.Cloneable)
Classe java.awt.GridBagLayout (implémente java.awt.LayoutManager)
Classe java.awt.GridLayout (implémente java.awt.LayoutManager)
Classe java.awt.Image
Interface java.awt.image.ImageConsumer
Classe java.awt.image.ImageFilter (implémente java.awt.image.ImageConsumer,
java.lang.Cloneable)
Classe java.awt.image.CropImageFilter
Classe java.awt.image.RGBImageFilter
Interface java.awt.image.ImageObserver
Interface java.awt.image.ImageProducer
Classe java.net.InetAddress
Classe java.io.InputStream
Classe java.io.ByteArrayInputStream
Classe java.io.FileInputStream
Classe java.io.FilterInputStream
Classe java.io.BufferedInputStream
Classe java.io.DataInputStream (implémente java.io.DataInput)
Classe java.io.LineNumberInputStream
Classe java.io.PushbackInputStream
Classe java.io.PipedInputStream
Classe java.io.SequenceInputStream
Classe java.io.StringBufferInputStream
Classe java.awt.Insets (implémente java.lang.Cloneable)
Interface java.awt.LayoutManager
Classe java.lang.Math
Classe java.awt.MediaTracker
Classe java.awt.image.MemoryImageSource (implémente java.awt.image.ImageProducer)
Classe java.awt.MenuComponent
Classe java.awt.MenuBar (implémente java.awt.MenuContainer)
Classe java.awt.MenuItem
Classe java.awt.CheckboxMenuItem
Classe java.awt.Menu (implémente java.awt.MenuContainer)
Interface java.awt.MenuContainer
Classe java.lang.Number
Classe java.lang.Double
Classe java.lang.Float
Classe java.lang.Integer
Classe java.lang.Long
Classe java.util.Observable
Interface java.util.Observer
vendredi 11 février 2000 Du C/C++ à Java : La bibliothèque Java Page: 4

Classe java.io.OutputStream
Classe java.io.ByteArrayOutputStream
Classe java.io.FileOutputStream
Classe java.io.FilterOutputStream
Classe java.io.BufferedOutputStream
Classe java.io.DataOutputStream (implémente java.io.DataOutput)
Classe java.io.PrintStream
Classe java.io.PipedOutputStream
Classe java.awt.image.PixelGrabber (implémente java.awt.image.ImageConsumer)
Classe java.awt.Point
Classe java.awt.Polygon
Classe java.lang.Process
Classe java.util.Random
Classe java.io.RandomAccessFile (implémente java.io.DataOutput, java.io.DataInput)
Classe java.awt.Rectangle
Interface java.lang.Runnable
Classe java.lang.Runtime
Classe java.lang.SecurityManager
Classe java.net.ServerSocket
Classe java.net.Socket
Classe java.net.SocketImpl
Interface java.net.SocketImplFactory
Classe java.io.StreamTokenizer
Classe java.lang.String
Classe java.lang.StringBuffer
Classe java.util.StringTokenizer (implémente java.util.Enumeration)
Classe java.lang.System
Classe java.lang.Thread (implémente java.lang.Runnable)
Classe java.lang.ThreadGroup
Classe java.lang.Throwable
Classe java.lang.Error
Classe java.awt.AWTError
Classe java.lang.LinkageError
Classe java.lang.ClassCircularityError
Classe java.lang.ClassFormatError
Classe java.lang.IncompatibleClassChangeError
Classe java.lang.AbstractMethodError
Classe java.lang.IllegalAccessError
Classe java.lang.InstantiationError
Classe java.lang.NoSuchFieldError
Classe java.lang.NoSuchMethodError
Classe java.lang.NoClassDefFoundError
Classe java.lang.UnsatisfiedLinkError
Classe java.lang.VerifyError
Classe java.lang.ThreadDeath
Classe java.lang.VirtualMachineError
Classe java.lang.InternalError
Classe java.lang.OutOfMemoryError
Classe java.lang.StackOverflowError
Classe java.lang.UnknownError
Classe java.lang.Exception
Classe java.awt.AWTException
Classe java.lang.ClassNotFoundException
Classe java.lang.CloneNotSupportedException
Classe java.lang.IllegalAccessException
Classe java.lang.InstantiationException
Classe java.lang.InterruptedException
Classe java.io.IOException
Classe java.io.EOFException
vendredi 11 février 2000 Du C/C++ à Java : La bibliothèque Java Page: 5

Classe java.io.FileNotFoundException
Classe java.io.InterruptedIOException
Classe java.net.MalformedURLException
Classe java.net.ProtocolException
Classe java.net.SocketException
Classe java.io.UTFDataFormatException
Classe java.net.UnknownHostException
Classe java.net.UnknownServiceException
Classe java.lang.RuntimeException
Classe java.lang.ArithmeticException
Classe java.lang.ArrayStoreException
Classe java.lang.ClassCastException
Classe java.util.EmptyStackException
Classe java.lang.IllegalArgumentException
Classe java.lang.IllegalThreadStateException
Classe java.lang.NumberFormatException
Classe java.lang.IllegalMonitorStateException
Classe java.lang.IndexOutOfBoundsException
Classe java.lang.ArrayIndexOutOfBoundsException
Classe java.lang.StringIndexOutOfBoundsException
Classe java.lang.NegativeArraySizeException
Classe java.util.NoSuchElementException
Classe java.lang.NullPointerException
Classe java.lang.SecurityException
Classe java.awt.Toolkit
Classe java.net.URL
Classe java.net.URLConnection
Classe java.net.URLEncoder
Classe java.net.URLStreamHandler
Interface java.net.URLStreamHandlerFactory
Classe java.util.Vector (implémente java.lang.Cloneable)
Classe java.util.Stack

(Les classes du package java.awt.peer ne sont pas citées)


vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 1

Les classes de bases

Les classes de gestion des objets


Les classes d'emballage
Calcul mathématique : la classe java.lang.Math
Les classes de gestion des threads
Les classes de gestion du système

Ce chapitre décrit le package java.lang de Java 1.0 qui rassemble les classes de base de Java.
Les principales classes de ce package ayant déjà été étudiées (Class, Object, String, StringBuffer,
System, Thread, Throwable et ses dérivées), voici la description des autres classes et interfaces fournies
par ce package.

Rappel : Toutes les classes et interfaces de java.lang sont automatiquement importées par le
compilateur.

Gestion des objets

La classe java.lang.Object

Cette classe est à la racine de la hiérarchie des classes Java, donc toute classe hérite implicitement de la
classe Object. Elle est détaillée au chapitre sur les objets.

L'interface java.lang.Cloneable

Cette interface ne déclare ni constantes, ni méthodes. Elle doit être implémentée par toute classe qui
veut outrepasser et utiliser la méthode clone () de la classe Object, qui permet de créer une copie d'un
objet.

La classe java.lang.Class

Cette classe final permet de représenter chacune des classes chargées par la Machine Virtuelle Java.
Elle est détaillée au chapitre sur les objets.

La classe java.lang.ClassLoader

La création d'une classe dérivée de cette classe abstract permet de créer un chargeur de classe
spécifique.
Par défaut, chaque classe est chargée dynamiquement à sa première utilisation à partir du fichier .class de
même nom que la classe qui provient de la machine locale ou à travers un réseau. Mais vous pouvez
charger vous-même une ou plusieurs classes pour qu'elles existent et soient utilisables dans la Machine
Virtuelle Java.
Ceci permet par exemple de protéger l'ensemble des fichiers .class d'une application, pour éviter qu'il
soit repris par d'autres personnes. Il suffit de coder ces fichiers et de créer une classe de chargeur de
classes capable de les décoder. Au chargement d'une classe, les fichiers seront décodés puis transmis à
la Machine Virtuelle Java.

Constructeur

protected ClassLoader () throws SecurityException

Méthodes
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 2

protected abstract Class loadClass (String name, boolean resolve)


throws ClassNotFoundException

Cette méthode doit être outrepassée pour charger et renvoyer la classe de nom name. Si resolve est égal
à true, les classes qu'utilise cette classe doivent être résolues. Si la classe n'est pas trouvée une
exception de classe ClassNotFoundException doit être déclenchée.
Les trois autres méthodes sont final et protected et sont des outils utilisés pour créer ou préparer les
classes.

protected final Class defineClass (byte data [ ], int offset, int length)
throws IndexOutOfBoundsException, ClassFormatError

Permet de créer une instance de la classe Class à partir des length octets du tableau data pris à partir de
l'indice offset. Si cet ensemble d'octets ne représente pas une classe Java valide, une exception
ClassFormatError est déclenchée.

protected final void resolveClass (Class c)

Permet de résoudre et charger les classes qu'utilise la classe c. Cette opération doit être effectuée avant
d'utiliser la classe c et entraîne que la méthode loadClass () sera appelée pour chacune des classes que
référencent les variables de la classe c.

protected final Class findSystemClass (String name) throws ClassNotFoundException

Charge la classe de nom name avec le chargeur de classe par défaut.

Le constructeur de la classe ClassLoader peut éventuellement déclencher une exception de classe


SecurityException, si le gestionnaire de sécurité interdit de créer d'autres chargeurs de classes, comme
le font par exemple les navigateurs. Donc, si vous créez des applets pour Internet vous ne pourrez pas
changer le chargeur de classe par défaut.

La classe java.lang.Compiler

Cette classe final fournit un ensemble de méthodes relatives à la compilation des classes Java en code
machine du système sur lequel fonctionne la Machine Virtuelle Java.

Méthodes

public static Object command (Object any)


public static boolean compileClass (Class class)
public static boolean compileClasses (String string)
public static void disable ()
public static void enable ()

La classe java.lang.Throwable

Cette classe est à la racine de toutes les classes d'exceptions. Throwable et toutes ses classes dérivées
sont détaillées au chapitre traitant des exceptions.

Les classes d'emballage

La bibliothèque Java fournit un ensemble de classes qui permettent de manipuler les types de base sous
forme d'objets, comme par exemple la classe Boolean pour le type boolean. Chacune de ces classes ne
mémorisant qu'une variable du type de base donné associé, on appelle ce type de classe une classe
d'emballage (wrapper en anglais).
Ces classes peuvent être utile pour les classes ne manipulant que des objets, comme par exemple la
classe java.util.Vector, qui permet de créer un ensemble d'objets quelconques.
Une classe d'emballage existant pour tous les types de base (sauf pour les types byte, short et void sous
Java 1.0), il est donc possible de programmer du "pur objet" en Java, où toutes les variables ne seraient
que des objets et uniquement cela.
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 3

Les classes d'emballage de Java ne permettent pas de modifier la valeur qu'elles mémorisent.

La classe java.lang.Boolean

Cette classe final est la classe d'emballage qui correspond au type boolean.

Variables

public final static Boolean TRUE


public final static Boolean FALSE

TRUE et FALSE sont deux constantes qui correspondent aux valeurs true et false du type boolean.

Constructeurs

public Boolean (boolean value)


public Boolean (String s)

Ces constructeurs permettent de créer une instance de la classe Boolean à partir de la valeur booléene
value ou de la chaîne de caractères s.

Méthodes

public boolean booleanValue ()

Renvoie la valeur booléenne mémorisée.

public static Boolean valueOf (String s)

Renvoie une instance de la classe Boolean à partir de la chaîne de caractères s. s est considérée égale à
true si et seulement si elle est égale à "true" en minuscules ou en majuscules.

public static boolean getBoolean (String s)

Renvoie true ou false si la propriété name du système est égale à "true". Si cette propriété n'existe pas,
cette méthode renvoie false.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet booléen à un objet ou renvoyer une chaîne de caractères décrivant la valeur booléenne.

La classe java.lang.Character

Cette classe final est la classe d'emballage qui correspond au type char. Elle déclare un grand nombre
de méthodes static permettant de reconnaître le type de lettres (majuscule, minuscule, chiffre,...) et
d'effectuer des conversions.

Variables

public final static int MIN_RADIX


public final static int MAX_RADIX

Ces constantes sont les bases de conversion minimum (= 2) et maximum (= 36) des nombres entiers.

public final static char MIN_VALUE


public final static char MAX_VALUE

Valeurs minimum ('\u0000') et maximum ('\ffff') d'un caractère Unicode.

Constructeur

public Character (char value)


vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 4

Construit une instance de la classe Character à partir du caractère value.

Méthodes

public char charValue ()

Renvoie le caractère mémorisé.

public static boolean isLowerCase (char ch)


public static boolean isUpperCase (char ch)
public static boolean isTitleCase (char ch)

Ces méthodes renvoient true si le caractère ch est une minuscule, une majuscule ou une lettre de titre.

public static boolean isDefined (char ch)

Ces méthodes renvoient true si le caractère ch est défini dans l'ensemble des caractères Unicode.

public static boolean isDigit (char ch)

Renvoie true si le caractère ch est un chiffre.

public static boolean isLetter (char ch)

Renvoie true si le caractère ch est une lettre.

public static boolean isLetterOrDigit (char ch)

Renvoie true si le caractère ch est un chiffre ou une lettre.

public static boolean isJavaLetter (char ch)

Renvoie true si le caractère ch est une lettre Java, c'est-à-dire qu'elle est permise comme première lettre
d'un identifiant Java.

public static boolean isJavaLetterOrDigit (char ch)

Renvoie true si le caractère ch est une lettre ou un chiffre Java, c'est-à-dire qu'elle est permise comme
lettre d'un identifiant Java différente de la première.

public static boolean isSpace (char ch)

Renvoie true si le caractère ch est un espace c'est-à-dire un des caractères ' ', '\t', '\f', '\n' ou '\r'.

public static char toLowerCase (char ch)


public static char toUpperCase (char ch)
public static char toTitleCase (char ch)

Ces méthodes renvoient le caractère ch converti en minuscule, en majuscule ou en lettre de titre.

public static int digit (char ch, int radix)

Renvoie le nombre correspondant au caractère ch en base radix ou -1 si le caractère ch est incorrect en


base radix.

public static char forDigit (int digit, int radix)

Renvoie le caractère correspondant au nombre digit en base radix, ou le caractère '\0' si le nombre
est négatif ou plus grand que la base radix.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet caractère à un objet ou renvoyer une chaîne de caractères avec le caractère mémorisé.
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 5

Exemples

Applet Unicode .
Classe ToUpperCaseInputStream.

La classe java.lang.Number

Cette classe abstract est la super classe des classes d'emballage Integer, Long, Float et Double qui
permettent de traiter des nombres de type int, long, float et double sous forme d'objets.

Constructeur

public Number ()

Méthodes

public abstract int intValue ()


public abstract long longValue ()
public abstract float floatValue ()
public abstract double doubleValue ()

Ces méthodes doivent renvoyer le nombre mémorisé dans l'un des types de base int, long, float ou
double.

La classe java.lang.Integer

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type int.

Variables

public final static int MIN_VALUE


public final static int MAX_VALUE

Valeurs minimum (-2147483648) et maximum (21474836487) d'un entier de type int.

Constructeurs

public Integer (int value)


public Integer (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Integer à partir de la valeur entière
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public static String toString (int i, int radix)

Renvoie la chaîne de caractères correspondant au nombre i en base radix.

public static String toHexString (int i)


public static String toOctalString (int i)
public static String toBinaryString (int i)
public static String toString (int i)

Renvoie la chaîne de caractères correspondant au nombre i en base 16 (hexadécimal), en base 8 (octal),


en base 2 (binaire) ou en base 10 (décimal).

public static int parseInt (String s, int radix) throws NumberFormatException


public static int parseInt (String s) throws NumberFormatException
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 6

Ces méthodes renvoient le nombre contenu dans la chaîne de caractères s en base radix (radix = 10
par défaut). Si s contient des caractères invalides ou si le nombre contenu dans s est plus grand que
MAX_VALUE ou plus petit que MIN_VALUE, une exception NumberFormatException est déclenchée.

public static Integer valueOf (String s, int radix) throws NumberFormatException


public static Integer valueOf (String s) throws NumberFormatException

Ces méthodes renvoient une instance de la classe Integer mémorisant le nombre contenu dans la chaîne
de caractères s en base radix (radix = 10 par défaut).

public static Integer getInteger (String name)


public static Integer getInteger (String name, int val)
public static Integer getInteger (String name, Integer val)

Ces méthodes renvoient la propriété name du système. Si cette propriété n'existe pas ou n'est pas un
entier, la valeur val (ou par défaut null) est renvoyée.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet entier à un objet ou renvoyer une chaîne de caractères décrivant la valeur entière.

Exemples

Applets PaperBoardClient , Unicode et ListePolices .


Application LectureFichier .

La classe java.lang.Long

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type long.

Variables

public final static int MIN_VALUE


public final static int MAX_VALUE

Valeurs minimum (-9223372036854775808) et maximum (9223372036854775807) d'un entier de type long.

Constructeurs

public Long (long value)


public Long (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Long à partir de la valeur entière value
ou du nombre contenu dans la chaîne de caractères s.

Méthodes

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public static String toString (long i, int radix)

Renvoie la chaîne de caractères correspondant au nombre i en base radix.

public static String toHexString (long i)


public static String toOctalString (long i)
public static String toBinaryString (long i)
public static String toString (long i)
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 7

Renvoie la chaîne de caractères correspondant au nombre i en base 16 (hexadécimal), en base 8 (octal),


en base 2 (binaire) ou en base 10 (décimal).

public static long parseLong (String s, int radix) throws NumberFormatException


public static long parseLong (String s) throws NumberFormatException

Ces méthodes renvoient le nombre contenu dans la chaîne de caractères s en base radix (radix = 10
par défaut). Si s contient des caractères invalides une exception NumberFormatException est déclenchée.

public static Long valueOf (String s, int radix) throws NumberFormatException


public static Long valueOf (String s) throws NumberFormatException

Ces méthodes renvoient une instance de la classe Long mémorisant le nombre contenu dans la chaîne de
caractères s en base radix (radix = 10 par défaut).

public static Long getLong (String name)


public static Long getLong (String name, long val)
public static Long getLong (String name, Long val)

Ces méthodes renvoient la propriété name du système. Si cette propriété n'existe pas ou n'est pas un
entier, la valeur val (ou par défaut null) est renvoyée.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet entier à un objet ou renvoyer une chaîne de caractères décrivant la valeur entière.

La classe java.lang.Float

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type float.

Variables

public final static float POSITIVE_INFINITY


public final static float NEGATIVE_INFINITY
public final static float NaN

Ces constantes permettent d'obtenir les valeurs correspondantes à l'infini positif, négatif ou représentant
une valeur non significative.

public final static double MAX_VALUE


public final static double MIN_VALUE

Valeurs minimum (1.40129846432481707e-45f) et maximum (3.40282346638528860e+38f) d'un nombre


flottant de type float.

Constructeurs

public Float (float value)


public Float (double value)
public Float (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Float à partir de la valeur flottante
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public boolean isInfinite ()


vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 8

public boolean isNaN ()

Ces méthodes renvoie true si le nombre mémorisé est infini ou si c'est une valeur non significative.

public static boolean isInfinite (float v)


public static boolean isNaN (float v)

Ces méthodes renvoie true si v est une valeur infinie ou si c'est une valeur non significative.

public static String toString (float f)

Renvoie la chaîne de caractères correspondant au nombre f.

public static Float valueOf (String s) throws NumberFormatException

Renvoie une instance de la classe Float mémorisant le nombre contenu dans la chaîne de caractères s. Si
s contient des caractères invalides une exception NumberFormatException est déclenchée.

public static int floatToIntBits (float value)


public static float intBitsToFloat (int bits)

Ces méthodes convertissent la valeur value en son équivalent en int, ou inversement la valeur bits de
type int en float. Elles sont utilisées par les méthodes writeFloat () et readFloat () de certaines des
classes d'entrée-sortie qui manipulent un nombre de type float sous forme de valeur 32 bits.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe Float à un objet ou renvoyer une chaîne de caractères décrivant la valeur flottante.

Exemple

Applet CalculetteSimple .

La classe java.lang.Double

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type
double.

Variables

public final static double POSITIVE_INFINITY


public final static double NEGATIVE_INFINITY
public final static double NaN

Ces constantes permettent d'obtenir les valeurs correspondantes à l'infini positif, négatif ou représentant
une valeur non significative (par exemple, Math.sqrt (-1) renvoie Double.NaN).

public final static double MAX_VALUE


public final static double MIN_VALUE

Valeurs minimum (2.2250738585072014e-308) et maximum (1.79769313486231570e+308) d'un nombre


flottant de type double.

Constructeurs

public Double (double value)


public Double (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Double à partir de la valeur flottante
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 9

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public boolean isInfinite ()


public boolean isNaN ()

Ces méthodes renvoie true si le nombre mémorisé est infini ou si c'est une valeur non significative.

public static boolean isInfinite (double v)


public static boolean isNaN (double v)

Ces méthodes renvoie true si v est une valeur infinie ou si c'est une valeur non significative.

public static String toString (double d)

Renvoie la chaîne de caractères correspondant au nombre d.

public static Double valueOf (String s) throws NumberFormatException

Renvoie une instance de la classe Double mémorisant le nombre contenu dans la chaîne de caractères s.
Si s contient des caractères invalides une exception NumberFormatException est déclenchée.

public static long doubleToLongBits (double value)


public static double longBitsToDouble (long bits)

Ces méthodes convertissent la valeur value en son équivalent en long, ou inversement la valeur bits de
type long en double. Elles sont utilisées par les méthodes writeDouble () et readDouble () de certaines
des classes d'entrée-sortie qui manipulent un nombre de type double sous forme de valeur 64 bits.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe Double à un objet ou renvoyer une chaîne de caractères décrivant la valeur flottante.

Exemple

Application TestExpression .

La classe java.lang.String

Cette classe final permet de manipuler les chaînes de caractères constantes. Elle est détaillée au
chapitre sur les chaînes de caractères.

La classe java.lang.StringBuffer

Cette classe permet de manipuler et modifier des chaînes de caractères. Elle est détaillée au chapitre sur
les chaînes de caractères.

Calcul mathématique : la classe java.lang.Math

Cette classe final rassemble l'ensemble des méthodes de calcul mathématique Java. Toutes ses
méthodes sont static.

Variables

public final static double E


public final static double PI

Ces variables permettent d'obtenir les nombres e (= 2.71828...) et Pi (π = 3.14159...).


vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 10

Méthodes

public static double sin (double a)


public static double cos (double a)
public static double tan (double a)

Ces méthodes permettent de calculer le sinus, le cosinus du nombre a exprimé en radian.

public static double asin (double a)

Renvoie l'arc sinus (sin-1) du nombre a, exprimé en radian et compris entre -PI/2 et PI/2.

public static double acos (double a)

Renvoie l'arc cosinus (cos-1) du nombre a, exprimé en radian et compris entre 0 et PI.

public static double atan (double a)

Renvoie l'arc tangente (tan-1) du nombre a, exprimé en radian et compris entre -PI/2 et PI/2.

public static double atan2 (double a, double b)

Renvoie l'arc tangente (tan-1) de b/a, exprimé en radian et compris entre -PI et PI.

public static double exp (double a)


public static double log (double a)

Ces méthodes permettent de calculer l'exponentielle ou le logarithme népérien du nombre a.

public static double IEEEremainder (double f1, double f2)

Renvoie le reste de la division de f1 par f2 (par exemple Math.IEEEremainder (4.5, 2) = 0.5,


Math.IEEEremainder (4.5, 2.2) = 0.1) .

public static double floor (double a)

Renvoie la plus grande valeur entière plus petite ou égale à a.

public static double ceil (double a)

Renvoie la plus petite valeur entière plus grande ou égale à a.

public static double sqrt (double a)

Renvoie la racine carrée du nombre a.

public static double pow (double a, double b)

Renvoie le nombre a à la puissance b (ab).

public static double rint (double a)


public static int round (float a)
public static long round (double a)

Ces méthodes renvoient la valeur arrondie au plus proche entier du nombre décimal a.

public static synchronized double random ()

Renvoie un nombre aléatoire compris entre 0. inclus et 1. exclu. Voir aussi la classe java.util.Random.

public static int abs (int a)


public static long abs (long a)
public static float abs (float a)
public static double abs (double a)

Ces méthodes renvoient la valeur absolue du nombre a.


vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 11

public static int max (int a, int b)


public static long max (long a, long b)
public static float max (float a, float b)
public static double max (double a, double b)
public static int min (int a, int b)
public static long min (long a, long b)
public static float min (float a, float b)
public static double min (double a, double b)

Ces méthodes renvoient la valeur maximum ou minimum des nombres a et b.

Les méthodes log (), sqrt (), pow (),... auxquelles sont envoyés des arguments interdits en
mathématique (comme le logarithme népérien d'un nombre négatif) renvoie la valeur non significative
Double.NaN sans déclencher d'exception.

Exemples

Applets AfficheurDeCalcul , ObservateurCalcul, DrawIt , ImageNoirEtBlanc , Compteur et Horloge .

Gestion des threads

L'interface java.lang.Runnable

Cette interface déclare uniquement la méthode run () :

public void run ()

Toute classe Classe1 peut implémenter l'interface Runnable, et implémenter une méthode run () qui
sera exécutée par les threads créés à partir de la classe Classe1.

Exemples

Application PaperBoardServer .
Applets Chrono , AfficheurDeCalcul , ObservateurCalcul , PaperBoardClient , AnimationFleche , ScrollText et
Horloge .

La classe java.lang.Thread

Cette classe permet de créer et de manipuler les threads. Elle est détaillée au chapitre sur les threads.

La classe java.lang.ThreadGroup

Cette classe permet de consulter ou créer des groupes de threads, pour rassembler vos threads par
exemple par type. Voir aussi la classe Thread.

Constructeurs

public ThreadGroup (String name) throws SecurityException


public ThreadGroup (ThreadGroup parent, String name)
throws SecurityException, IllegalThreadStateException

Méthodes

public String toString ()


public final void checkAccess ()
public final String getName ()
public final ThreadGroup getParent ()
public final boolean parentOf (ThreadGroup g)
public final synchronized void stop () throws SecurityException
public final synchronized void suspend () throws SecurityException
public final synchronized void resume () throws SecurityException
public final synchronized void destroy ()
throws SecurityException, IllegalThreadStateException
public final int getMaxPriority ()
public final synchronized void setMaxPriority (int newMaxPriority)
throws SecurityException, IllegalArgumentException
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 12

public final boolean isDaemon ()


public final void setDaemon (boolean daemon) throws SecurityException
public synchronized int activeCount ()
public synchronized int activeGroupCount ()
public int enumerate (Thread list [ ])
public int enumerate (Thread list [ ], boolean recurse)
public int enumerate (ThreadGroup list [ ])
public int enumerate (ThreadGroup list [ ], boolean recurse)
public synchronized void list ()
public void uncaughtException (Thread t, Throwable e)

Gestion du système

La classe java.lang.System

Cette classe final permet d'accéder à différentes fonctionnalités du système de la Machine Virtuelle
Java. Elle est détaillée au chapitre sur les objets.

La classe java.lang.Runtime

Cette classe permet de manipuler le Runtime Java, renvoyé par la méthode getRuntime (). Les méthodes
exit (), exec (), load () et loadLibrary () sont susceptibles de déclencher une exception
SecurityException si le gestionnaire de sécurité interdit ces opérations.

Méthodes

public static Runtime getRuntime ()

Renvoie le Runtime Java.

public synchronized void load (String filename)


throws SecurityException, UnsatisfiedLinkError
public synchronized void loadLibrary (String libname)
throws SecurityException, UnsatisfiedLinkError
public void exit (int status) throws SecurityException
public void gc ()
public void runFinalization ()

Ces méthodes ont le même effet que celles que de la classe java.lang.System.

public Process exec (String command)


throws IOException, SecurityException
public Process exec (String command, String envp [ ])
throws IOException, SecurityException
public Process exec (String cmdarray [ ])
throws IOException, SecurityException
public Process exec (String cmdarray [ ], String envp [ ])
throws IOException, SecurityException

Ces méthodes permettent d'exécuter la commande du système command ou cmdarray [0]. envp [] est un
tableau contenant les paramètres à passer à la commande au format param=valeur (pour passer par
exemple des paramètres à java ). exec () renvoie une instance de la classe Process qui permet de
contrôler la commande exécutée.

public long freeMemory ()


public long totalMemory ()

Ces méthodes renvoient la taille de la mémoire libre et la taille totale de la mémoire.

public void traceInstructions (boolean on)


public void traceMethodCalls (boolean on)
public InputStream getLocalizedInputStream (InputStream in)
public OutputStream getLocalizedOutputStream (OutputStream out)

La classe java.lang.Process

Les objets dérivant de cette classe abstract sont retournés par les méthodes exec () de l a classe
vendredi 11 février 2000 Du C/C++ à Java : Les classes de bases Page: 13

Runtime. Ces méthodes permettent de contrôler l'état d'un process.

Constructeurs

public Process ()

Méthodes

public abstract OutputStream getOutputStream ()


public abstract InputStream getInputStream ()
public abstract InputStream getErrorStream ()

Ces méthodes renvoient les flux de sortie, d'entrée et d'erreur d'un process.

public abstract int waitFor () throws InterruptedException


public abstract void destroy ()

Ces méthodes permettent d'attendre la fin d'un process ou de le détruire.

public abstract int exitValue () throws IllegalThreadStateException

Renvoie la valeur renvoyée par le process.

La classe java.lang.SecurityManager

Une classe dérivée de cette classe abstract permet de définir un gestionnaire de sécurité dont l'instance
est passée en paramètre à la méthode setSecurityManager () de la classe System. Ce gestionnaire est
utilisé par les navigateurs pour interdire aux applets en autre, l'accès au système de fichiers local.

Variables

protected boolean inCheck

Constructeurs

protected SecurityManager () throws SecurityException

Méthodes

protected Class [ ] getClassContext ()


protected int classDepth (String name)
protected boolean inClass (String name)
protected ClassLoader currentClassLoader ()
protected int classLoaderDepth ()
protected boolean inClassLoader ()
public boolean getInCheck ()
public Object getSecurityContext ()
public void checkAccept (String host, int port) throws SecurityException
public void checkAccess (Thread t) throws SecurityException
public void checkAccess (ThreadGroup g) throws SecurityException
public void checkConnect (String host, int port) throws SecurityException
public void checkConnect (String host, int port, Object context)
throws SecurityException
public void checkCreateClassLoader () throws SecurityException
public void checkDelete (String file) throws SecurityException
public void checkExec (String cmd) throws SecurityException
public void checkExit (int status) throws SecurityException
public void checkLink (String libname) throws SecurityException
public void checkListen (int port) throws SecurityException
public void checkPackageAccess (String packageName) throws SecurityException
public void checkPackageDefinition (String packageName) throws SecurityException
public void checkPropertiesAccess () throws SecurityException
public void checkPropertyAccess (String key) throws SecurityException
public void checkRead (FileDescriptor fd) throws SecurityException
public void checkRead (String file) throws SecurityException
public void checkRead (String file, Object context) throws SecurityException
public void checkSetFactory () throws SecurityException
public boolean checkTopLevelWindow () throws SecurityException
public void checkWrite (FileDescriptor fd) throws SecurityException
public void checkWrite (String file) throws SecurityException
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 1

Les outils Java

Gestion de collections de données


Gestion des dates
Génération de nombres aléatoires
Autres classes d'outils

Gestion de collections de données

Le package java.util rassemble des classes d'utilitaires dont les plus intéressantes permettent de gérer
les collections de données (classes Vector, Stack, Dictionary, Hashtable, BitSet et interface
Enumeration).

L'interface java.util.Enumeration

Cette interface est implémentée par les classes désirant pouvoir faire une énumération des objets
mémorisés par une autre classe, comme par exemple la classe Vector. Les méthodes de cette interface
sont généralement utilisées dans une boucle while, comme dans l'exemple suivant (à recopier dans un
fichier EssaiEnumeration.java que vous compilez avec l'instruction javac EssaiEnumeration.java pour
ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction java EssaiEnumeration) :

import java.util.*;

// Classe mémorisant des chaînes de caractères


class CollectionChaines
{
String [ ] tabChaines;
int nbreChaines = 0;

CollectionChaines (int max)


{
tabChaines = new String [max];
}

public void ajouterChaine (String chaine)


{
tabChaines [nbreChaines++] = chaine;
}

public Enumeration chaînes ()


{
return new EnumerationChaines (this);
}
}

// Classe permettant d'énumérer les chaînes de CollectionChaines


class EnumerationChaines implements Enumeration
{
CollectionChaines collection;
int indice = 0;

public EnumerationChaines (CollectionChaines collection)


{
this.collection = collection;
}

public boolean hasMoreElements ()


{
return indice < collection.nbreChaines;
}

public Object nextElement ()


{
if (indice < collection.nbreChaines)
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 2

return collection.tabChaines [indice++];


else
throw new NoSuchElementException ();
}
}

// Classe d'essai
public class EssaiEnumeration
{
public static void main (String [ ] args)
{
// Création d'une collection de chaînes et ajout de 3 chaînes
CollectionChaines donnees = new CollectionChaines (10);
donnees.ajouterChaine ("Toto");
donnees.ajouterChaine ("Titi");
donnees.ajouterChaine ("Tutu");

// Enumération des éléments de la collection


Enumeration enum = donnees.chaînes ();
while (enum.hasMoreElements ())
System.out.println (enum.nextElement ());
}
}

Méthodes

public boolean hasMoreElements ()

Cette méthode doit renvoyer true s'il reste encore un ou plusieurs éléments à énumérer.

public Object nextElement () throws NoSuchElementException

Cette méthode doit retourner l'élément suivant à énumérer ou déclencher une exception de classe
NoSuchElementException si le dernier élément a déjà été énuméré.

La classe java.util.Vector

Cette classe qui implémente l'interface Cloneable, permet de créer un vecteur. Ce type d'ensemble
permet de mémoriser un ensemble d'objets de classe quelconque dans un tableau de taille variable (ces
éléments peuvent être éventuellement égal à null). Comme pour les tableaux, l'accès aux éléments se
fait par un indice. La classe Vector comporte de nombreuses méthodes qui permettent d'ajouter,
d'insérer, de supprimer ou de rechercher des éléments. Toutes les méthodes de Vector sont final sauf
clone ().

Variables

protected Object [ ] elementData


protected int elementCount
protected int capacityIncrement

Ces variables correspondent aux éléments mémorisés, au nombre d'éléments mémorisés et à l'incrément
à appliquer au tableau elementData quand il est rempli. Ces classes étant protected ne sont accessibles
que dans les classes dérivées de Vector.

Constructeurs

public Vector ()
public Vector (int initialCapacity)
public Vector (int initialCapacity, int capacityIncrement)

Ces constructeurs permettent de créer un vecteur de capacité initiale initialCapacity (égal à 10 par
défaut) et d'incrément capacityIncrement (égal à 0 par défaut). Si l'incrément est nul, la taille du tableau
mémorisant les éléments du vecteur sera doublée à chaque fois que ce tableau a besoin d'être agrandi.

Méthodes

public final synchronized void addElement (Object obj)

Ajoute l'élément obj en fin de vecteur. Si le tableau du vecteur est trop petit, il est automatiquement
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 3

agrandi.

public final synchronized void insertElementAt (Object obj, int index)


throws IndexOutOfBoundsException

Insère l'élément obj à l'indice index. Si le tableau du vecteur est trop petit, il est automatiquement
agrandi.

public final synchronized Object elementAt (int index)


throws IndexOutOfBoundsException
public final synchronized void setElementAt (Object obj, int index)
throws IndexOutOfBoundsException

Ces méthodes permettent d'interroger ou de modifier l'élément mémorisé à l'indice index. Si index est
plus grand que le nombre d'éléments du vecteur, une exception IndexOutOfBoundsException est
déclenchée.

public final synchronized boolean removeElement (Object obj)


public final synchronized void removeElementAt (int index)
throws IndexOutOfBoundsException
public final synchronized void removeAllElements ()

Ces méthodes permettent de retirer du vecteur, soit l'élément obj, soit l'élément mémorisé à l'indice
index, soit tous les éléments mémorisés. Les deux premières méthodes décalent les éléments qui suivent
l'élément retiré du tableau du vecteur à un indice inférieur.

public final boolean isEmpty ()

Renvoie true si le vecteur est vide.

public final int size ()

Renvoie le nombre d'éléments mémorisés dans le vecteur.

public final synchronized void setSize (int newSize)

Modifie la taille du vecteur. Si newSize est plus petit que le nombre d'éléments courant du vecteur, les
derniers éléments sont perdus, sinon un ensemble d'éléments égaux à null sont ajoutés pour atteindre la
taille newSize.

public final int capacity ()


public final synchronized void ensureCapacity (int minCapacity)

Ces méthodes permettent d'interroger ou de modifier la capacité courante du vecteur. La capacité du


vecteur est le nombre maximum que son tableau peut contenir avant que celui-ci est besoin d'être
agrandi.

public final synchronized void trimToSize ()

Réduit la capacité du tableau du vecteur aux nombres d'éléments mémorisés par le vecteur.

public final synchronized Object firstElement ()


throws NoSuchElementException
public final synchronized Object lastElement ()
throws NoSuchElementException

Ces méthodes renvoient le premier ou le dernier élément mémorisé par le vecteur.

public final synchronized void copyInto (Object array [ ])


throws IndexOutOfBoundsException

Recopie dans le tableau array l'ensemble des éléments mémorisés par le vecteur. Si array est top petit,
une exception IndexOutOfBoundsException est déclenchée.

public final synchronized Enumeration elements ()

Permet d'énumérer les éléments du vecteur.


vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 4

public final boolean contains (Object elem)

Renvoie true si le vecteur contient un élément égal à elem. La méthode equals () de la classe de l'objet
elem est utilisée pour comparer les objets.

public final int indexOf (Object elem)


public final synchronized int indexOf (Object elem, int index)
throws IndexOutOfBoundsException
public final int lastIndexOf (Object elem)
public final synchronized int lastIndexOf (Object elem, int index)
throws IndexOutOfBoundsException

Ces méthodes renvoient l'indice du premier ou du dernier élément égal à elem, ou -1 si elem n'est pas
trouvé. La méthode equals () de la classe de l'objet elem est utilisée pour comparer les objets. index
permet d'éventuellement spécifier le premier indice à partir duquel commencer la recherche.

public synchronized Object clone ()

Renvoie un clone du vecteur. Les éléments du vecteur ne sont pas clonés eux-mêmes. Cette méthode
outrepasse la méthode clone () de la classe Object.

public final synchronized String toString ()

Cette méthode outrepasse la méthode toString () de la classe Object, pour renvoyer une chaîne de
caractères décrivant la liste des éléments mémorisés par le vecteur.

Exemples

Application ConcatenationFichiers .
Applet PaperBoardClient .

La classe java.util.Stack

Cette classe qui dérive de Vector permet de créer des piles, où vous pouvez empiler un objet avec la
méthode push (), retirer l'élément en haut de la pile avec pop (), ou consulter sans le retirer l'élément
en haut de la pile avec peek ().

Méthodes

public Object push (Object item)


public Object pop () throws EmptyStackException
public Object peek () throws EmptyStackException
public boolean empty ()
public int search (Object o)

La classe java.util.Dictionary

Cette classe abstract permet de créer un dictionnaire représenté par un ensemble d'entrées associant un
élément et une clé. Chaque clé du dictionnaire est unique et est associé au plus à un élément, mais un
même élément peut avoir plusieurs clés d'accès. Les éléments et les clés devant être de la classe Object,
le cast de références permet donc d'utiliser n'importe quel type de classe pour les clés et les éléments
(chaînes de caractères, classes d'emballage des nombres ou d'autres classes).
Un dictionnaire peut être comparé à un tableau : Dans un tableau tab, vous mémorisez un ensemble
d'éléments accessible grâce à un indice entier i, par l'expression tab [i]. Il est possible de mémoriser
plusieurs fois le même objet dans tab à des indices différents, mais par contre chaque indice i est unique
et vous permet d'accéder aux différents éléments du tableau grâce à tab [i]. Dans un dictionnaire dict,
vous mémorisez de la même manière des éléments auquel vous accédez grâce à une clé plutôt que par
un indice entier. Cette clé peut être de n'importe quelle classe, ce qui permet de mémoriser les éléments
d'une manière plus riche qu'avec un simple indice entier. Pour faire un parallèle entre l'utilisation d'un
tableau tab et d'un dictionnaire dict, l'expression tab [i] = element a pour équivalent dict.put (cle,
element) et l'expression element = tab [i] a pour équivalent element = dict.get (cle).
Voici toutes les méthodes que doit implémenter une classe dérivant de Dictionary, pour pouvoir être
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 5

instanciée (comme la classe Hashtable) :

Constructeur

public Dictionary ()

Méthodes

abstract public int size ()

size () doit renvoyer le nombre d'entrées dans le dictionnaire. Chaque clé devant être unique ce
nombre est égal au nombre de clés.

abstract public boolean isEmpty ()

isEmpty () renvoie true si le dictionnaire ne contient aucune entrée.

abstract public Object get (Object key)

Cette méthode doit renvoyer l'élément associé à la clé key, ou null si la clé key n'existe pas.

abstract public Object put (Object key, Object element)

Cette méthode doit ajouter dans le dictionnaire une entrée associant la clé key avec l'élément element. Si
une entrée avec une clé égale à key existe déjà (par la méthode equals ()), l'élément de cette entrée est
renvoyé par put () après avoir été remplacé par element. Sinon put () renvoie null, après avoir ajouté
une nouvelle entrée.

abstract public Object remove (Object key)

Cette méthode doit supprimer du dictionnaire l'entrée ayant comme clé key. Si une entrée avec une clé
égale à key existe déjà (par la méthode equals ()), l'élément de cette entrée est renvoyé par remove ()
après que cette entrée ait été supprimée. Sinon, remove () renvoie null.

abstract public Enumeration keys ()


abstract public Enumeration elements ()

Ces méthodes doivent renvoyer un objet d'une classe implémentant Enumeration permettant d'énumérer
les clés et les éléments du dictionnaire respectivement.

La classe java.util.Hashtable

Cette classe implémente l'interface Cloneable et dérive de la classe Dictionary dont elle implémente
toutes les méthodes, en y ajoutant certaines méthodes. Les tables de hash utilise la valeur que retourne
la méthode hashCode () des clés, pour organiser le rangement des entrées de la table afin que get ()
fonctionne avec efficacité. Les objets utilisés comme clés dans une table de hash devraient avoir leur
classe qui outrepassent les méthodes equals () et hashCode () pour un fonctionnement correct (voir la
classe Object).

Constructeurs

public Hashtable (int initialCapacity, float loadFactor)

Crée une table de hash avec une capacité initiale de initialCapacity entrées. La table est réorganisée
avec la méthode rehash (), à chaque fois que le nombre d'entrées atteint loadFactor multiplié par la
capacité de la table (0 < loadFactor < 1) . Plus loadFactor est petit, plus souvent la table sera
réorganisée, mais attention cette opération prend du temps ! Si c'est possible, Il vaut mieux prévoir une
table de hash avec une capacité initiale proche du nombre d'éléments à mémoriser dans la table, pour
éviter que la table ait trop souvent à se réorganiser.

public Hashtable (int initialCapacity)


public Hashtable ()

Méthodes
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 6

public int size ()


public boolean isEmpty ()
public synchronized Object get (Object key)
public synchronized Object put (Object key, Object element)
public synchronized Object remove (Object key)
public synchronized Enumeration keys ()
public synchronized Enumeration elements ()

Ces méthodes outrepassent celles de la classe Dictionary. Pour plus d'informations, consultez la classe
Dictionary.

public synchronized boolean contains (Object element)


public synchronized boolean containsKey (Object key)

Ces méthodes renvoient true si respectivement un élément ou une clé existe dans la table.

public synchronized void clear ()

clear () supprime toutes les entrées (clés et éléments) de la table.

protected void rehash ()

Cette méthode agrandit et réorganise la table pour plus d'efficacité.

public synchronized Object clone ()

Renvoie un clone de la table de hash. Cette méthode outrepasse la méthode clone () de la classe
Object.

public synchronized final String toString ()

Cette méthode outrepasse la méthode toString () de la classe Object, pour renvoyer une chaîne de
caractères décrivant l'ensemble des entrées (clé + élément) de la table.

La classe java.util.Properties

Cette classe dérivant de Hashtable désigne une table de hash ne pouvant mémoriser que des chaînes de
caractères et comportant des fonctionnalités de sauvegarde/lecture dans un fichier.
Cette classe ressemble un peu à la gestion des fichiers profile Windows (.INI ), et la classe System
l'utilise pour mémoriser les propriétés associés à un programme Java.

Variable

protected Properties defaults

Constructeurs

public Properties ()
public Properties (Properties defaults)

Méthodes

public String getProperty (String property)


public String getProperty (String property, String defaultValue)

Ces méthodes renvoient la propriété de nom property, ou defaultValue si cette propriété n'est pas
définie.

public Enumeration propertyNames ()

Permet d'énumérer l'ensemble des noms de propriétés.

public void list (PrintStream out)

Ecrit sur le flux de données out la liste des propriétés et de leur valeur.
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 7

public synchronized void load (InputStream in) throws IOException


public synchronized void save (OutputStream out, String header)

Ces méthodes permettent d'écrire ou de lire un ensemble de propriétés à patir des flux de données in ou
out. Le format des données est ensemble de couples property=value.

La classe java.util.BitSet

Cette classe final qui implémente l'interface Cloneable permet de manipuler aisément un ensemble de
bits. Les objets de cette classe ont leur nombre de bits mémorisés qui croit automatiquement à l'appel
des méthodes set () et clear (). Les méthodes and (), or () et xor () permettent d'effectuer les
opérations ET, OU et OU Exclusif bit à bit sur les ensembles de bits des objets de cette classe.

Constructeurs

public BitSet ()
public BitSet (int nbits)

Les nbits bits qu'une instance de la classe BitSet mémorise sont stockés dans un tableau dont les
éléments sont de type long (64 bits).

Méthodes

public boolean get (int bitIndex)


public void set (int bitIndex)
public void clear (int bitIndex)
public void and (BitSet set)
public void or (BitSet set)
public void xor (BitSet set)
public int size ()

public boolean equals (Object obj)


public int hashCode ()
public Object clone ()
public String toString ()

Gestion des dates

La classe java.util.Date

Cette classe permet de manipuler les dates. Les objets de cette classe représente un moment donné à la
seconde près. La classe Date fournit des méthodes permettant d'obtenir ou de modifier l'année, le mois,
le jour, l'heure, la minute ou la seconde d'une date et de comparer des dates.

L'année est un int égal à 0 pour l'année 1900.


Le mois est un int allant de 0 pour le mois de Janvier à 11 pour le mois de décembre.
Le jour du mois est un int allant de 1 à 31.
L'heure est un int allant de 0 à 23.
La minute est un int allant de 0 à 59.
La seconde est un int allant de 0 à 59.

Les objets de la classe Date ne sont pas des horloges. A chaque fois que vous voulez obtenir le temps
courant, utilisez la méthode currentTimeMillis () de la classe System, ou créez un nouvel objet de
classe Date avec le constructeur par défaut.

Constructeurs

public Date
()
public Date
(long time)
public Date
(int year, int month, int date)
public Date
(int year, int month, int date, int hours, int minutes)
public Date
(int year, int month, int date, int hours,
int minutes, int seconds)
public Date (String s) throws IllegalArgumentException
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 8

Méthodes

public int getYear ()


public void setYear (int year)
public int getMonth ()
public void setMonth (int month)
public int getDate ()
public void setDate (int date)
public int getHours ()
public void setHours (int hours)
public int getMinutes ()
public void setMinutes (int minutes)
public int getSeconds ()
public void setSeconds (int seconds)

Ces méthodes permettent d'interroger ou de modifier l'année, le mois, le jour du mois, l'heure, la minute
ou la seconde d'une date.

public int getDay ()

Renvoie le jour de la semaine sachant que le nombre 0 correspond à dimanche.

public long getTime ()


public void setTime (long time)

Ces méthodes permettent d'interroger ou de modifier le temps en millisecondes

public boolean before (Date when)


public boolean after (Date when)

Ces méthodes renvoient true si une date est inférieure ou supérieure à la date when.

public int getTimezoneOffset ()


public static long UTC (int year, int month, int date,
int hours, int minutes, int seconds)

public static long parse (String s) throws IllegalArgumentException

Renvoie le temps à partir d'une chaîne de caractère respectant le format IETF.

public String toLocaleString ()

Renvoie une chaîne de caractères décrivant la date courante respectant la notation locale.

public String toGMTString ()

Renvoie une chaîne de caractères décrivant la date courante respectant la notation GMT.

public boolean equals (Object obj)


public int hashCode ()
public String toString ()

Exemples

Applets TraitementTexte , MiseAJourHorloge et Horloge .

Génération de nombres aléatoires

La classe java.util.Random

Les méthodes de la classe Random permettent de générer des nombres aléatoires, comme la méthode
random () de la classe java.lang.Math. Ces méthodes peuvent générer des nombres entiers, décimaux
(entre 0. inclus et 1. exclus) ou respectant la courbe de Gauss (entre -1. et 1.).

Constructeurs

public Random ()
public Random (long seed)
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 9

Le second constructeur permet de créer un générateur dont on fournit la base de départ. Pour une même
base, la série de nombres aléatoires générées sera toujours la même à chaque exécution d'un programme.
Le premier constructeur crée un générateur dont la base est égale au temps courant. Ce temps étant
toujours différent d'une exécution à l'autre, chaque série sera différente à chaque exécution.

Méthodes

public synchronized void setSeed (long seed)


public int nextInt ()
public long nextLong ()
public float nextFloat ()
public double nextDouble ()
public synchronized double nextGaussian ()

Exemples

Applets ObservateurCalcul , DrawIt et ImageNoirEtBlanc .

Autres classes d'outils

La classe java.util.StringTokenizer

Cette classe permet d'énumérer à partir d'une chaîne de caractères str un ensemble de sous-chaînes
séparées par des délimiteurs (voir aussi la classe java.io.StreamTokenizer).

Constructeurs

public StringTokenizer (String str, String delim, boolean returnTokens)


public StringTokenizer (String str, String delim)
public StringTokenizer (String str)

Permet de spécifier la chaîne str dans laquelle on recherche des sous-chaînes. Les sous-chaînes sont
séparées par des délimiteurs qui peuvent être n'importe quel caractère de delim. Si returnTokens est
true, l'énumération rendra les sous-chaînes et les délimiteurs. Par défaut, le délimiteur est un espace et
returnTokens est égal à false.

Méthodes

public boolean hasMoreElements ()


public boolean hasMoreTokens ()

Ces deux méthodes renvoient vraies si la chaîne str a encore des sous-chaînes à énumérer.

public Object nextElement () throws NoSuchElementException


public String nextToken () throws NoSuchElementException
public String nextToken (String delim) throws NoSuchElementException

Ces trois méthodes renvoient la sous-chaîne suivante de str (ou le délimiteur si returnTokens est true),
et avance la position de recherche dans str au caractère suivant la sous-chaîne renvoyée. La troisième
méthode permet de remplacer les délimiteurs recherchés.

public int countTokens ()

Renvoie le nombre de sous-chaînes restant à énumérer.

L'interface java.util.Observer

Toute classe qui implémente cette interface peut instancier un observateur d'un objet observé . Cette objet
observé dont la classe est Observable ou ses dérivées, peut prévenir tous ses observateurs d'un
changement en appelant automatiquement la méthode update () de chacun d'eux. Voir la classe
Observable.

Méthode
vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 10

public void update (Observable o, Object arg)

Exemple

Applet ObservateurCalcul .

La classe java.util.Observable

Cette classe permet de créer des objets observés par des observateurs , ces derniers étant des objets dont la
classe implémente l'interface Observer. Quand, à la suite d'un changement, un objet observé appelle la
méthode notifyObservers (), chacun de ses observateurs voit sa méthode update () appelée.

Constructeur

public Observable ()

Méthodes

public synchronized void addObserver (Observer o)


public synchronized void deleteObserver (Observer o)
public synchronized void deleteObservers ()
public synchronized int countObservers ()

Ces méthodes permettent d'ajouter, de supprimer ou de compter les observateurs d'un objet.

public void notifyObservers ()


public synchronized void notifyObservers (Object arg)

Ces méthodes provoqueront l'appel de la méthode update () de chacun des observateurs, si l'objet a
appelé setChanged () pour spécifier que l'objet a été modifié.

protected synchronized void setChanged ()


protected synchronized void clearChanged ()
public synchronized boolean hasChanged ()

Ces méthodes permettent de valider, d'annuler ou d'interroger un flag indiquant si l'objet a changé.

Voici un exemple illustrant l'utilisation de la classe Observable et de l'interface Observer, avec une
applet affichant des sinusoïdes, dérivée de l'exemple du chapitre sur les threads :

et le programme Java correspondant (à copier dans un fichier dénommé ObservateurCalcul.java et invoqué


à partir d'un fichier HTML) :

import java.awt.*;
import java.applet.Applet;
import java.util.*;

public class ObservateurCalcul extends Applet implements Observer


{
CalculateurObservable calculateur;

public void start ()


{
// Création d'un calculateur que l'applet observe
(calculateur = new CalculateurObservable (this)).addObserver (this);
}

public void stop ()


{
calculateur.stop ();
}

public void update (Observable calculateur, Object arg)


vendredi 11 février 2000 Du C/C++ à Java : Les outils Java Page: 11

{
((CalculateurObservable)calculateur).dessinerCourbe (getGraphics ());
}
}

class CalculateurObservable extends Observable implements Runnable


{
private int [ ] courbe;
private Component fenetre;
private Random generateur = new Random ();
private Thread threadCalculateur;

public CalculateurObservable (Component fenetre)


{
this.fenetre = fenetre;
// Démarrage d'un thread de calcul
(threadCalculateur = new Thread (this)).start ();
}

public void stop ()


{
threadCalculateur.stop ();
}

public void calculerCourbe ()


{
courbe = new int [fenetre.size ().width];
// Calcul d'une sinusoïde avec fréquence aléatoire
double pasCalcul = 2 * Math.PI / courbe.length / Math.random ();
for (int i = 0; i < courbe.length; i++)
courbe [i] = (int)( (Math.sin (i * pasCalcul) + 1)
* fenetre.size ().height / 2);
// Nouveau calcul
setChanged();
}

public void dessinerCourbe (Graphics gc)


{
gc.setColor (Color.white);
gc.fillRect (0, 0, fenetre.size ().width, fenetre.size ().height);
// Création d'une couleur aléatoire
gc.setColor (new Color (generateur.nextFloat (),
generateur.nextFloat (),
generateur.nextFloat ()));
if (courbe != null)
// Dessin de la courbe en reliant les points un à un
for (int i = 1; i < courbe.length; i++)
gc.drawLine (i - 1, courbe [i - 1], i, courbe [i]);
}

public void run ()


{
try
{
while (threadCalculateur.isAlive ())
{
// Calculer une courbe puis prévenir d'un nouveau calcul
calculerCourbe ();
notifyObservers ();

Thread.sleep (200);
}
}
catch (InterruptedException e) { }
}
}
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 1

La gestion des fichiers


et des flux de données

Gestion des entrées-sorties


Manipulation des fichiers
Gestion des flux de données
Accès à un flux de données en lecture
Accès à un flux de données en écriture
Gestion de l'accès aléatoire aux fichiers

Ce chapitre décrit le package java.io de Java 1.0 qui rassemble les classes permettant de manipuler les
fichiers et de gérer les entrées-sorties.

Gestion des entrées-sorties

En observant la hiérarchie des classes de Java, vous serez peut-être étonné par le nombre de classes
dévolues à la gestion des entrées-sorties (package java.io), et particulièrement celles de gestion des flux
de données (stream ). Ne vous inquiétez pas : une fois que vous aurez cerner le principe de
fonctionnement de la gestion des flux de données, vous verrez que toutes les classes dérivant des
classes InputStream et OutputStream s'utilisent de manière intuitive.

Mode d'accès aux données

Comme dans la plupart des langages informatiques, Java permet de manipuler les fichiers soit en accès
par flux de données (Stream ), soit en accès aléatoire (Random access ) :

Le mode d'accès par flux de données permet d'accéder aux données sous la forme d'un flux séquentiel.
Pour faire un parallèle, imaginez un lecteur de CD sans les touches d'avance et de retour rapides >> et << :
Vous pouvez accéder à un morceau de musique grâce aux touches >>| et |<< et l'écouter, mais quand le
morceau est en train d'être lu, vous ne pouvez que l'écouter de manière continu sans revenir en arrière ni
sauter une partie.
Similairement quand vous accédez à un flux de données, une fois que vous commencez à accéder aux
données, vous devez traiter les données au fur et à mesure qu'elles vous sont délivrées.

Ce système permet d'accéder à toute sorte de sources de données comme :


un fichier (classes FileInputStream et FileOutputStream)
une chaîne de caractères (classe StringBufferInputStream)
un tableau d'octets (classes ByteArrayInputStream et ByteArrayOutputStream)
un pipeline (classes PipedInputStream et PipedOutputStream)
l'entrée et les sorties standards (variables System.in, System.out et System.err ou FileDescriptor.in,
FileDescriptor.out et FileDescriptor.err)
une URL
un socket.

Quel est l'intérêt d'utiliser un flux de données pour accéder à une chaîne de caractères ou à un tableau
d'octets, alors qu'il sera impossible d'accéder aux données dans un ordre quelconque ?
La programmation orientée objet utilisée par Java permet de manipuler les données entrantes ou sortantes avec
les mêmes classes, quelque soit le type de source de données : Pour lire les données d'un flux vous utilisez
les méthodes des classes InputStream, FilterInputStream ou de ses classes dérivées, et pour écrire dans un
flux de données, vous utilisez les méthodes des classes OutputStream, FilterOutputStream ou de ses classes
dérivées.
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 2

Grâce à ceci, si vous créez une méthode qui effectue des traitements sur des données provenant d'un flux,
elle acceptera n'importe quelle source de données, qu'elle soit en mémoire, dans un fichier ou sur Internet.

Contrairement au C où il existe des fonctions différentes suivant le type de source de données (comme
scanf (), fscanf (), sscanf ()), Java permet d'accéder aux différents types de flux de données par les
mêmes méthodes.
Le mode d'accès aléatoire permet d'accéder aux données dans n'importe quel ordre. Vous pouvez à tout
moment aller en avant ou en arrière pour lire ou écrire une partie des données. Ce mode d'accès est
comparable à celui utilisé pour la mémoire vive (Random Access Memory elle aussi !). Du fait que certains types
de source de données, comme les pipelines ou les URL ne peuvent supporter ce mode d'accès, son usage est
plus spécifique et Java fournit uniquement la classe RandomAccessFile qui permet d'accéder à une position
quelconque dans un fichier.

En C, la différence entre les deux types d'accès par flux de données (fonctions f... () de stdio.h ) et à
accès aléatoire (open (), read (), ...) n'apparaît pas toujours de manière évidente. En effet, les
méthodes ftell () et fseek () permettent de se déplacer à une position quelconque dans un fichier,
ce qui rend possible l'accès aléatoire !
En Java, la différence est franche : Les classes InputStream et OutputStream et leurs classes dérivées
ne permettent d'accéder que sous forme de flux de données continus (avec éventuellement un retour
en arrière quand c'est possible), au contenu d'un fichier ou d'une source de donnée d'un autre type, et
la classe RandomAccessFile permet d'accéder aléatoirement au contenu d'un fichier.

Gestion de l'accès aux données avec les exceptions

Les exceptions en Java permettent de programmer de manière plus simple et surtout plus maintenable une
séquence d'instructions devant effectuer des entrées/sorties. Ce type de programme effectue habituellement
un grand nombre de contrôles puisqu'il faut vérifier à chaque accès à un flux de données ou un fichier si tout
s'est bien déroulé. Une programmation n'utilisant pas les exceptions est donc truffée de contrôles après
chaque instruction, ce qui en réduit la lisibilité.
En Java, la plupart des méthodes d'entrée/sortie déclenche une exception de classe IOException ou de ses
dérivées EOFException, FileNotFoundException, InterruptedIOException ou UTFDataFormatException. Par
exemple, une méthode qui va effectuer une lecture à l'intérieur d'un fichier respectera généralement le schéma
suivant :

import java.io.*;

class Classe1
{
void lireFichier (String fichier)
{
try
{
// Suite d'instructions accédant au fichier et
// ne s'occupant pas de la gestion des erreurs

// Tentative d'ouvrir un fichier

// Lecture dans le fichier


}
catch (FileNotFoundException e)
{
// Exception déclenchée si le fichier n'existe pas
}
catch (EOFException e)
{
// Exception déclenchée si la fin du fichier est atteinte
}
catch (IOException e)
{
// Exception déclenchée si un autre problème survient
// pendant l'accès au fichier
}
finally
{
// Le bloc finally est toujours exécuté ce qui permet d'être sûr
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 3

// que la fermeture du fichier sera effectuée


try
{
// Fermeture du fichier si le fichier a été ouvert
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant la fermeture
}
}
}
}

Manipulation des fichiers

Java permet de manipuler les fichiers situés sur le système local (par opposition à ceux auquel on accède
via une URL), grâce à l'interface FilenameFilter et les classes File et FileDescriptor. On peut ajouter
à cette liste la classe java.awt.FileDialog, qui permet de choisir interactivement un fichier avec une
boite de dialogue.

L'interface java.io.FilenameFilter

Cette interface est utilisée pour permettre d'accepter ou de rejeter un fichier issu d'une liste. Une
instance d'une classe implémentant cette interface est requise par les méthodes list () de la classe File
et setFilenameFilter () de la classe FileDialog.

Méthode

public boolean accept (File dir, String name)

Cette méthode doit renvoyer true si le fichier name se trouvant dans le répertoire dir est accepté.

La classe java.io.File

Cette classe est utilisée pour représenter un chemin d'accès au système de fichiers local et effectuer des
opérations sur celui-ci (autorisation d'accès en lecture/écriture, liste des fichiers d'un répertoire,...). Ce
chemin d'accès peut désigner un fichier ou un répertoire.
Les méthodes de cette classe peuvent éventuellement déclenchée une exception SecurityException, si
le gestionnaire de sécurité actif leur interdit d'être exécutée. Les applets fonctionnant dans un navigateur
empêchent généralement notamment tout accès au système de fichiers local donc n'imaginez pas une
applet qui puisse accéder aux fichiers du système dans ce contexte.
La description des chemins d'accès (séparateurs, description des disques,...) utilise les conventions du
système sur lequel est exécutée la Machine Virtuelle.

Variables

public static final String separator


public static final char separatorChar

La chaîne ou le caractère utilisé comme séparateur de fichier (égal généralement à / ou \).

public static final String pathSeparator


public static final char pathSeparatorChar

La chaîne ou le caractère utilisé comme séparateur de plusieurs chemins d'accès (égal généralement à ;).

Constructeurs

public File (String path)

Construit une instance de la classe File, à partir d'un chemin d'accès path pouvant être absolu ou relatif
au répertoire courant. path peut représenter un répertoire ou un fichier suivant l'utilisation que vous
voulez faire de cette nouvelle instance.

public File (String dir, String name)


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 4

public File (File dir, String name)

Ces constructeurs permettent de créer une instance de la classe File, à partir d'un répertoire dir (chemin
d'accès absolu ou relatif au répertoire courant), et d'un nom de fichier ou de répertoire name accessible à
partir du répertoire dir.

Méthodes

public String getName ()

Renvoie le nom du fichier ou du répertoire d'un chemin d'accès.

public String getPath ()

Renvoie le chemin d'accès d'un objet de classe File.

public String getAbsolutePath ()

Renvoie le chemin d'accès absolu.

public String getParent ()

Renvoie le chemin d'accès du répertoire parent au chemin d'accès de l'objet de classe File, ou null s'il
n'existe pas.

public boolean exists () throws SecurityException


public boolean isFile () throws SecurityException
public boolean isDirectory () throws SecurityException
public boolean isAbsolute ()

Ces méthodes renvoient true, si le chemin d'accès existe, s'il correspond à un fichier ou à un répertoire,
ou si le chemin d'accès est absolu.

public boolean canRead () throws SecurityException


public boolean canWrite () throws SecurityException

Ces méthodes renvoient true, si le chemin d'accès est accessible en lecture ou en écriture.

public long lastModified () throws SecurityException

Renvoie la date de dernière modification du fichier. Cette date n'étant pas exprimé dans la même échelle
que celle utilisée par la classe Date, elle ne peut être utilisée que pour comparer des dates de différents
fichiers.

public long length () throws SecurityException

Renvoie la taille du chemin d'accès représentant un fichier.

public boolean delete () throws SecurityException


public boolean renameTo (File dest) throws SecurityException

Ces méthodes permettent de supprimer ou de renommer un chemin d'accès, et renvoient true si


l'opération s'est effectuée correctement.

public boolean mkdir () throws SecurityException


public boolean mkdirs () throws SecurityException

Ces méthodes permettent de créer le répertoire ou les répertoires représentés par le chemin d'accès et
renvoient true si l'opération s'est effectuée correctement.

public String [ ] list () throws SecurityException


public String [ ] list (FilenameFilter filter) throws SecurityException

Ces méthodes renvoient un tableau de chaînes de caractères dont les éléments sont les fichiers et les
répertoires contenus dans le répertoire représenté par un chemin d'accès. Les répertoires . et .. sont
exclus de cette liste.
La seconde méthode permet d'appliquer le filtre filter pour ne récupérer que les fichiers et les
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 5

répertoires autorisés par la méthode accept () de la classe de l'objet filter implémentant l'interface
FilenameFilter.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
chemin d'accès à un objet ou renvoyer une chaîne de caractères égale au chemin d'accès.

La classe java.io.FileDescriptor

Cette classe final représente un descripteur de fichier. Une instance de cette classe permet de
manipuler un fichier ouvert ou l'entrée et les sorties standards.
Les méthodes getFD () des classes FileInputStream, FileOutputStream et RandomAccessFile permettent
d'obtenir une instance de la classe FileDescriptor et les variables static in, out et err permettant de
manipuler l'entrée standard, la sortie standard et la sortie standard d'erreurs sont de cette classe.

Variables

public static final FileDescriptor in


public static final FileDescriptor out
public static final FileDescriptor err

Ces variables représentent l'entrée standard, la sortie standard ou la sortie standard des erreurs. Les
variables de même nom de la classe System sont construites à partir de ces variables.
Vous pouvez les utiliser pour accéder à l'entrée et les sorties standards avec d'autres classes de gestion
des flux de données.

Constructeur

public FileDescriptor ()

Méthodes

public boolean valid ()

Gestion des flux de données

En Java, les classes dévolues aux flux de données permettent d'accéder aux données soit en lecture
grâce aux classes qui dérivent de la classe abstract InputStream, soit en écriture grâce aux classes qui
dérivent de la classe abstract OutputStream.

A chaque type de flux de données (fichier, chaîne de caractères, tableau d'octets, pipeline) correspond
une classe permettant de le manipuler, mais il existe aussi un ensemble de classes de filtres dérivées des
classes abstract FilterInputStream et FilterOutputStream qui peuvent s'utiliser ensemble pour
simplifier ou optimiser l'accès aux flux.

Accès à un flux de données en lecture

L'accès en lecture à un flux de données s'effectue grâce aux classes qui dérivent de la classe
InputStream. Ces classes se divisent en deux catégories :

Les classes qui permettent de se lier à un type de source de données spécifique :


la classe FileInputStream pour accéder en lecture à un fichier
la classe StringBufferInputStream pour parcourir une chaîne de caractères sous forme de flux de
données
la classe ByteArrayInputStream pour parcourir un tableau d'octets sous forme de flux de données
la classe PipedInputStream pour accéder à un flux de données sous forme de pipeline
la classe SequenceInputStream pour accéder aux données de plusieurs flux de données les uns après les
autres.
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 6

La création d'une instance d'une de ces classes permet d'accéder sous forme d'un flux à la source de données
à laquelle elle est dédiée. En particulier, la création d'un objet de classe FileInputStream permet d'ouvrir un
fichier et d'en lire le contenu.
En regardant les constructeurs de ces classes vous verrez qu'ils prennent en paramètre une instance de la
classe représentant le type de sources de données qu'il manipule, comme le résume le tableau suivant :

Classes Paramètres des constructeurs


Chemin d'accès à un fichier (classe String)
FileInputStream Fichier décrit par une instance de la classe File
Descripteur de fichier (classe FileDescriptor)
StringBufferInputStream Chaîne de caractères (classe String)
ByteArrayInputStream Tableau d'octets (de type byte [])

Aucun pour créer un pipeline d'entrée


PipedInputStream
Sortie d'un pipeline (classe PipedOutputStream)
Une énumération de flux de données de classe InputStream
SequenceInputStream
Deux flux de données de classe InputStream

Une fois que vous avez créé une instance d'une de ces classes, vous pouvez appeler les méthodes de la classe
InputStream, puisque toutes ces classes en héritent, comme dans l'application suivante qui permet d'écrire sur
la sortie standard les n premiers caractères d'un fichier. Recopiez-la dans un fichier LectureFichier.java , que
vous compilez avec l'instruction javac LectureFichier.java pour ensuite l'exécuter avec java ou Java
Runner , grâce à l'instruction java LectureFichier nomFichier nombreCaracteres (par exemple java
LectureFichier LectureFichier.java 100) :

import java.io.*;

public class LectureFichier


{
// Méthode lancée à l'appel de l'instruction :
// java LectureFichier nomFichier nombreCaracteres
public static void main (String [ ] args)
{
try
{
// Ouverture du fichier passé en paramètre dans la ligne de commande
InputStream fluxFichier = new FileInputStream (args [0]);

// Lecture des n premiers octets du fichier. n est passé en paramètre


byte contenuFichier [ ] = new byte [Integer.parseInt (args [1])];
fluxFichier.read (contenuFichier);

// Ecriture sur la sortie standard des octets lus convertis


// en une chaîne de caractères
System.out.println (new String (contenuFichier, 0));

// Fermeture du fichier
fluxFichier.close ();
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant l'accès au fichier
System.out.println (e);
}
}
}

L'accès aux trois autres types de sources de données restantes (l'entrée standard, les sockets et un fichier via
une URL) s'effectue différemment :
L'entrée standard est accessible soit directement par la variable static in de la classe System, qui est de
classe InputStream, soit par la variable static in de la classe FileDescriptor. Cette dernière variable
étant elle-même de classe FileDescriptor, la lecture sur l'entrée standard peut démarrer en créant une
instance de classe FileInputStream, avec l'instruction new FileInputStream (FileDescriptor.in)
(l'entrée standard n'est pas disponible sous MacOS).
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 7

L'accès en lecture aux sockets et à un fichier accessible via une URL s'effectue grâce à la méthode
getInputStream () des classes Socket et URLConnection, qui renvoie une instance d'une classe dérivée
de InputStream.

Cette architecture est très pratique : elle permet de créer par exemple des méthodes de traitement qui peuvent
accéder à n'importe quel flux de données via une référence de classe InputStream qui leur est passé en
paramètre. Mais les méthodes de lecture de cette classe paraissent d'un intérêt très limité : elle permettent de
lire un ou plusieurs octets, c'est tout ! C'est là qu'intervient la seconde catégorie de classes dérivant de
InputStream, afin d'enrichir les méthodes de lecture des flux de données.
Les classes de filtre sur un flux de données qui dérivent de la classe FilterInputStream :
La classe BufferedInputStream pour accéder à un flux de données en utilisant une zone mémoire
tampon (buffer )
La classe DataInputStream pour lire dans un flux de données des données d'un des types de base de
Java ou des chaînes de caractères
La classe LineNumberInputStream pour permettre de compter les lignes d'un flux de données
La classe PushBackInputStream pour permettre d'accéder à un flux de données avec la possibilité de
revenir en arrière d'un octet.

Contrairement aux classes dédiées à un type de source de données, les constructeurs de ces classes prennent
tous en paramètre un objet de classe InputStream, qui est le flux de données sur lequel elles font une lecture
avant d'effectuer des traitements supplémentaires. La classe InputStream étant la super classe de toutes les
classes d'accès aux flux de données, vous pouvez donc passer en paramètre une instance de n'importe quelle
classe qui en dérive.
Grâce à ce système vous pouvez utiliser n'importe quel filtre sur une source de données, et même cumuler
les filtres les uns derrière les autres, comme le montre le schéma suivant :

figure 11. Utilisation cumulée de filtres

L'application suivante utilise ce schéma de filtres pour numéroter les lignes lues dans un fichier. Recopiez la
dans un fichier NumerotationLigne.java , que vous compilez avec l'instruction javac NumerotationLigne.java
pour ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction java NumerotationLigne nomFichier
(par exemple java NumerotationLigne NumerotationLigne.java) :

import java.io.*;

public class NumerotationLigne


{
// Méthode lancée à l'appel de l'instruction :
// java LectureFichier nomFichier
public static void main (String [ ] args)
{
try
{
// Ouverture du fichier passé en paramètre dans la ligne de commande
// avec un filtre utilisant un buffer
InputStream fluxFichier = new BufferedInputStream (
new FileInputStream (args [0]));

// Numérotation des lignes


numeroterLigne (fluxFichier);

fluxFichier.close ();
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant l'accès au fichier
System.out.println (e);
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 8

}
}

public static void numeroterLigne (InputStream flux) throws IOException


{
// Création d'un filtre de décompte des lignes
LineNumberInputStream fluxLignes = new LineNumberInputStream (flux);
// Ajout d'un filtre pour lire de manière plus pratique les caractères
DataInputStream fluxLecture = new DataInputStream (fluxLignes);

// Lecture des lignes du flux jusqu'à la fin du flux


String ligne;
for (ligne = fluxLecture.readLine ();
ligne != null;
ligne = fluxLecture.readLine ())
// Ecriture sur la sortie standard de la ligne lue avec son numéro de ligne
System.out.println (String.valueOf (fluxLignes.getLineNumber ())
+ " : " + ligne);
}
}

En poursuivant dans cette logique, il est simple de créer et d'utiliser vos propres filtres sur des flux de
données en dérivant vos nouvelles classes de la classe FilterInputStream ou d'une de ses classes dérivées.

La classe java.io.InputStream

Cette classe abstract est la super classe de toutes les classes qui permettent d'accéder à d'un flux de données
en lecture (voir aussi le paragraphe précédent).

Méthodes

public abstract int read () throws IOException

Lit un octet dans le flux de données. Renvoie -1 si la fin est atteinte.

public int read (byte [ ] tab) throws IOException

Lit tab.length octets dans le tableau tab. Si la fin est atteinte pendant la lecture, tab est remplit avec les
octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est atteinte.

public int read (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Lit length octets dans le tableau tab, en le remplissant à partir de l'indice offset. Si la fin est atteinte pendant
la lecture, tab est remplit avec les octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est atteinte.

public long skip (long n) throws IOException

Saute n octets plus loin dans la lecture du flux de données. Renvoie le nombre d'octets effectivement sautés.

public int available () throws IOException

Renvoie le nombre d'octets que les méthodes read () peuvent actuellement lire sans bloquer le thread
courant.

public void close () throws IOException

Ferme l'accès au flux de données.

public synchronized void mark (int readlimit)

Marque la position courante dans le flux de données, pour qu'un appel à la méthode reset () provoque un
retour en arrière à cette position. readlimit permet de préciser le nombre d'octets maximum que l'on peut lire
avant que la marque soit invalide, c'est-à-dire la taille maximum du retour en arrière qu'il est possible de faire.

public synchronized void reset () throws IOException

Repositionne la position courante dans le flux de données sur la dernière marque positionnée par la méthode
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 9

mark (). Si aucune marque n'a été positionnée ou si la marque est invalide, une exception IOException est
déclenchée.

public boolean markSupported ()

Renvoie true si le flux de données autorise l'utilisation des méthodes mark () et reset ().

Exemples

Applications LectureFichier , NumerotationLigne , ConcatenationFichiers et EchoServeur .

La classe java.io.FileInputStream

Cette classe qui dérive de la classe InputStream permet d'ouvrir et d'accéder à un fichier en lecture, sous
forme de flux de données.

Constructeurs

public FileInputStream (String path)


throws SecurityException, FileNotFoundException
public FileInputStream (File file)
throws SecurityException, FileNotFoundException

Ces constructeurs permettent de créer une instance de classe FileInputStream à partir du chemin d'accès à un
fichier path ou d'une instance de classe File.

public FileInputStream (FileDescriptor fdObj) throws SecurityException

Construit une instance de classe FileInputStream à partir d'un descripteur de fichier fdObj.

Méthodes

public int read () throws IOException


public int read (byte [ ] tab) throws IOException
public int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public long skip (long n) throws IOException
public int available () throws IOException
public void close () throws IOException

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à la lecture d'un fichier.

public final FileDescriptor getFD () throws IOException

Permet d'obtenir un descripteur de fichier.

protected void finalize () throws IOException

Méthode de la classe Object outrepassée pour que le fichier ouvert à la création d'une instance de la classe
FileInputStream soit fermé avant que le Garbage Collector n'intervienne pour détruire cet objet. Le moment
où intervient le Garbage Collector n'étant généralement pas prévisible, il vaut mieux fermer soi-même le
fichier avec la méthode close (), pour éviter d'en bloquer l'accès à d'autres programmes.

Exemples

Applications LectureFichier , NumerotationLigne et ConcatenationFichiers .

La classe java.io.StringBufferInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder à une chaîne de caractères en lecture, sous
forme de flux de données.

Variables

protected String buffer


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 10

protected int pos


protected int count

Constructeur

public StringBufferInputStream (String s)

Construit une instance de classe StringBufferInputStream à partir de la chaîne de caractères s.

Méthodes

public synchronized int read ()


public synchronized int read (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException
public synchronized long skip (long n)
public synchronized int available ()
public synchronized void reset ()

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à une chaîne de
caractères.

Exemple

Applet PaperBoardClient .

La classe java.io.ByteArrayInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder à un tableau d'octets en lecture, sous forme
de flux de données.

Variables

protected byte [ ] buf


protected int pos
protected int count

Constructeurs

public ByteArrayInputStream (byte [ ] buf)

Construit une instance de classe ByteArrayInputStream à partir du tableau d'octets buf.

public ByteArrayInputStream (byte [ ] buf, int offset, int length)

Construit une instance de classe ByteArrayInputStream à partir du tableau d'octets buf, où length octets
seront lus à partir de l'indice offset.

Méthodes

public synchronized int read () throws IndexOutOfBoundsException


public synchronized int read (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException
public synchronized long skip (long n)
public synchronized int available ()
public synchronized void reset ()

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un tableau d'octets.

La classe java.io.PipedInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder à un pipeline en lecture, sous forme de flux
de données.

Constructeurs

public PipedInputStream ()
public PipedInputStream (PipedOutputStream src) throws IOException
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 11

Méthodes

public void connect (PipedOutputStream src) throws IOException

Crée une connexion avec la sortie du pipeline src.

public synchronized int read () throws IOException


public synchronized int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un pipeline.

La classe java.io.SequenceInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder en lecture à un ensemble de flux de
données les uns après les autres.

Constructeurs

public SequenceInputStream (InputStream s1, InputStream s2)

Construit une instance de classe SequenceInputStream à partir des flux de données s1 et s2. Les méthodes
read () de cette classe liront le contenu des flux s1 et s2 l'un à la suite de l'autre, comme s'il forme un seul
flux.

public SequenceInputStream (Enumeration e)

Construit une instance de classe SequenceInputStream à partir d'une énumération de flux de données e. Les
méthodes read () de cette classe liront le contenu de tous les flux énumérés par e, comme s'il forme un seul
flux. Chacun des objets énumérés par e doit donc être de classe InputStream ou d'une de ses classes dérivées.

Méthodes

public int read () throws IOException


public int read (byte [ ] buf, int pos, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un ensemble de flux de
données.

Exemple

Application ConcatenationFichiers .

La classe java.io.FilterInputStream

Cette classe qui dérive de la classe InputStream est la super classe de toutes les classes utilisées pour filtrer la
lecture d'un flux de données. Vous pouvez utiliser les classes BufferedInputStream, DataInputStream,
LineNumberInputStream ou PushBackInputStream qui en dérivent mais aussi créer vos propres classes de filtre
dérivant de cette classe.

Variable

protected InputStream in

Le flux de données sur lequel la lecture est faite. Utilisez cette variable dans vos classes dérivées pour lire
des données sur le flux à filtrer.

Constructeur

protected FilterInputStream (InputStream in)


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 12

Le constructeur de cette classe est protected ce qui vous empêche de l'instancier (cette classe n'a de toute
façon aucun effet sur le flux de données ). Il est utilisé par les classes dérivées.

Méthodes

public int read () throws IOException


public int read (byte [ ] tab) throws IOException
public int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public long skip (long n) throws IOException
public int available () throws IOException
public void close () throws IOException
public synchronized void mark (int readlimit)
public synchronized void reset () throws IOException
public boolean markSupported ()

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un flux de données
filtré en lecture.

Voici un exemple d'une classe de filtre dérivée de FilterInputStream qui a pour effet de mettre en majuscule
toutes les lettres qui sont lus sur un flux de données :

class ToUpperCaseInputStream extends FilterInputStream


{
// Constructeur : appel du constructeur de la super classe
public ToUpperCaseInputStream (InputStream flux)
{
super (flux);
}

public int read () throws IOException


{
// Lecture sur le flux d'un byte
int car = (char)in.read ();

// Si la fin du fichier n'est pas atteinte et si le caractère


// lu est une minuscule renvoyer la majuscule correspondante
if ( car != -1
&& Character.isLowerCase ((char)car))
return Character.toUpperCase ((char)car);
return car;
}

public int read (byte tab [], int offset, int length) throws IOException
{
// Lecture sur le flux : read () renvoie le nombre d'octets lus
int nbreOctetsLus = in.read (tab, offset, length);

// Conversion de toutes les minuscules lues en majuscules


for (int i = 0; i < nbreOctetsLus; i++)
if (Character.isLowerCase ((char)tab [offset + i]))
tab [offset + i] = (byte)Character.toUpperCase ((char)tab [offset + i]);
return nbreOctetsLus;
}
}

Si dans l'application NumerotationLigne vous remplacez la ligne :

DataInputStream fluxLecture = new DataInputStream (fluxLignes);

par les lignes :

DataInputStream fluxLecture = new DataInputStream (


new ToUpperCaseInputStream (fluxLignes));

vous pourrez vous rendre compte du résultat. Bien sûr ce filtre ne peut s'appliquer qu'à des fichiers textes.

La classe java.io.BufferedInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream permet de lire sur un flux de données
en utilisant une zone mémoire tampon (buffer ). Cette classe se charge de lire sur le flux de données un grande
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 13

nombre d'octets qu'elle garde dans un buffer. Tous les appels à la méthode read () ultérieurs sur une
instance de cette classe renvoient du coup le contenu de ce buffer tant qu'il y reste des octets à lire. Ceci
évite de faire une lecture physique sur le flux de données à chaque appel de la méthode read () c'est
pourquoi ce filtre est très souvent utilisé.

Variables

protected byte [ ] buf


protected int count
protected int pos
protected int markpos
protected int marklimit

Constructeurs

public BufferedInputStream (InputStream in)


public BufferedInputStream (InputStream in, int size)

Ces constructeurs permettent de créer une instance de la classe BufferedInputStream dont la lecture sera
effectuée sur le flux de données in. size est la taille utilisée pour créer le buffer (égal à 2048 par défaut).

Méthodes

public synchronized int read () throws IOException


public synchronized int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public synchronized long skip (long n) throws IOException
public synchronized int available () throws IOException
public synchronized void mark (int readlimit)
public synchronized void reset () throws IOException
public boolean markSupported ()

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

Exemples

Applications NumerotationLigne , ConcatenationFichiers et PaperBoardServer .


Applet PaperBoardClient .

L'interface java.io.DataInput

Cette interface implémentée par les classes DataInputStream et RandomAccessFile déclare un ensemble de
méthodes qui permettent de lire à partir d'un flux de données ou d'un fichier des données de différents types :
Soit un ensemble d'octets grâce aux méthodes readFully (), soit une donnée binaire représentant un type de
base Java, soit une chaîne de caractères.

Méthodes

public void readFully (byte [ ] tab) throws IOException

Cette méthode doit permettre de lire tab.length octets dans le tableau tab. Une exception EOFException est
déclenchée si la fin du fichier est atteinte, et une exception IOException est déclenchée en cas d'autre erreur.

public void readFully (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Cette méthode doit permettre de lire length octets dans le tableau tab, en le remplissant à partir de l'indice
offset. Une exception EOFException est déclenchée si la fin du fichier est atteinte, et une exception
IOException est déclenchée en cas d'autre erreur.

public boolean readBoolean () throws IOException


public byte readByte () throws IOException
public int readUnsignedByte () throws IOException
public short readShort () throws IOException
public int readUnsignedShort () throws IOException
public char readChar () throws IOException
public int readInt () throws IOException
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 14

public long readLong () throws IOException


public float readFloat () throws IOException
public double readDouble () throws IOException

Ces méthodes doivent permettre de lire une valeur booléenne, un octet, un octet non signé (codé sur 8 bits et
compris en 0 et 255), un short, un short non signé, un int, un long, un float ou un double. Une exception
EOFException est déclenchée si la fin du fichier est atteinte, et une exception IOException est déclenchée en
cas d'autre erreur.

public String readLine () throws IOException

Cette méthode doit permettre de lire tous les caractères jusqu'au premier symbole de retour à la ligne. Elle
manipule des caractères codés sur 8 bits et non sur 16 bits (Unicode). Renvoie la chaîne de caractères lue sans
le caractère de retour à la ligne à la fin, ou null si la fin du fichier est atteinte.

public String readUTF () throws IOException

Cette méthode doit permettre de lire une chaîne de caractères qui est codée au format UTF. Renvoie la chaîne
de caractères lue ou null si la fin du fichier est atteinte.

public int skipBytes (int n) throws IOException

Cette méthode doit permettre de sauter n octets. Une exception EOFException est déclenchée si la fin du
fichier est atteinte, et une exception IOException est déclenchée en cas d'autre erreur.

La classe java.io.DataInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream implémente l'interface DataInput, ce
qui permet de lire à partir d'un flux de données autres chose que des octets.

Constructeur

public DataInputStream (InputStream in)

Construit une instance de la classe DataInputStream dont la lecture sera effectuée sur le flux de données in.

Méthodes

public final void read (byte [ ] tab) throws IOException


public final void read (byte [ ] tab, int offset, int length)
throws IOException,

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public final void readFully (byte [ ] tab)


throws IOException
public final void readFully (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final boolean readBoolean () throws IOException
public final byte readByte () throws IOException
public final int readUnsignedByte () throws IOException
public final short readShort () throws IOException
public final int readUnsignedShort () throws IOException
public final char readChar () throws IOException
public final int readInt () throws IOException
public final long readLong () throws IOException
public final float readFloat () throws IOException
public final double readDouble () throws IOException
public final String readLine () throws IOException
public final String readUTF () throws IOException
public final static String readUTF (DataInput in) throws IOException
public final int skipBytes (int n) throws IOException

Implémentation des méthodes de l'interface DataInput.

Exemple

Applications NumerotationLigne et PaperBoardServer .


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 15

Applet PaperBoardClient .

La classe java.io.LineNumberInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream permet de compter le nombre de lignes
lors de la lecture d'un flux de données. Une ligne se termine par le caractère '\n'. L'application
NumerotationLigne utilise cette classe de filtre.

Constructeur

public LineNumberInputStream (InputStream in)

Construit une instance de la classe LineNumberInputStream dont la lecture sera effectuée sur le flux de
données in. Le numéro de ligne est initialisé à 0.

Méthodes

public int read () throws IOException


public int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public long skip (long n) throws IOException
public int available () throws IOException
public void mark (int readlimit)
public void reset () throws IOException

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public int getLineNumber ()


public void setLineNumber (int lineNumber)

Ces méthodes permettent d'interroger ou de modifier le numéro de ligne courant.

Exemple

Application NumerotationLigne .

La classe java.io.PushBackInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream permet de simuler un retour en arrière
d'un octet pendant la lecture d'un flux de données. La méthode unread () qui est utilisée pour revenir en
arrière d'un octet prend en paramètre l'octet à renvoyer à la lecture suivante. Cette classe est surtout utilisée
pour les analyseurs lexicaux et syntaxiques.

Variable

protected int pushBack

Constructeur

public PushbackInputStream (InputStream in)

Méthodes

public int read () throws IOException


public int read (byte [ ] bytes, int offset, int length)
throws IOException, IndexOutOfBoundsException
public int available () throws IOException
public boolean markSupported ()

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public void unread (int ch) throws IOException

Permet de simuler un retour en arrière d'un octet. L'octet ch est le prochain octet qui sera renvoyé par un
appel à une méthode read (). Cette méthode déclenche une exception de classe IOException si deux appels à
unread () sont effectués à la suite sans appel à une des méthodes read () entre temps.
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 16

La classe java.io.StreamTokenizer

Cette classe permet d'énumérer à partir d'un flux de données un ensemble de sous-chaînes ou de nombres
séparées en spécifiant différents délimiteurs de manière plus riche que la classe java.util.StringTokenizer.
Cette classe peut être utilisée par exemple pour écrire un compilateur.
Seuls les caractères dont le code est compris entre 0 et 255 sont utilisables.

Variables

public static final int TT_EOF


public static final int TT_EOL
public static final int TT_NUMBER
public static final int TT_WORD

Ces constantes représentent le type d'une sous-chaîne lue : la fin du flux de données, la fin d'une ligne, un
nombre ou un mot.

public int ttype

Cette variable mémorise le type de la dernière sous-chaîne lue (avec pour valeur TT_EOF, TT_EOL, TT_NUMBER ou
TT_WORD). Voir aussi la description des méthodes qui suivent.

public String sval


public double nval

Ces variables mémorisent le dernier nombre (si ttype = TT_NUMBER) ou le dernier mot lu (si ttype = TT_WORD).

Constructeur

public StreamTokenizer (InputStream in)

Construit une instance de la classe StreamTokenizer avec comme flux de données d'entrée in.

Méthodes

public void wordChars (int low, int high)

Permet d'ajouter tous les caractères dont le code est compris entre low et high à l'ensemble des caractères
utilisés pour construire un mot. La lecture d'un mot s'arrête quand un des caractères utilisés comme espace ou
un caractère ordinaire sont lus. Quand un mot est lu, la variable ttype est égale à TT_WORD et la variable sval
contient le mot lu.

public void whitespaceChars (int low, int high)

Permet d'ajouter tous les caractères dont le code est compris entre low et high à l'ensemble des caractères
utilisés comme espace.

public void commentChar (int ch)

Permet d'ajouter le caractère de code ch à l'ensemble des caractères délimitant le début d'un commentaire de
fin de ligne.

public void ordinaryChar (int ch)


public void ordinaryChars (int low, int high)

Permet d'ajouter le caractère de code ch ou tous les caractères dont le code est compris entre low et high à
l'ensemble des caractères ordinaires. Un caractère ordinaire n'appartient ni à l'ensemble des caractères utilisés
pour construire un mot ou un nombre, ni à l'ensemble des caractères utilisés comme espace ou comme
délimiteurs de commentaire.
Quand un caractère ordinaire est lu, la variable ttype est égale à ce caractère.

public void quoteChar (int ch)

Permet d'ajouter le caractère de code ch à l'ensemble des caractères utilisés pour entourer une chaîne de
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 17

caractères constante. Quand une chaîne de caractères constante est lue, la variable ttype est égale au
délimiteur et la variable sval contient la chaîne lue sans les délimiteurs.

public void parseNumbers ()

Permet de spécifier que les nombres doivent être lus en tant que tels, c'est-à-dire que l'appel de cette méthode
mémorise que les chiffres 0 à 9, les caractères '.' et '-' appartiennent à l'ensemble des caractères utilisés
pour construire un nombre. Quand un nombre est lu, la variable ttype est égale à TT_NUMBER et la variable
nval contient le nombre lu.

public void resetSyntax ()

Remet à zéro la syntaxe définie avec les méthodes précédentes.

public void eolIsSignificant (boolean flag)

Permet de spécifier que les caractères de fin de ligne sont significatifs ou non. Si flag est égal à true, la
variable ttype vaudra TT_EOL chaque fois qu'une fin de ligne est lue, sinon les caractères de fin de ligne seront
considérés comme un espace.

public void slashStarComments (boolean flag)

Permet de spécifier si les commentaires Java /* ... */ sont significatifs ou non.

public void slashSlashComments (boolean flag)

Permet de spécifier si les commentaires Java de fin de ligne // ... sont significatifs ou non.

public void lowerCaseMode (boolean flag)

Permet de spécifier si tous les mots lus sont convertis en minuscules ou non.

public int nextToken () throws IOException

Lit dans le flux d'entrée la sous-chaîne suivante en respectant la syntaxe définie grâce aux méthodes
précédentes. Renvoie la valeur de la variable ttype.

public void pushBack ()

Permet de revenir d'une sous-chaîne en arrière une fois.

public int lineno ()

Renvoie le numéro de ligne courant.

public String toString ()

Méthode de la classe Object, outrepassée pour qu'elle renvoie une description de la dernière sous-chaîne lue.

Exemple

Applet PaperBoardClient .

Accès à un flux de données en écriture

De manière similaire à l'accès à un flux de données en lecture, l'accès à un flux de données en écriture
s'effectue grâce aux classes qui dérivent de la classe OutputStream. Ces classes elles aussi se divisent en deux
catégories :
Les classes qui permettent de se lier à un type de source de données spécifique :
la classe FileOutputStream pour accéder en écriture à un fichier
la classe ByteArrayOutputStream pour écrire dans un tableau d'octets sous forme de flux de données
la classe PipedOutputStream pour accéder à un flux de données sous forme de pipeline

La création d'une instance d'une de ces classes permet d'accéder sous forme d'un flux à la source de données
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 18

à laquelle elle est dédiée. En particulier, la création d'un objet de classe FileOutputStream permet d'ouvrir un
fichier et d'y écrire.
En regardant les constructeurs de ces classes vous verrez qu'ils prennent en paramètre une instance de la
classe représentant le type de sources de données qu'il manipule, comme le résume le tableau suivant :

Classes Paramètres des constructeurs


Chemin d'accès à un fichier (classe String)
FileOutputStream Fichier décrit par une instance de la classe File
Descripteur de fichier (classe FileDescriptor)
Aucun pour créer un tableau d'octets par défaut
ByteArrayOutputStream
Taille minimum du tableau d'octets
Aucun pour créer un pipeline de sortie
PipedOutputStream
Entrée d'un pipeline (classe PipedInputStream)

Une fois que vous avez créé une instance d'une de ces classes, vous pouvez appeler les méthodes de la classe
OutputStream, puisque toutes ces classes en héritent.
L'accès aux trois autres types de sources de données restantes (les sorties standards, les sockets et un fichier
via une URL) s'effectue différemment :
Les sorties standard et d'erreur sont accessibles soit directement par les variables static out et err de la
classe System, qui sont de classe PrintStream, soit par les variables static out et err de la classe
FileDescriptor. Ces dernières variables étant elles-même de classe FileDescriptor, l'écriture sur les
sorties standard peut démarrer en créant une instance de classe FileOutputStream, par exemple avec
l'instruction new FileOutputStream (FileDescriptor.out).
L'accès en écriture aux sockets et à un fichier accessible via une URL s'effectue grâce à la méthode
getOutputStream () des classes Socket et URLConnection, qui renvoie une instance d'une classe dérivée
de InputStream.

Comme pour la classe InputStream, les méthodes d'écriture de la classe OutputStream paraissent d'un
intérêt très limité : elle permettent d'écrire un ou plusieurs octets, c'est pourquoi il existe aussi une
seconde catégorie de classes dérivant de OutputStream, afin d'enrichir les méthodes d'écriture sur les
flux de données.
Les classes de filtre sur un flux de données qui dérivent de la classe FilterOutputStream :
La classe BufferedOutputStream pour écrire sur un flux de données en utilisant une zone mémoire
tampon (buffer )
La classe DataOutputStream pour écrire dans un flux de données des données d'un des types de base de
Java ou des chaînes de caractères
La classe PrintStream pour d'écrire au format texte les types de base Java

Contrairement aux classes dédiées à un type de source de données, les constructeurs de ces classes
prennent tous en paramètre un objet de classe OutputStream, qui est le flux de données sur lequel elles
effectuent des traitements supplémentaires avant d'y écrire. La classe OutputStream étant la super classe
de toutes les classes d'écriture sur les flux de données, vous pouvez donc passer en paramètre une
instance de n'importe quelle classe qui en dérive.

L'application suivante utilise les classes FileInputStream, SequenceInputStream, FileOutputStream et


BufferedOutputStream pour effectuer la concaténation de plusieurs fichiers dans un fichier destination.
Recopiez la dans un fichier ConcatenationFichiers.java , que vous compilez avec l'instruction javac
ConcatenationFichiers.java pour ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction java
ConcatenationFichiers nomFichier1 ... nomFichierN fichierDest (par exemple java
ConcatenationFichiers ConcatenationFichiers.java NumerotationLigne.java resultat.java) :

import java.io.*;
import java.util.Vector;

public class ConcatenationFichiers


{
// Méthode lancée à l'appel de l'instruction :
// java ConcatenationFichiers nomFichier1 ... nomFichierN nomFichierDest
public static void main (String [ ] args)
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 19

{
try
{
Vector ensembleFichiers = new Vector ();
// Ajout à ensembleFichiers de tous les flux de données avec buffer
// correspondant à chaque fichier d'entrée passé en paramètres
// (sauf le dernier paramètre)
for (int i = 0; i < args.length - 1; i++)
ensembleFichiers.addElement (new BufferedInputStream (
new FileInputStream (args [i])));

// Création d'un ensemble de flux d'entrée


InputStream fluxEntree =
new SequenceInputStream (ensembleFichiers.elements ());

// Ouverture en écriture avec un buffer du fichier


// passé en dernier paramètre dans la ligne de commande
OutputStream fluxDestination =
new BufferedOutputStream (
new FileOutputStream (args [args.length -1]));

byte donnees [ ] = new byte [1000];


int nbreOctetsLus;
// Lecture puis écriture des données
while ((nbreOctetsLus = fluxEntree.read (donnees)) != -1)
fluxDestination.write (donnees, 0, nbreOctetsLus);

// Fermeture des flux


fluxDestination.close ();
fluxEntree.close ();
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant l'accès aux fichiers
System.out.println (e);
}
}
}

La classe java.io.OutputStream

Cette classe abstract est la super classe de toutes les classes qui permettent d'écrire sur un flux de données
en lecture (voir aussi le paragraphe précédent).

Méthodes

public abstract void write (int b) throws IOException

Ecrit un octet sur le flux de données.

public void write (byte [ ] tab) throws IOException

Ecrit les octets du tableau tab sur le flux de données.

public void write (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Ecrit length octets du tableau tab sur le flux de données, à partir de l'indice offset.

public void flush () throws IOException

Ecrit physiquement sur le flux de données quand une zone tampon (buffer ) est utilisée.

public void close () throws IOException

Ferme l'accès au flux de données.

Exemple

Application ConcatenationFichiers et EchoServeur .


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 20

La classe java.io.FileOutputStream

Cette classe qui dérive de la classe OutputStream permet d'ouvrir un fichier et d'y écrire, sous forme de flux
de données.

Constructeurs

public FileOutputStream (String path)


throws SecurityException, FileNotFoundException
public FileOutputStream (File file)
throws SecurityException, FileNotFoundException

Ces constructeurs permettent de créer une instance de classe FileOutputStream à partir du chemin d'accès à
un fichier path ou d'une instance de classe File. Si le fichier n'existe pas il est créé.

public FileOutputStream (FileDescriptor fdObj) throws SecurityException

Construit une instance de classe FileInputStream à partir d'un descripteur de fichier fdObj.

Méthodes

public void write (int b) throws IOException


public void write (byte [ ] tab) throws IOException
public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture dans un fichier.

public final FileDescriptor getFD () throws IOException

Permet d'obtenir un descripteur de fichier.

protected void finalize () throws IOException

Méthode de la classe Object outrepassée pour que le fichier ouvert à la création d'une instance de la classe
FileOutputStream soit fermé avant que le Garbage Collector n'intervienne pour détruire cet objet. Le moment
où intervient le Garbage Collector n'étant généralement pas prévisible, il vaut mieux fermer soi-même le
fichier avec la méthode close (), pour éviter d'en bloquer l'accès à d'autres programmes.

Exemple

Application ConcatenationFichiers .

La classe java.io.PipedOutputStream

Cette classe qui dérive de la classe OutputStream permet d'accéder à un pipeline en écriture, sous forme de
flux de données.

Constructeurs

public PipedOutputStream (PipedInputStream snk) throws IOException


public PipedOutputStream ()

Méthodes

public void connect (PipedInputStream snk) throws IOException

Crée une connexion avec l'entrée du pipeline snk.

public void write (int b) throws IOException


public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture sur un pipeline.
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 21

La classe java.io.ByteArrayOutputStream

Cette classe qui dérive de la classe OutputStream permet d'écrire dans un tableau d'octets, sous forme de flux
de données. Ce tableau est créé par la classe et automatiquement agrandi s'il est plein.

Variables

protected byte [ ] buf


protected int count

Constructeurs

public ByteArrayOutputStream ()
public ByteArrayOutputStream (int size)

Ces constructeurs permettent de créer une instance de classe ByteArrayOutputStream, dont le tableau sera au
minimum de size octets (égal à 32 par défaut).

Méthodes

public synchronized void write (int b)


public synchronized void write (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture dans un tableau
d'octets.

public int size ()

Renvoie le nombre courant d'octets écrits dans le tableau.

public synchronized void reset ()

Remet l'index courant dans le tableau à 0 sans allouer un nouveau tableau.

public synchronized byte [ ] toByteArray ()

Renvoie un tableau d'octets copie des octets écrits dans le tableau interne.

public String toString ()

Méthode de la classe Object, outrepassée pour qu'elle renvoie les octets écrits dans le tableau sous forme de
chaîne de caractères.

public String toString (int hibyte)

Renvoie les octets écrits dans le tableau sous forme de chaîne de caractères, dont chaque caractère Unicode a
hibyte pour partie haute.

public synchronized void writeTo (OutputStream out) throws IOException

Ecrit sur le flux de données out les octets écrits dans le tableau.

La classe java.io.FilterOutputStream

Cette classe qui dérive de la classe OutputStream est la super classe de toutes les classes utilisées pour filtrer
l'écriture sur un flux de données. Vous pouvez utiliser les classes BufferedOutputStream, DataOutputStream
ou PrintStream qui en dérivent mais aussi créer vos propres classes de filtre dérivant de cette classe (voir
aussi la classe ToUpperCaseInputStream comme exemple de filtre).

Variable

protected OutputStream out

Constructeur
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 22

public FilterOutputStream (OutputStream out)

Méthodes

public void write (int b) throws IOException


public void write (byte [ ] tab) throws IOException
public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void flush () throws IOException
public void close () throws IOException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'accès à un flux de données
filtré en écriture.

La classe java.io.BufferedOutputStream

Cette classe qui dérive des classes FilterOutputStream et OutputStream permet d'écrire sur un flux de
données en utilisant une zone mémoire tampon (buffer ). Lors d'un appel à la méthodes write (), les octets
sont stockés dans le buffer. Le buffer est écrit finalement sur le flux de données une fois qu'il est plein ou
lors d'un appel à la méthode flush (). Ceci évite de faire une écriture physique sur le flux de données à
chaque appel de la méthode write () c'est pourquoi ce filtre est très souvent utilisé.

Variables

protected byte [ ] buf


protected int count

Constructeurs

public BufferedOutputStream (OutputStream out)


public BufferedOutputStream (OutputStream out, int size)

Ces constructeurs permettent de créer une instance de la classe BufferedOutputStream dont l'écriture sera
effectuée sur le flux de données out. size est la taille utilisée pour créer le buffer (égal à 512 par défaut).

Méthodes

public synchronized void write (int b) throws IOException


public synchronized void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public synchronized void flush () throws IOException

Méthodes de la classe FilterOutputStream outrepassées pour qu'elles s'appliquent à ce filtre.

Exemple

Application ConcatenationFichiers et PaperBoardServer .


Applet PaperBoardClient .

L'interface java.io.DataOutput

Cette interface implémentée par les classes DataOutputStream et RandomAccessFile déclare un ensemble de
méthodes qui permettent d'écrire dans un flux de données ou dans un fichier des données de différents types :
Soit un ensemble d'octets grâce aux méthodes write (), soit une donnée binaire représentant un type de base
Java, soit une chaîne de caractères.
Les données écrites grâce à ces méthodes sont lisibles grâce aux méthodes de l'interface DataInput, quelque
soit le système sur lequel les données ont été écrites. Le codage binaire des nombres étant souvent
différemment d'un système à l'autre, ceci permet de créer des flux de données portables.

Méthodes

public void write (int b) throws IOException

Cette méthode doit permettre d'écrire un octet.


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 23

public void write (byte [ ] tab) throws IOException

Cette méthode doit permettre d'écrire les octets du tableau tab.

public void write (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Cette méthode doit permettre d'écrire length octets du tableau tab, à partir de l'indice offset.

public void writeBoolean (boolean v) throws IOException


public void writeByte (int v) throws IOException
public void writeShort (int v) throws IOException
public void writeChar (int v) throws IOException
public void writeInt (int v) throws IOException
public void writeLong (long v) throws IOException
public void writeFloat (float v) throws IOException
public void writeDouble (double v) throws IOException

Ces méthodes doivent permettre d'écrire une valeur booléenne, un octet, un short, un caractère Unicode, un
int, un long, un float ou un double.

public void writeBytes (String s) throws IOException

Cette méthode doit permettre d'écrire la chaîne de caractères s. Elle écrit les caractères en les codant sur 8
bits et non sur 16 bits (Unicode).

public void writeChars (String s) throws IOException

Cette méthode doit permettre d'écrire la chaîne de caractères s, comme une suite de caractères Unicode.

public void writeUTF (String s) throws IOException

Cette méthode doit permettre d'écrire au format UTF la chaîne de caractères s.

La classe java.io.DataOutputStream

Cette classe qui dérive des classes FilterOutputStream et OutputStream implémente l'interface DataOutput, ce
qui permet d'écrire sur un flux de données autres chose que des octets.

Variable

protected int written

Constructeur

public DataOutputStream (OutputStream out)

Construit une instance de la classe DataOutputStream dont l'écriture sera effectuée sur le flux de données out.

Méthodes

public synchronized void write (int b) throws IOException


public synchronized void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final void writeBoolean (boolean v) throws IOException
public final void writeByte (int v) throws IOException
public final void writeShort (int v) throws IOException
public final void writeChar (int v) throws IOException
public final void writeInt (int v) throws IOException
public final void writeLong (long v) throws IOException
public final void writeFloat (float v) throws IOException
public final void writeDouble (double v) throws IOException
public final void writeBytes (String s) throws IOException
public final void writeChars (String s) throws IOException
public final void writeUTF (String str) throws IOException

Implémentation des méthodes de l'interface DataOutput. La méthode write (byte [ ] tab) est implémentée
par la classe FilterOutputStream.
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 24

public void flush () throws IOException

Méthodes de la classe FilterOutputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public final int size ()

Renvoie le nombre d'octets écrits sur le flux de données depuis la création du filtre.

La classe java.io.PrintStream

Cette classe qui dérive des classes FilterOutputStream et OutputStream permet d'écrire au format texte les
types de base Java. Elle est très souvent utilisée via la variable System.out qui est justement de classe
PrintStream.
Contrairement aux méthodes des autres classes écrivant sur un flux de données, les méthodes de la classe
PrintStream ne déclenchent jamais d'exceptions de classe IOException. Sur une erreur survient pendant
l'écriture sur le flux de données, une variable private prend la valeur true et la méthode checkError ()
renvoie cette valeur. Ce système qui peut paraître peu contraignant pour l'utilisateur de la classe PrintStream
et laisser passer des erreurs plus facilement, comporte malgré tout un avantage majeur : il permet par exemple
d'écrire sur la sortie standard avec la variable System.out sans être obligé d'utiliser un bloc try ... catch.

Constructeurs

public PrintStream (OutputStream out)


public PrintStream (OutputStream out, boolean autoflush)

Ces constructeurs permettent de créer une instance de la classe PrintStream dont l'écriture sera effectuée sur
le flux de données out. autoflush permet de préciser si un appel à la méthode flush () doit être effectué à
chaque retour à la ligne (égal à false par défaut).

Méthodes

public void write (int b)


public void write (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException
public void flush ()
public void close ()

Méthodes de la classe FilterOutputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public boolean checkError ()

Appelle la méthode flush () et renvoie true si une erreur est survenue pendant un appel aux autres
méthodes de cette classe.

public void print (Object obj)


public synchronized void print (String s)
public synchronized void print (char [ ] tab)
public void print (boolean b)
public void print (char c)
public void print (int i)
public void print (long l)
public void print (float f)
public void print (double d)

Ces méthodes permettent d'écrire au format texte un objet, une chaîne de caractère, un tableau de caractères,
une valeur booléenne, un caractère, un int, un long, un float ou un double, sur le flux de données. Le texte
écrit pour l'objet obj est la chaîne de caractère renvoyée par l'appel obj.toString ().

public synchronized void println (Object obj)


public synchronized void println (String s)
public synchronized void println (char [ ] tab)
public synchronized void println (boolean b)
public synchronized void println (char c)
public synchronized void println (int i)
public synchronized void println (long l)
public synchronized void println (float f)
vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 25

public synchronized void println (double d)

Ces méthodes effectuent les mêmes opérations que le groupe précédent, puis écrivent un retour à la ligne sur
le flux de données.

public void println ()

Ecrit un retour à la ligne sur le flux de données.

Cette classe est l'équivalent des fonctions printf () du C, mais ne comporte pas toute la richesse des
formats de données disponibles avec printf ().

Exemples

Les applications Banque , LectureFichier , NumerotationLigne , ConcatenationFichiers , TestExpression et


PaperBoardServer utilisent la variable System.out et la méthode println ().
Applet PaperBoardClient .

Gestion de l'accès aléatoire aux fichiers

Java fournit la classe RandomAccessFile pour accéder aléatoirement au contenu d'un fichier en lecture ou
en écriture (voir aussi le début de ce chapitre).

La classe java.io.RandomAccessFile

Cette classe qui implémente les interfaces DataInput et DataOuput, permet d'accéder aléatoirement à un fichier
en mode lecture/écriture, ou uniquement en mode lecture.

Constructeurs

public RandomAccessFile (String path, String mode)


throws SecurityException, IOException, IllegalArgumentException
public RandomAccessFile (File file, String mode)
throws SecurityException, IOException, IllegalArgumentException

Ces constructeurs permettent d'ouvrir le fichier représenté par son chemin d'accès path ou par une instance
de la classe File. Si mode est égal à "rw" (read/write ) il est possible d'écrire ou de lire dans le fichier ouvert
et le fichier est créé s'il n'existe pas.
Si mode est égal à "r" (read ), le fichier est accessible uniquement en lecture et par conséquent l'utilisation des
méthodes d'écriture write...() de cette classe déclenchera une exception de classe IOException.

Méthodes

public final FileDescriptor getFD () throws IOException

Permet d'obtenir un descripteur de fichier.

public long getFilePointer () throws IOException

Renvoie la position courante dans le fichier.

public void seek (long pos) throws IOException

Déplace la position courante en pos dans le fichier.

public long length () throws IOException

Renvoie la taille courante du fichier.

public void close () throws IOException

Ferme l'accès au fichier.

public abstract int read () throws IOException


vendredi 11 février 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 26

Lit un octet dans le flux de données. Renvoie -1 si la fin est atteinte.

public int read (byte [ ] tab) throws IOException

Lit tab.length octets dans le tableau tab. Si la fin est atteinte pendant la lecture, tab est remplit avec les
octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est atteinte.

public int read (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Lit length octets dans le tableau tab, en le remplissant à partir de l'indice offset. Si la fin est atteinte pendant
la lecture, tab est remplit avec les octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est atteinte.

public int skipBytes (int n) throws IOException

Méthode de l'interface DataInput implémentée pour qu'elle déplace de n octets la position courante dans le
fichier. n peut être positif ou négatif (pour revenir en arrière). Renvoie n.

public final void readFully (byte [ ] tab) throws IOException


public final void readFully (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final boolean readBoolean () throws IOException
public final byte readByte () throws IOException
public final int readUnsignedByte () throws IOException
public final short readShort () throws IOException
public final int readUnsignedShort () throws IOException
public final char readChar () throws IOException
public final int readInt () throws IOException
public final long readLong () throws IOException
public final float readFloat () throws IOException
public final double readDouble () throws IOException
public final String readLine () throws IOException
public final String readUTF () throws IOException

Implémentation des autres méthodes de l'interface DataInput.

public void write (int b) throws IOException


public void write (byte [ ] tab) throws IOException
public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final void writeBoolean (boolean v) throws IOException
public final void writeByte (int v) throws IOException
public final void writeShort (int v) throws IOException
public final void writeChar (int v) throws IOException
public final void writeInt (int v) throws IOException
public final void writeLong (long v) throws IOException
public final void writeFloat (float v) throws IOException
public final void writeDouble (double v) throws IOException
public final void writeBytes (String s) throws IOException
public final void writeChars (String s) throws IOException
public final void writeUTF (String str) throws IOException

Implémentation des méthodes de l'interface DataOutput.


vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 1

Les accès au réseau

Accès via une URL


L'architecture client-serveur
Accès via les sockets
Accès via les datagrams

Ce chapitre décrit le package java.net de Java 1.0 qui rassemble les classes permettant de gérer les accès
réseau via une URL ou par les sockets.

Accès via une URL

Les accès au réseau peuvent se faire à plusieurs niveaux en Java.


Le niveau le plus simple permet d'accéder à un fichier sur le réseau grâce à son URL (Uniformed Resource
Locator ) : Contrairement au chemin d'accès classique à un fichier dont la notation varie d'un système à
l'autre (c:\fichier.txt, /usr/unCompte/fichier.txt), une URL permet de désigner un fichier de
manière uniforme quelque que soit le système qui héberge ce fichier.
Même si vous ne la connaissiez pas sous ce nom, l'adresse d'un site Internet est une URL :
http://www.javasoft.com/index.html, http://www.eteks.com/coursjava/net10.html# AccesURL sont
des exemples d'URL.
De quoi est composée une URL ?

Une chaîne représentant le protocole à utiliser pour accéder au fichier (http, ftp, file, mailto,...),
suivi du symbole :.
Le nom de l'hôte qui fournit le service recherché (www.yahoo.fr). Cet hôte correspond à une
machine.
Eventuellement un numéro de port sur la machine de l'hôte précédé du symbole :.
Le chemin d'accès au fichier recherché sur l'hôte. Les répertoires éventuels de ce chemin d'accès
sont séparés par le symbole /.
Pour les fichiers HTML, le nom du fichier peut être suivi d'une référence à une ancre à l'intérieur
du fichier précédé du symbole #.

Une URL peut représenter un fichier mais aussi de manière plus générale une ressource. Par exemple,
une ressource peut être un programme renvoyant une image ou le résultat d'un accès à une base de
données. La plupart des programmes auxquels on accède sur un site Internet via une URL utilise le
modèle CGI (Common Gateway Interface ). Ce modèle définit comment appeler un programme et lui
transmettre ses paramètres, de manière standard.
Par exemple, http://www.hit-parade.com/hp.asp?site=a15740 est un programme CGI appelé avec la
valeur a15740 pour le paramètre site. Ce programme renvoie l'image du site hit-parade.com.

Java permet d'accéder au fichier ou à la ressource représentés par une URL sous forme de flux de
données, grâce aux deux classes URL et URLConnection.

La classe java.net.URL

Cette classe final permet de manipuler une URL sous forme d'un objet constant. Les constructeurs de
cette classe pouvant éventuellement déclencher une exception de classe MalformedURLException, la
création d'une nouvelle instance de classe URL doit être programmée dans un bloc try ... catch. L'objet
créé ne garantit pas que le fichier existe mais seulement que l'URL mémorisée par l'objet est
correctement formée et que le protocole spécifié est géré par la Machine Virtuelle Java.
Les objets de cette classe sont utilisés pour accéder sous forme de flux de données au fichier
correspondant, mais aussi par certaines méthodes de la classe Applet pour obtenir l'URL d'un document
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 2

HTML, lire une image ou un fichier son.

Constructeurs

public URL (String protocol, String host, int port, String file)
throws MalformedURLException
public URL (String protocol, String host, String file)
throws MalformedURLException

Ces constructeurs permettent de créer un objet de classe URL à partir du protocole protocol, l'hôte host,
le port port et le chemin d'accès file d'une URL.

public URL (String spec) throws MalformedURLException

Construit une instance de classe URL à partir de la chaîne de caractères spec représentant une URL.

public URL (URL context, String spec) throws MalformedURLException

Construit une instance de classe URL à partir d'une URL existante context et de la chaîne de caractères
spec. Si spec décrit entièrement une URL (avec protocole, hôte, fichier,...) , l'URL context est ignorée et
l'objet créé correspond à l'URL spec. Sinon, context est utilisé comme base complétée par les
informations fournies dans spec, pour construire le nouvel objet de classe URL (spec peut par exemple
désigné un fichier dans un sous répertoire différent).

Méthodes

public String getProtocol ()


public String getHost ()
public int getPort ()
public String getFile ()
public String getRef ()

Ces méthodes permettent d'interroger le protocole, l'hôte, le port, le chemin d'accès au fichier et la
référence mémorisée dans une instance de la classe URL. Si le port n'a pas été précisé en paramètre du
constructeur, la valeur -1 est renvoyée.

public boolean sameFile (URL other)

Renvoie true si other désigne la même URL que l'objet sur lequel est invoquée cette méthode. Les
références mémorisées par les objets ne sont pas prises en compte pour la comparaison.

public String toExternalForm ()

Renvoie une chaîne de caractères correspondant à l'URL mémorisée par un objet de classe URL.

public final InputStream openStream () throws IOException

Ouvre en lecture la connexion avec une URL, et renvoie une instance de la classe InputStream pour
accéder aux données sous forme de flux de données.

public final Object getContent () throws IOException

Renvoie un objet correspondant au contenu du fichier représenté par un objet de classe URL. Par
exemple, si le fichier est une image, cette méthode renverra une instance d'une classe dérivée de la
classe Image.

public URLConnection openConnection () throws IOException

Ouvre une connexion avec une URL, et renvoie une instance de la classe URLConnection qui permet
d'obtenir toute sorte de renseignements (dates, en-tête,...) sur le fichier correspondant à l'URL.

public static synchronized void setURLStreamHandlerFactory


(URLStreamHandlerFactory fac)
throws SecurityException
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 3

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création


de flux de données pour chacun des protocoles reconnus. Cette méthode static ne peut être appelée
qu'une seule fois. Par défaut, la Machine Virtuelle Java reconnaît un certain nombre de protocoles (voir
l'exemple qui suit).

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe URL à un objet ou renvoyer une chaîne de caractères correspondant à l'URL.

L'application suivante permet de tester si la Machine Virtuelle Java accepte ou non les 4 protocoles les
plus communément utilisés : http, ftp, file et mailto. Recopiez-la dans un fichier TestProtocole.java , que
vous compilez avec l'instruction javac TestProtocole.java pour ensuite l'exécuter avec java ou Java
Runner , grâce à l'instruction java TestProtocole :

import java.net.*;

public class TestProtocole


{
public static void main (String [] args)
{
// Tableau avec 4 URL à tester
String urlTest [] = {"http://wwwusers.imaginet.fr/~puybaret/learnjava/index.html",
"ftp://wwwusers.imaginet.fr/~puybaret/learnjava/index.html",
"file://C//java/bin/java.exe",
"mailto:puybaret@imaginet.fr"};

for (int i = 0; i < urlTest.length; i++)


try
{
// Création d'une URL
URL url = new URL (urlTest [i]);
System.out.println ("URL : " + url + " correctement form\u00e9e\n"
+ " et protocole " + url.getProtocol () + " reconnu.");
}
catch (MalformedURLException e)
{
// URL incorrect ou protocole inconnu
System.out.println ("URL : " + urlTest [i] + " incorrect\n"
+ " ou protocole non reconnu\n"
+ "(Exception " + e + ").");
}
}
}

Autre exemple

Applets HelloFromNet et BoutonsNavigation .

La classe java.net.URLConnection

Cette classe abstract gère la connexion avec une URL. Une instance d'une classe dérivant de cette
classe peut être obtenue grâce à la méthode openConnection () de la classe URL.
Les méthodes de cette classe permettent de créer un flux de données pour lire et/ou d'écrire dans le
fichier désigné par une URL, mais aussi d'obtenir toute sorte d'information sur ce fichier (sa date de
dernière modification, sa taille, son type,...).

Variables

protected URL url


protected boolean doInput
protected boolean doOutput
protected boolean allowUserInteraction
protected boolean useCaches
protected long ifModifiedSince
protected boolean connected
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 4

Ces variables protected sont utilisées par les classes dérivées de la classe URLConnection.

Constructeur

protected URLConnection (URL url)

Méthodes

public URL getURL ()

Renvoie l'URL avec laquelle la connexion est faite.

public InputStream getInputStream () throws IOException


public OutputStream getOutputStream () throws IOException

Ces méthodes renvoient un flux de données en lecture ou en écriture à partir d'une connexion à une
URL. Une exception de classe UnknownServiceException (dérivant de la classe IOException) est
déclenchée si le protocole interdit l'accès en lecture ou en écriture à cette URL.

public int getContentLength ()

Renvoie la taille du fichier avec lequel la connexion est faite ou 0 si cette taille n'est pas connue.

public String getContentType ()


public String getContentEncoding ()

Renvoie le type du contenu et son codage ou null si ces renseignements ne sont pas connus

public Object getContent () throws IOException

Renvoie un objet correspondant au contenu du fichier.

public long getDate ()


public long getLastModified ()
public long getExpiration ()

Renvoie la date à laquelle le fichier a été envoyé, sa date de dernière modification et sa date d'expiration
ou 0 si ces renseignements ne sont pas connus.

public String getHeaderField (String name)


public int getHeaderFieldInt (String name, int default)
public long getHeaderFieldDate (String name, long default)
public String getHeaderFieldKey (int n)
public String getHeaderField (int n)

Ces méthodes renvoient les renseignements correspondant aux clés contenues dans l'entête d'un fichier

public boolean getDoInput ()


public boolean getDoOutput ()
public boolean getUseCaches ()
public boolean getAllowUserInteraction ()

Ces méthodes renvoient true, s'il est possible de lire ou d'écrire sur une connexion, si la connexion
utilise le cache ou si elle autorise d'interagir avec l'utilisateur (pour lui demander par exemple un mot de
passe). Par défaut, une connexion est ouverte en lecture.

public long getIfModifiedSince ()

Renvoie la durée de validité du fichier sur la connexion est faite.

public String getRequestProperty (String key)

Renvoie la valeur associée au mot-clé key par lequel une requête est reconnue.

public abstract void connect () throws IOException

Les classes dérivées de la classe URLConnection doivent implémenter cette méthode pour réaliser la
connexion avec une URL.
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 5

public void setDoInput (boolean doinput)


public void setDoOutput (boolean dooutput)
public void setUseCaches (boolean usecaches)
public void setAllowUserInteraction (boolean allowuserinteraction)
public void setIfModifiedSince (long ifmodifiedsince)
public void setRequestProperty (String key, String value)

Ces méthodes permettent d'autoriser l'accès en lecture ou en écriture à un fichier, l'utilisation du cache,...
Elles ne peuvent pas être appelées quand la connexion est déjà ouverte.

public boolean getDefaultUseCaches ()


public void setDefaultUseCaches (boolean defaultusecaches)
public static boolean getDefaultAllowUserInteraction ()
public static void setDefaultAllowUserInteraction
(boolean defaultallowuserinteraction)
public static String getDefaultRequestProperty (String key)
public static void setDefaultRequestProperty (String key, String value)
protected static String guessContentTypeFromName (String fname)
protected static String guessContentTypeFromStream (InputStream is)
throws IOException

public static synchronized void setContentHandlerFactory


(ContentHandlerFactory fac) throws SecurityException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création


d'objets représentés dans un fichier suivant leur type mime, différent de celui fourni par défaut. Cette
méthode static ne peut être appelée qu'une seule fois.

public String toString ()

Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant la connexion.

Voici un exemple simple d'applet qui lit le texte contenu dans le fichier accessible à l'URL
http://www.eteks.com/classes/hello.txt pour l'afficher à l'écran :

et le programme Java correspondant (à copier dans un fichier dénommé HelloFromNet.java et invoqué à


partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.Graphics;
import java.net.*;
import java.io.*;

public class HelloFromNet extends Applet


{
String texteLu;

// Méthode appelée par le système à l'initialisation de l'applet


public void init ()
{
try
{
// Création de l'URL http://www.eteks.com/classes/hello.txt
URL urlFichierHello = new URL ("http",
"www.eteks.com",
"/classes/hello.txt");
// Ouverture d'une connexion et récupération d'un flux d'entrée
URLConnection connexion = urlFichierHello.openConnection ();
InputStream fluxFichier = connexion.getInputStream ();

// Lecture du contenu du flux d'entrée


byte contenuFichier [ ] = new byte [connexion.getContentLength ()];
int octetsLus = fluxFichier.read (contenuFichier);

texteLu = new String (contenuFichier, 0, 0, octetsLus);

// Fermeture de la connexion
fluxFichier.close ();
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 6

}
catch (Exception e)
{
texteLu = "Probleme...";
}
}

// Méthode appelée par le système pour mettre à jour le dessin de l'applet


public void paint (Graphics gc)
{
if (texteLu != null)
// Affichage du texte lu
gc.drawString(texteLu, 10, 20);
}
}

La classe java.net.URLEncoder

L'unique méthode encode () de cette classe est utile si vous voulez créer un formulaire en Java et en
envoyer les résultats au serveur pour être traité par exemple par un programme CGI. Cette méthode
transforme une chaîne de caractères au format x-www-form-urlencoded, c'est-à-dire que tous les espaces
sont remplacés par le signe + et tous les caractères différents des lettres de l'alphabet, des chiffres et du
caractère _ par leur code hexadécimal précédé du symbole %.

Méthode

public static String encode (String s)

Vous n'aurez normalement pas à utiliser les interfaces et les classes qui suivent, car la Machine Virtuelle
Java fournit et gère automatiquement un certain nombre de protocoles et la création d'objet en fonction
du contenu d'un fichier. Ces classes sont utilisées indirectement par les méthodes de la classes
URLConnection.

L'interface java.net.URLStreamHandlerFactory

Cette interface est implémentée par les classes désirant gérer l'accès sous forme de flux de données à un
ou plusieurs protocoles. Elle est utilisée comme paramètre de la méthode setURLStreamHandlerFactory
() de la classe URL, pour modifier le gestionnaire par défaut.

Méthode

public URLStreamHandler createURLStreamHandler (String protocol)

Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract URLStreamHandler ou
null si le protocole protocol est refusé ou n'est pas reconnu par ce gestionnaire. L'objet renvoyé doit
pouvoir créer un flux de données à partir du protocole protocol et d'une URL.

La classe java.net.URLStreamHandler

Cette classe abstract est la super classe de toutes les classes qui gère la création d'un flux de données à
partir d'un protocole donné et d'une URL.
La Machine Virtuelle Java fournit un ensemble de classes pour un certain nombre de protocoles. Ces
classes sont utilisées par défaut si la méthode setURLStreamHandlerFactory () de la classe URL n'a pas
été appelée pour changer de gestionnaire de création de flux de données.

Constructeur

public URLStreamHandler ()

Méthodes

protected abstract URLConnection openConnection (URL url) throws IOException


vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 7

Cette méthode doit ouvrir un connexion avec l'URL url et renvoyer une instance d'une classe dérivant
de la classe abstract URLConnection pour permettre d'accéder à l'URL sous forme de flux de données.

protected void parseURL (URL url, String spec, int start, int limit)

Analyse la chaîne de caractères spec représentant une URL entre les caractères start et limit (exclu),
pour compléter l'instance url de la classe URL. Cette méthode analyse la chaîne spec en respectant la
syntaxe du protocole http, et donc s'attend à ce que start désigne le caractère suivant le symbole : du
protocole, et limit le caractère # précédant la référence à une ancre.
Outrepassez cette méthode si vous voulez qu'elle analyse différemment la chaîne spec.

protected String toExternalForm (URL url)

Renvoie une chaîne de caractères correspondant à l'URL mémorisée par url.

protected void setURL (URL url, String protocol, String host,


int port, String file, String ref)

Permet de modifier le protocole protocol, l'hôte host, le port port, le chemin d'accès au fichier file et
la référence ref mémorisés par l'objet url.

L'interface java.net.ContentHandlerFactory

Cette interface est implémentée par les classes désirant gérer le contenu d'un fichier suivant leur type
mime (par exemple image/gif ou text/plain). Elle est utilisée comme paramètre de la méthode
setContentHandlerFactory () de la classe URLConnection, pour modifier le gestionnaire par défaut.

Méthode

public ContentHandler createContentHandler (String mimetype)

Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract ContentHandler. Cet
objet doit pouvoir créer un objet correspondant au contenu d'un fichier de type mime mimetype.

La classe java.net.ContentHandler

Cette classe abstract est la super classe de toutes les classes qui gère la création d'un objet
correspondant au contenu d'un fichier d'un type mime donné.
La Machine Virtuelle Java fournit un ensemble de classes pour certains types mime. Ces classes sont
utilisées par défaut si la méthode setContentHandlerFactory () de la classe URLConnection n'a pas été
appelée pour changer de gestionnaire de création d'objet.

Constructeur

public ContentHandler ()

Méthode

public abstract Object getContent (URLConnection urlc) throws IOException

Cette méthode doit renvoyer un objet correspondant au contenu du fichier avec lequel la connexion
urlc a été établie.

L'architecture client-serveur

Grâce à une URL, l'accès à un fichier ou une ressource est géré de manière simple : que le fichier soit
sur un disque local ou sur Internet, il vous suffit de respecter les règles de construction d'une URL et le
tour est joué.
Mais Java permet aussi de gérer des accès réseau plus complexe, pour réaliser par exemple un dialogue
entre deux ordinateurs d'un réseau. Ce dialogue est basé sur l'architecture client-serveur, dont voici un
rappel des grands principes.
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 8

Principe

Comme la programmation orientée objet, l'architecture client-serveur est une technologie informatique
très utilisée actuellement.
Elle est surtout connue dans le monde des SGBD (Système de Gestion de Base de Données ), mais elle
s'applique aussi à d'autres domaines comme Internet, les terminaux X utilisés sous UNIX (que l'on
appelle aussi serveurs X).
Son principe est assez simple mais ce sont des exemples qui vous permettront de mieux comprendre
comment cette architecture s'applique concrètement.

Comme son nom l'indique, le client-serveur implique qu'il y ait au moins deux acteurs en jeu, un client
et un serveur , qui communiquent entre eux, souvent à travers un réseau.
Un serveur est un programme ou par extension une machine programmée pour rendre toujours le même
service , suite à une requête qui lui est adressée.
Un client est un programme ou par extension une machine qui demande à un serveur un service , en lui
adressant une requête .
Gardez-bien toujours en tête ce mot service , car c'est souvent en pensant à qui rend service à l'autre que
vous arriverez à déterminer qui est le client du serveur dans une architecture client-serveur.

Exemples d'utilisation

figure 12. Architecture client-serveur sur Internet

Internet que vous utilisez en ce moment, est un bon exemple d'architecture client-serveur : Les sites que
vous consultez sont souvent appelés aussi des serveurs, car les programmes qui les gèrent rendent
toujours le même service au client qu'est votre navigateur.
Quel service ? Le programme d'un serveur est en attente que vous lui demandiez une page du site que
vous voulez visualiser. Quand votre requête lui parvient, il vous renvoie cette page. Votre navigateur de
son côté est un client du serveur car il lui demande une page qu'il visualise une fois téléchargée.

Le client-serveur est souvent utilisé pour accéder à de grandes bases de données utilisées par plusieurs
utilisateurs. La base de données est stockée sur une machine utilisée comme serveur. Le programme sur
ce serveur a pour mission de répondre à des requêtes SQL que lui adressent des clients. Les requêtes
SQL que le serveur reçoit lui permettent de modifier ou d'interroger la base de données qu'il gère, et de
renvoyer au client la réponse attendue. Le client est généralement une machine de type PC dont le
programme est utilisé pour mettre en page les réponses reçues du serveur.

Le principale avantage de cette architecture est qu'elle permet de séparer complètement le serveur qui
généralement gère le stockage de données, du client qui cherche à y accéder.
Ainsi, dans le cas d'Internet, il est possible d'avoir des navigateurs qui tournent sous différents systèmes
et même des logiciels de navigation différents sur un même système. Tous ces navigateurs sont des
clients du serveur d'un site Internet auquel ils peuvent accéder simultanément.
A l'opposé, le serveur d'un site Internet peut fonctionner sur n'importe quel système, que vous n'avez
d'ailleurs pas à connaître. Ce qu'il compte, c'est qu'il réponde correctement à vos requêtes, quand vous
voulez voir une page hébergée sur ce site.
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 9

La clé de voûte qui permet à ce système de fonctionner sans que le client et le serveur n'aient à savoir
comment ils fonctionnent l'un l'autre est l'utilisation d'un protocole commun. Ce protocole établit la
norme que doit respecter le client et le serveur pour communiquer entre eux : comment établir une
communication, comment un client doit envoyer une requête à un serveur et comment un serveur doit
répondre à la requête du client.

Protocoles

Deux types de protocoles peuvent être utilisés sur Internet :

TCP (Transport Control Protocol ) : Sans que vous ayez à le savoir, ce protocole est utilisé par
d'autres protocoles de plus haut niveau comme HTTP (HyperText Transfer Protocol ) et FTP (File
Transfer Protocol ).
Ce protocole permet d'établir une connexion entre deux machines (un client et un serveur) : Une
fois la connexion établie, les programmes client et serveur peuvent s'échanger des données, et ce
protocole garantit que les données émises d'une machine arriveront sur l'autre, et dans l'ordre où
elles ont été émises. Ainsi suite à une requête, le transfert d'un fichier peut être effectué en étant
assuré de son intégrité à l'arrivée.
Les classes URL et URLConnection utilisent TCP implicitement et permettent à un client d'accéder ne
manière simple à un fichier ou une ressource d'un serveur Internet. Ces classes sont généralement
suffisantes pour les communications utilisant les protocoles comme HTTP ou FTP. Mais Java
permet aussi de programmer des communications quelconques entre un client et un serveur en
utilisant TCP, grâce aux classes Socket et ServerSocket.
UDP (User Datagram Protocol ) : A l'opposé de TCP, ce protocole est utilisé pour transmettre des
données entre deux machines sans garantir que ces données arriveront à destination et dans l'ordre
où elles ont été émises. Ces données sont envoyées par paquets appelés aussi des datagrammes.
Ce protocole évite la contrainte d'avoir à établir et maintenir une connexion entre les deux
machines.
Il peut être utile par exemple pour un serveur envoyant les tics d'une horloge sur requête d'un
client. Si un datagramme contenant une heure donnée ne parvient pas au client ou son
acheminement est retardé, il est inutile de s'en inquiéter car au tic suivant le serveur renverra un
nouveau datagramme contenant l'heure actualisée. Si ces datagrammes arrivent dans le désordre, il
suffira au client de ne pas tenir compte des datagrammes contenant une heure obsolète.
Les classes DatagramSocket et DatagramPacket permet d'utiliser UDP pour programmer ce type de
communication entre un client et un serveur.

Port

Les serveurs des sites auxquels vous vous connectez sur Internet sont accessibles grâce à un
mnémonique qui est le nom de la machine hôte (par exemple java.sun.com, www.yahoo.fr). Chaque
mnémonique est associé à un identifiant numérique représentant la machine hôte qui héberge un serveur.
Cet identifiant est un nombre 32 bits appelé adresse IP que l'on note généralement sous la forme d'une
suite de 4 nombres séparés par des points (par exemple 194.51.83.2) : certains navigateurs affichent
dans la barre d'état l'adresse IP de l'hôte d'un site, quand vous essayez de vous connectez à ce site.
Chaque machine reliée à Internet possède une adresse IP différente qui permet de la désigner de manière
unique sur le réseau Internet.

Tous les systèmes d'exploitation permettent de faire fonctionner plusieurs programmes en même temps
sur une machine. Si plusieurs serveurs sont hébergés sur une même machine, ils vont devoir partager
l'accès physique au réseau sur cette machine. Un deuxième niveau d'identification est donc nécessaire
pour désigner le programme avec lequel vous voulez vous connecter sur une machine donnée.
A chaque programme désirant communiquer sur Internet est associé un port unique, qui est un nombre
16 bits compris entre 0 et 65535. Les ports dont le nombre est compris entre 0 et 1023 sont réservés à
des services particuliers et ne doivent pas être utilisés par les serveurs que vous programmez.

Globalement, un programme est identifié sur Internet par l'adresse IP de la machine sur lequel il
fonctionne et le numéro du port auquel il est associé.
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 10

Accès via les sockets

Chaque extrémité d'une connexion entre un client et un serveur est appelée un socket. Un socket est
une représentation logique du point de branchement d'un programme au réseau.

Pour programmer une architecture client serveur utilisant le protocole TCP (Transport Control Protocol ), il
vous faut réaliser, grâce aux classes ServerSocket et Socket qui suivent, un programme faisant office de
serveur sur une machine hôte et un autre utilisé comme client sur une autre machine.
Pour vous permettre de tester les programmes serveur et client sur la même machine, le client peut
essayer de se connecter à la machine localhost qui représente la machine locale.

figure 13. Utilisation des sockets en Java

Comme le montre la figure précédente, le programme serveur doit créer une instance de la classe
ServerSocket en précisant le numéro de port sur lequel il attend les demandes de connexion des
programmes clients. Le serveur appelle ensuite la méthode accept () de cette classe pour se mettre en
attente de demande de connexion.
Une fois qu'un client s'est connecté, une instance de la classe Socket est renvoyée. Le serveur peut alors
grâce au méthodes de cette classe obtenir des flux de données en lecture et en écriture pour
communiquer avec le programme client.

Le programme client de son côté se connecte à un programme serveur en créant une instance de la
classe Socket, en précisant l'adresse Internet de la machine hôte et le numéro de port sur lequel le
serveur attend les demandes de connexions.
Comme pour le programme serveur, une fois qu'il a obtenu une instance cette classe, il peut obtenir des
flux de données en lecture et en écriture.

Chacun des programmes serveurs et clients pouvant envoyer et lire des données, tout est alors prêt pour
communiquer des données dans les deux sens entre les programmes.
Pour un fonctionnement correct, il suffit que quand le client écrit sur le flux de données de sortie de
son socket, le serveur soit en lecture sur le flux de données d'entrée de son socket et inversement.

La classe java.net.InetAddress

Cette classe final permet de manipuler l'adresse Internet d'une machine hôte. Elle est utilisée par les
classes ServerSocket, Socket et DatagramPacket.

Méthodes

public static synchronized InetAddress getByName (String host)


throws UnknownHostException

Renvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine host.

public static synchronized InetAddress [ ] getAllByName (String host)


throws UnknownHostException

Renvoie toutes les adresses Internet de la machine host.

public static InetAddress getLocalHost () throws UnknownHostException


vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 11

Renvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine locale. Très
pratique pour tester sur une même machine les programmes client et serveur. Equivalent à getByName
(null) ou getByName ("localhost").

public String getHostName ()

Renvoie le nom de la machine hôte.

public byte [ ] getAddress ()

Renvoie l'adresse IP mémorisée par une instance de la classe InetAddress. Le tableau renvoyé contient
les 4 nombres d'une adresse IP qui sont séparées par des points (par exemple 194.51.83.2).

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe InetAddress à un objet ou renvoyer une chaîne de caractères décrivant une adresse
Internet.

Exemples

Applets EchoClient et PaperBoardClient .

La classe java.net.Socket

Cette classe final permet de manipuler un socket. Vous pouvez en obtenir une instance de deux
manières différentes, suivant que vous réalisez le programme client ou le serveur :

Quand un programme client essaye de se connecter à un serveur, il doit créer une instance de la
classe Socket grâce à l'un des constructeurs de cette classe (par exemple new Socket
("www.eteks.com", 26197)). Chacun de ses constructeurs prend comme paramètres l'adresse
Internet de la machine hôte du serveur et le numéro de port sur lequel le serveur est en attente de
connexion avec un client.
Quand un programme serveur en attente sur la méthode accept () reçoit une demande de
connexion avec un programme client, cette connexion est créée puis accept () renvoie une
instance de la classe Socket.

Une fois obtenue une instance de la classe Socket, il est possible d'obtenir côté client comme côté
serveur un flux de données d'entrée et un flux de données de sortie, grâce aux méthodes
getInputStream () et getOutputStream (). Ces méthodes renvoient des instances de classes dérivant
des classes InputStream et OutputStream dont les méthodes permettent de de lire et d'envoyer des
données entre le client et le serveur.

Constructeurs

public Socket (String host, int port)


throws UnknownHostException, IOException
public Socket (String host, int port, boolean stream)
throws IOException

Ces constructeurs permettent de créer une instance de la classe Socket et d'obtenir pour un programme
client une connexion avec le programme serveur de la machine host en attente sur le port port. Si
stream (égal à true par défaut) est égale à false, un socket de datagrammes est créé.

public Socket (InetAddress address, int port)


throws IOException
public Socket (InetAddress address, int port, boolean stream)
throws IOException

Ces constructeurs fonctionnent comme les deux premiers, mais prennent comme premier paramètre une
instance de la classe InetAddress.
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 12

Méthodes

public InputStream getInputStream () throws IOException


public OutputStream getOutputStream () throws IOException

Ces méthodes permettent d'obtenir un flux de données en lecture ou un flux de données en écriture,
pour communiquer avec le socket distant.

public synchronized void close () throws IOException

Ferme la connexion avec le socket distant.

public InetAddress getInetAddress ()


public int getPort ()

Ces méthodes renvoient l'adresse Internet et le port du socket distant auquel le socket est connecté.

public int getLocalPort ()

Renvoie le port local sur lequel le socket est connecté.

public static synchronized void setSocketImplFactory (SocketImplFactory fac)


throws IOException, SecurityException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création


de sockets, différent de celui fourni par défaut. Cette méthode static ne peut être appelée qu'une seule
fois.

public String toString ()

Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le socket.

Exemples

Applications EchoServer et PaperBoardServer .


Applets EchoClient et PaperBoardClient .

La classe java.net.ServerSocket

Cette classe final est utilisée par un programme serveur pour gérer l'attente de demande de connexion
d'un programme client sur un port de la machine hôte du serveur.

Constructeurs

public ServerSocket (int port) throws IOException


public ServerSocket (int port, int count) throws IOException

Ces constructeurs permettent de créer un socket de serveur en attente sur le port port. Pour autoriser la
connexion anonyme d'un client, utilisez un port est égal à 0. count (égal à 50 par défaut) permet de
spécifier le nombre maximum de demande de connexions en attente que le serveur termine d'exécuter la
méthode accept ().

Méthodes

public Socket accept () throws IOException

Attend la demande de connexion d'un programme client, et renvoie un socket une fois la connexion
établie.

public void close () throws IOException

Ferme le socket du serveur.

public InetAddress getInetAddress ()


public int getLocalPort ()
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 13

Ces méthodes renvoient l'adresse Internet et le port local sur lequel le socket du serveur est connecté.

public static synchronized void setSocketFactory


(SocketImplFactory fac) throws IOException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création


de socket de serveur, différent de celui fourni par défaut. Cette méthode static ne peut être appelée
qu'une seule fois.

public String toString ()

Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le serveur.

Exemples

Applications EchoServer et PaperBoardServer .

Le client serveur d'écho

Ce premier exemple de client serveur se décompose en deux programmes : une application pour le
serveur et une applet pour le client. Vous pouvez tester cet exemple sur votre ordinateur car l'applet se
connecte sur le serveur de la machine locale, où vous pouvez lancer l'application serveur.
Cet exemple réalise une opération très simple d'écho : le serveur ne fait que renvoyer en l'état les
données que le client lui envoie, jusqu'à ce qu'il rencontre un retour chariot.
Il faut lancer l'application du serveur avant de lancer l'applet du client.

Voici le programme du serveur. Recopiez-le dans un fichier EchoServer.java , que vous compilez avec
l'instruction javac EchoServer.java pour ensuite l'exécuter avec java ou Java Runner , grâce à
l'instruction java EchoServer : :

import java.net.*;
import java.io.*;

public class EchoServer


{
public static void main (String args [])
{
try
{
// Création d'un serveur sur le port 13267
ServerSocket server = new ServerSocket (13267);
// Attente de la connexion d'un client
Socket socket = server.accept ();

System.out.println ("Connexion sur le socket : " + socket);

// Récupération des flux d'entrés et de sortie


InputStream fluxEntree = socket.getInputStream ();
OutputStream fluxSortie = socket.getOutputStream ();

char caractereLu;
// Renvoie au client de chaque caractère lu
// jusqu'à ce que ce caractère soit un retour chariot
while ((caractereLu = (char)fluxEntree.read ()) != '\n')
fluxSortie.write (caractereLu);

server.close ();
}
catch (IOException e)
{
System.out.println (e);
}
}
}

Une fois programmé le serveur, il faut réaliser un programme client. Celui-ci est une applet simple qui
envoie au serveur tous les caractères saisis au clavier et affiche les caractères lus à partir du serveur sous
la forme d'une chaîne de caractères (à mettre dans un fichier EchoClient.java ) :
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 14

import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.*;

public class EchoClient extends Applet


{
String texteLu = "";
InputStream fluxEntree;
OutputStream fluxSortie;

// Méthode appelée par le système à l'initialisation de l'applet


public void init ()
{
try
{
// Récupération de l'adresse de l'hôte local
InetAddress adresse = InetAddress.getLocalHost ();
// Ouverture d'une connexion sur le port 13267 de cet hôte
Socket socket = new Socket (adresse, 13267);

// Récupération des flux d'entrés et de sortie


fluxEntree = socket.getInputStream ();
fluxSortie = socket.getOutputStream ();
}
catch (IOException e)
{
texteLu = "Probleme de connexion";
}
}

// Méthode appelée par le système pour mettre à jour le dessin de l'applet


public void paint (Graphics gc)
{
gc.drawString (texteLu, 10, 20);
}

// Méthode appelée le système quand une touche du clavier est enfoncée


public boolean keyDown (Event evt, int key)
{
if (fluxSortie != null)
try
{
// Envoie au serveur du caractère lu puis relecture
fluxSortie.write(key);
char caractereLu = (char)fluxEntree.read ();

// Ajout du caractère et redessin de l'applet


texteLu += caractereLu;
repaint();
}
catch (IOException e)
{ }
return true;
}
}

Voici un exemple de code HTML qui vous permettre d'appeler cette applet (à mettre par exemple dans
un fichier EchoClient.html et à exécuter avec appletviewer ou Applet Runner , grâce à l'instruction
appletviewer EchoClient.html) :

<applet code="EchoClient" width=200 height=30> </applet>

Le paper board Internet

Ce deuxième exemple de client serveur beaucoup plus élaboré se décompose toujours en deux
programmes : une application pour le serveur et une applet pour le client.
Chaque utilisateur visualisant cette applet peut partager un dessin qu'il dessine avec la souris, avec toutes
les autres personnes connectées au serveur. Pour saisir un élément de dessin, il suffit de déplacer la
souris en maintenant son bouton enfoncé.
Pour permettre plusieurs connexions simultanées de clients au serveur, ce dernier lance un thread gérant
chaque connexion. Chacun de ces threads a pour mission d'attendre les requêtes de chacun des clients.
Quatre types différents de requêtes sont gérées :
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 15

AJOUT : Le client envoie cette requête pour indiquer au serveur que l'utilisateur de l'applet client a
saisi une nouvelle suite de points (polyline ), avec la souris. Cette requête est suivie d'une liste de
valeurs entières représentant les couples x y de chacun des points de la polyline. Le serveur ajoute
cette liste à l'ensemble des polylines qu'il a déjà reçues.
LISTE : Le client envoie cette requête pour demander au serveur l'ensemble des polylines
actuellement enregistrées sur le serveur. Le serveur lui renvoie chacune des listes de points
séparée par des tabulations (caractère '\t'). Envoyée à intervalle régulier, cette requête permet à
chacun des clients du serveur de mettre à jour sa zone d'affichage en affichant toutes les polylines
qu'ont saisies l'ensemble des utilisateurs de l'applet client connectée au serveur.
CONNEXION : Le client envoie cette requête pour demander le nombre actuelle de clients connectés
au serveur. Ce dernier renvoie ce nombre.
FIN : Le client envoie cette requête pour indiquer au client son intention de se déconnecter du
serveur.

Pour tester cette applet, il faut une connexion Internet active. Cette version du manuel étant prévue
pour fonctionner off-line, vous pouvez éventuellement la tester sur
http://www.eteks.com/coursjava/net10.html#PaperBoard.

Voici le programme du serveur qui tourne sur www.eteks.com (A mettre dans un fichier
PaperBoardServer.java , et exécuté avec java ou Java Runner ) :

import java.net.*;
import java.io.*;
import java.util.*;

public class PaperBoardServer implements Runnable


{
public static int port = 26197;
public static String requeteFin = "FIN";
public static String requeteListe = "LISTE";
public static String requeteAjout = "AJOUT";
public static String requeteConnexion = "CONNEXION";

private static ServerSocket serveur;

public static void main (String args [])


{
if (lancerServeur () == null)
System.exit (-1);
}

// lancerServeur () peut être appelée par une servlet


// pour éviter de lancer une Machine Virtuelle supplémentaire
// sur le serveur où est hébergé le site
public static ServerSocket lancerServeur ()
{
if (serveur == null)
try
{
// Création d'un serveur sur le port 26197
serveur = new ServerSocket (port);
// Lancement d'un thread d'attente de connexion
new Thread (new PaperBoardServer ()).start ();
}
catch (IOException e)
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 16

{
System.out.println (e);
return null;
}

return serveur;
}

public void run ()


{
try
{
// Boucle sans fin d'attente de connexion
while (true)
{
// Attente de la connexion d'un client
Socket socket = serveur.accept ();
// Démarrage d'un nouveau thread pour gérer la connexion
new ThreadSocket (socket).start ();
}
}
catch (IOException e)
{
System.out.println (e);
}
finally
{
try
{
// Fermeture du serveur
serveur.close ();
serveur = null;
}
catch (IOException e)
{
System.out.println (e);
}
}
}
}

class ThreadSocket extends Thread


{
static private Vector listePolylines = new Vector (100);
static private int connexions;
private Socket socket;

public ThreadSocket (Socket socket)


{
this.socket = socket;
}

public void run ()


{
try
{
// Récupération des flux d'entrés et de sortie
DataInputStream fluxEntree = new DataInputStream (
new BufferedInputStream (socket.getInputStream ()));
PrintStream fluxSortie = new PrintStream (
new BufferedOutputStream (socket.getOutputStream ()), true);

// Augmentation du nombre de connexions au serveur


connexions++;

String chaineLue;
// Lecture sur le flux d'entrée tant que la requête FIN n'est pas arrivée
while ( (chaineLue = fluxEntree.readLine ()) != null
&& !chaineLue.equals (PaperBoardServer.requeteFin))
{
if (chaineLue.startsWith (PaperBoardServer.requeteListe))
{
// Si la liste des polylines est demandée, elles sont
// renvoyées séparées par des tabulations
for (Enumeration e = listePolylines.elements ();
e.hasMoreElements (); )
fluxSortie.print (e.nextElement ().toString () + '\t');
fluxSortie.write ('\n');
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 17

}
else if (chaineLue.equals (PaperBoardServer.requeteConnexion))
{
// Renvoi du nombre de connexions au serveur
fluxSortie.print (String.valueOf (connexions));
fluxSortie.write ('\n');
}
else if (chaineLue.startsWith (PaperBoardServer.requeteAjout))
{
// Si le vecteur arrive à saturation, il est vidée
// (ceci pour éviter que le dessin ne devienne trop dense)
if (listePolylines.size () == listePolylines.capacity ())
listePolylines.removeAllElements ();
// La nouvelle polyline reçue est ajoutée à la liste
listePolylines.addElement (
chaineLue.substring (PaperBoardServer.requeteAjout.length ()));
}
}

fluxEntree.close ();
fluxSortie.close ();
socket.close ();
}
catch (IOException e)
{
System.out.println (e);
}

connexions--;
}
}

Une fois programmé le serveur, il faut réaliser un programme client. Celui-ci est l'applet ci-dessus qui
envoie au serveur les polylines saisies par l'utilisateur, et demande au serveur toutes les secondes la liste
courante de toutes les polylines que celui-ci mémorise (à mettre dans un fichier PaperBoardClient.java ) :

import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;

public class PaperBoardClient extends Applet implements Runnable


{
private String message;
private Socket socket;
private DataInputStream fluxEntree;
private PrintStream fluxSortie;

private Vector listePolylines = new Vector (100);


private Polygon polylineCourante;

private int connexions;

// Méthode appelée par le système à l'affichage de l'applet


public void start ()
{
setBackground (Color.white);
try
{
// Récupération de l'adresse de l'hôte www.eteks.com
InetAddress adresse = InetAddress.getByName ("www.eteks.com");
// Ouverture d'une connexion sur le port 26197 de cet hôte
socket = new Socket (adresse, PaperBoardServer.port);

// Récupération des flux d'entrés et de sortie


fluxEntree = new DataInputStream (
new BufferedInputStream (socket.getInputStream ()));
fluxSortie = new PrintStream (
new BufferedOutputStream (socket.getOutputStream ()), true);

// Lancement d'un thread qui interroge à intervalle régulier


// la liste des polylines enregistrées sur le serveur
new Thread (this).start ();
}
catch (IOException e)
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 18

{
message = "Probleme de connexion avec le serveur";
}
}

// Méthode appelée par le système à la disparition de l'applet


public void stop ()
{
try
{
// Envoi d'une requête FIN et fermeture des flux
fluxSortie.println (PaperBoardServer.requeteFin);
fluxSortie.close ();
fluxEntree.close ();
socket.close ();
}
catch (IOException e)
{ }

fluxSortie = null;
}

public void run ()


{
try
{
while (fluxSortie != null)
{
// Envoi d'une requête CONNEXION pour récupérer le
// nombre de clients connectés au serveur
fluxSortie.print (PaperBoardServer.requeteConnexion);
fluxSortie.write ('\n');

message = fluxEntree.readLine () + " connexions";

// Envoi d'une requête LISTE pour récupérer


// la liste de toutes les polylines du serveur
fluxSortie.print (PaperBoardServer.requeteListe);
fluxSortie.write ('\n');

String liste = fluxEntree.readLine ();

// Vidange de la liste pour la remettre à jour


listePolylines.removeAllElements ();
StreamTokenizer tokens = new StreamTokenizer (
new StringBufferInputStream (liste));
tokens.parseNumbers ();
tokens.ordinaryChar ('\t');
tokens.whitespaceChars (' ', ' ');

// Décodage de la liste de points


while (tokens.nextToken () != StreamTokenizer.TT_EOF)
{
Polygon polyline = new Polygon ();
// Récupération des couples de valeurs (x,y)
// d'une polyline jusqu'à la prochaine tabulation
while (tokens.ttype != '\t')
{
int x = (int)tokens.nval;
tokens.nextToken ();
int y = (int)tokens.nval;
tokens.nextToken ();
polyline.addPoint (x, y);
}
// Ajout de la polyline à la liste
listePolylines.addElement (polyline);
}

repaint ();
// Arrête le thread pendant 1 s avant de lancer
// une nouvelle demande de mise à jour
Thread.sleep (1000);
}
}
catch (InterruptedException e)
{ }
catch (IOException e)
{ }
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 19

// Méthode appelée par le système pour mettre à jour le dessin de l'applet


public void paint (Graphics gc)
{
if (message != null)
gc.drawString (message, 10, 20);

// Dessin de toutes les polylines


// et de la polyline courante si elle existe
for (Enumeration e = listePolylines.elements ();
e.hasMoreElements (); )
drawPolyline (gc, (Polygon)e.nextElement ());

if (polylineCourante != null)
drawPolyline (gc, polylineCourante);
}

private void drawPolyline (Graphics gc, Polygon polyline)


{
for (int i = 1; i < polyline.npoints; i++)
gc.drawLine (polyline.xpoints [i - 1], polyline.ypoints [i - 1],
polyline.xpoints [i], polyline.ypoints [i]);
}

// Les méthodes mouseDown (), mouseDrag (), mouseUp () sont


// appelées par le système à l'enfoncement du bouton de la souris,
// au déplacement du pointeur, et au relâchement du bouton
public boolean mouseDown (Event evt, int x, int y)
{
polylineCourante = new Polygon ();
polylineCourante.addPoint (x, y);
return true;
}

public boolean mouseDrag (Event evt, int x, int y)


{
polylineCourante.addPoint (x, y);
paint (getGraphics ());
return true;
}

public boolean mouseUp (Event evt, int x, int y)


{
polylineCourante.addPoint (x, y);

// Construction d'une requête AJOUT avec la liste des points


// de la nouvelle polyline
fluxSortie.print (PaperBoardServer.requeteAjout);
for (int i = 0; i < polylineCourante.npoints; i++)
fluxSortie.print (String.valueOf (polylineCourante.xpoints [i])
+ ' ' + polylineCourante.ypoints [i] + ' ');

fluxSortie.write ('\n');
listePolylines.addElement (polylineCourante);
paint (getGraphics ());
return true;
}
}

Vous pouvez tester éventuellement ce programme sur votre machine locale en lançant le serveur
PaperBoardServer , et en remplaçant dans l'applet PaperBoardClient le nom du site Internet
"www.eteks.com" par "localhost".
Ce programme est inspiré du Groupboard disponible à l'adresse http://www.groupboard.com.

Vous n'aurez normalement pas à utiliser la classe et l'interface qui suivent, car elles sont utiles pour
réaliser l'implémentation des sockets que la Machine Virtuelle Java fournit.

La classe java.net.SocketImpl

Cette classe abstract permet de gérer un socket et la connexion au socket d'un serveur. Elle est utilisée
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 20

pour les sockets côté client comme côté serveur. Elle comporte des variables et des méthodes
protected qui sont utilisées pour réaliser les services des méthodes des classes précédentes Socket et
ServerSocket.

Variables

protected FileDescriptor fd
protected InetAddress address
protected int port
protected int localport

Constructeur

public SocketImpl ()

Méthodes

protected abstract void create (boolean stream) throws IOException


protected abstract void connect (String host, int port)
throws IOException
protected abstract void connect (InetAddress address, int port)
throws IOException
protected abstract void bind (InetAddress host, int port)
throws IOException
protected abstract void listen (int count) throws IOException
protected abstract void accept (SocketImpl s) throws IOException
protected abstract InputStream getInputStream () throws IOException
protected abstract OutputStream getOutputStream () throws IOException
protected abstract int available () throws IOException
protected abstract void close () throws IOException
protected FileDescriptor getFileDescriptor ()
protected InetAddress getInetAddress ()
protected int getPort ()
protected int getLocalPort ()

public String toString ()

L'interface java.net.SocketImplFactory

Cette interface est implémentée par les classes désirant gérer la création de sockets. Elle est utilisée
comme paramètre des méthodes setSocketImplFactory () de la classe Socket et setSocketFactory ()
de la classe ServerSocket, pour modifier le gestionnaire par défaut.

Méthode

public SocketImpl createSocketImpl ()

Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract SocketImpl.

Accès via les datagrammes

Pour programmer une architecture client serveur utilisant le protocole UDP (User Datagram Protocol ), il
faut utiliser les classes DatagramPacket représentant un paquet de données (ou datagramme) à recevoir
ou envoyer, et DatagramSocket utilisé pour recevoir et envoyer des datagrammes.

Contrairement au protocole TCP, le protocole UDP ne maintient pas de connexion permanente entre
deux machines. Chaque datagramme est indépendant l'un de l'autre : il comporte un tableau de données,
l'adresse Internet et le port de la machine à laquelle il est destiné.

La classe java.net.DatagramPacket

Cette classe final représente un datagramme. Le premier constructeur est utilisé pour créer un
datagramme à recevoir, le second pour créer un datagramme à envoyer à une machine.

Constructeurs
vendredi 11 février 2000 Du C/C++ à Java : Les accès au réseau Page: 21

public DatagramPacket (byte ibuf [ ], int ilength)

Construit une instance de la classe DatagramPacket utilisé pour recevoir un datagramme dont les paquets
de données de longueur ilength seront stockées dans le tableau ibuf. Si le datagramme reçu est plus
long que ilength, les données restantes ne sont pas recopiées dans ibuf.

public DatagramPacket (byte ibuf [ ], int ilength,


InetAddress iaddr, int iport)

Construit une instance de la classe DatagramPacket utilisé pour envoyer un paquet de données ibuf de
longueur ilength, au port iport d'une machine d'adresse Internet iaddr.

Méthodes

public InetAddress getAddress ()

Renvoie l'adresse Internet de la machine dont provient ou auquel est destiné ce datagramme.

public int getPort ()

Renvoie le port de la machine dont provient ou auquel est destiné ce datagramme.

public byte[] getData ()


public int getLength ()

Ces méthodes renvoient le tableau où sont stockées les données d'un datagramme et la longueur des
données à recevoir (ou reçues) ou à envoyer.

La classe java.net.DatagramSocket

Cette classe permet de recevoir et envoyer des datagrammes, grâce aux méthodes receive () et send ().

Constructeurs

public DatagramSocket () throws SocketException

Construit une instance de la classe DatagramSocket. Le port utilisé pour recevoir ou envoyer des
datagrammes est le premier disponible sur la machine locale.

public DatagramSocket (int port) throws SocketException

Construit une instance de la classe DatagramSocket. Le port port sur la machine locale est utilisé pour
recevoir ou envoyer des datagrammes.

Méthodes

public int getLocalPort ()

Renvoie le port de la machine locale sur lequel l'envoi ou la réception de datagrammes s'effectue.

public void synchronized receive (DatagramPacket packet) throws IOException

Permet de recevoir un paquet de données dans le datagramme packet. Cette méthode met en attente le
thread courant jusqu'à réception d'un datagramme.

public void send (DatagramPacket packet) throws IOException

Permet d'envoyer le datagramme packet.

public synchronized void close ()

Ferme le socket.

protected synchronized void finalize ()


vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 1

Les applications et les applets

Les applications Java


Les applets
L'intégration des applets dans les navigateurs
Transformer une applet en application

Ce chapitre décrit les applications Java et le package java.applet de Java 1.0 qui rassemble les classes
permettant de programmer une applet Java et de l'intégrer dans un navigateur.

Les applications Java

Comme il est décrit au premier chapitre, l'environnement Java vous permet de développer principalement
deux types de programmes : des applications Java ou des applets Java.

Les applications sont comparables à tout programme écrit dans un autre langage. Le point d'entrée d'une
application Java est la méthode main () de la classe donnée en argument à la commande java et
respectant la déclaration suivante :

public static void main (String [ ] args)

Comme n'importe quelle classe public ClasseExec qui définit cette méthode peut être exécutée par la
Machine Virtuelle Java, vous pouvez ajouter une méthode main () à une ou plusieurs classes utilisées
dans un même programme. Ceci permet de tester individuellement chacune de ces classes.
Attention, cette méthode est static et la Machine Virtuelle Java ne crée donc aucune instance de la
classe ClasseExec.
Les arguments passés après le nom de la classe, comme dans la ligne de commande java ClasseExec
arg0 arg1 sont mémorisés dans le tableau args (args [0] mémorise la chaîne arg0).

La méthode main () peut être suivie d'une clause throws pour citer toutes les classes d'exceptions
qu'elle n'intercepte pas. Ceci est pratique quand vous voulez écrire de petits programmes de test sans
vous soucier du traitement de certaines exceptions.

De manière comparable au C, le point d'entrée d'un programme Java, est la méthode static main ()
définie par la classe passée en argument à l'interpréteur Java.
La Machine Virtuelle Java rend la main au système une fois terminée l'exécution de la méthode main
(), si et seulement si plus aucun thread n'est vivant, excepté pour les threads en tâche de fond
(daemon ).
Vous pouvez aussi utiliser la méthode exit () de la classes System à tout moment pour forcer la
Machine Virtuelle à rendre la main.
Pour sa propre gestion, la bibliothèque AWT crée un ensemble de threads aussitôt qu'elle est utilisé.
Comme ces thread ne sont pas des daemons , il faut donc se servir de la méthode exit () pour forcer
la fin d'un programme utilisant AWT.

Les applets

Caractéristiques

Les applets sont des applications un peu spéciales et fonctionnent différemment des applications Java.
En voici les principales caractéristiques :
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 2

La classe d'une applet doit obligatoirement dérivé de la classe Applet.


Une applet de classe ClasseApplet est lancée grâce à la balise (tag ) <APPLET CODE=ClasseApplet
...> défini dans un fichier HTML .
Différents paramètres peuvent être passés à une applet grâce aux balise <PARAM NAME="Param"
VALUE="ValueParam"> inclus après la balise <APPLET ...>.
Une instance de la classe de l'applet demandée est créée pour chaque balise <APPLET ...>.
Une applet n'a pas un unique point d'entrée comme pour les applications : les méthodes
outrepassées init (), start (), stop () et destroy () d'une classe d'applet ClasseApplet sont
appelées respectivement à la création de l'applet ClasseApplet, son affichage, son effacement et sa
destruction.
De plus, pour chaque balise <APPLET CODE=ClasseApplet ...>, une instance de la classe
ClasseApplet est créée.
Une applet est une portion de fenêtre graphique : entendez par là que l'affichage d'informations
d'une applet est en mode graphique et pas en mode texte : On n'utilise pas la méthode println ()
dans une applet pour afficher du texte, mais plutôt la méthode graphique drawString ().
Comme pour une image, une applet s'affiche dans une zone de taille définie par la balise <APPLET
...> de la page HTML dans laquelle elle est exécutée.
La classe Applet dérive de la classe Panel et non de la classe Window, ce qui implique comme nous
le verrons que les applets ne peuvent bénéficier de services que l'on retrouve sur les fenêtres
isolées (menus, icone, titre,...).
La classe Applet et celles du package java.applet définissent des méthodes utiles dans un
navigateur Internet (chargement d'un fichier HTML , récupération de l'URL courante et des
paramètres passées à l'applet,...).
Pour éviter toute intrusion dans le système où fonctionne le navigateur, le gestionnaire de sécurité
(SecurityManager) défini par la Machine Virtuelle du navigateur est très restrictif :
Aucun accès au système de fichiers local.
Possibilité d'accéder uniquement à l'hôte sur lequel est hébergée le fichier de la classe de
l'applet.
Les fenêtres créées par une applet (de classe dérivée de la classe Window) comportent un
bandeau indiquant que la fenêtre a été créée par l'applet.
Impossibilité de lancer des applications locales.
Impossibilité de définir des méthodes native et d'utiliser des librairies du système.
Accès limité au propriétés du système de la Machine Virtuelle.

appletviewer et certains navigateurs permettent de modifier les paramètres du gestionnaire de


sécurité, pour autoriser certaines opérations. Mais si vous voulez réaliser des applets accessibles
publiquement sur Internet, concevez les en considérant que le niveau de sécurité des navigateurs
utilisés pour exécuter vos applets sera à son maximum.

Certaines caractéristiques qui précédent s'adressent à l'utilisation d'applet dans des pages HTML
affichées par un navigateur. Si vous utilisez appletviewer (ou Applet Runner sous MacOS), la commande
appletviewer pageweb.html créera une fenêtre isolée pour chaque applet définie par la balise <APPLET
...> du fichier pageweb.html.

On rencontre habituellement deux types d'applet :

Des applets dans lesquelles on dessine directement un dessin ou un texte en outrepassant la


méthode paint () de la classe Component, comme l'applet HelloWorld décrite au premier chapitre,
dont le résultat donne ceci :
Des applets qui sont utilisées comme container pour y inclure différents composants, en appelant
la méthode add () de la classe Container, comme l'applet AppletBouton décrite au chapitre
suivant, dont le résultat donne ceci :

La classe java.applet.Applet

La classe Applet appartient au package java.applet, et dérive des classes Panel, Container, Component,
du package java.awt. Ces dernières classes définissent beaucoup de méthodes utiles à la gestion de
fenêtres graphiques auxquelles la classe Applet ajoute des méthodes propres à la gestion des applets.
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 3

Constructeur

public Applet ()

Méthodes

public void init ()

Cette méthode est appelée automatiquement une fois au chargement du fichier HTML dans lequel une
applet est appelée. C'est le point d'entrée principal de l'applet (comparable à la méthode main () des
applications). Outrepassez cette méthode dans votre classe d'applet, pour initialiser votre applet.

public void start ()

Cette méthode est appelée à chaque fois qu'une applet est visualisée dans un navigateur ou dans
appletviewer . Si vous devez effectuer des opérations à chaque visualisation de votre applet, outrepassez
cette méthode dans votre classe d'applet.

public void stop ()

Cette méthode est appelée à chaque fois qu'une applet est effacée dans un navigateur ou dans
appletviewer (la page HTML où est visualisée l'applet n'est plus la page courante). Si par exemple votre
applet utilise un thread qui tourne continuellement, c'est l'endroit idéal pour l'arrêter, en outrepassant
cette méthode dans votre classe d'applet, comme dans l'exemple du chronomètre. En effet, n'oubliez pas
que si le thread n'est pas arrêté, il effectuera des opérations dont le résultat peut être inutile, puisque la
page HTML n'est plus affichée dans le navigateur.

public void destroy ()

Cette méthode est appelée quand le fichier HTML dans lequel une applet est appelée est supprimé de
la mémoire du navigateur. Si vous devez libérer certaines ressources utilisées par votre applet,
outrepassez cette méthode dans votre classe d'applet.

public String getAppletInfo ()

Cette méthode renvoie une chaîne de caractères d'information sur une applet. Outrepasser cette méthode
pour renvoyer une chaîne indiquant les renseignements concernant votre applet (Copyright, version,...) .

public String [ ][ ] getParameterInfo ()

Cette méthode renvoie un tableau de chaînes de caractères correspondant aux paramètres qu'il est
possible d'utiliser sur une applet. Outrepasser cette méthode pour renvoyer la liste des paramètres
accepter par votre applet. Le tableau doit être un ensemble de tableaux de 3 chaînes fournissant pour
chaque paramètre son nom, le type de valeur attendue et sa description, comme par exemple : {"param",
"0-10", "Description de param"}.

public void resize (int width, int height)


public void resize (Dimension d)

Permet de changer les dimensions d'une applet.

public URL getDocumentBase ()

Renvoie l'URL du fichier HTML dans lequel l'applet est incluse.

public URL getCodeBase ()

Renvoie l'URL du répertoire du fichier .class à partir duquel l'applet a été chargée (cette URL est
construite grâce à l'attribut CODEBASE du tag APPLET).

public String getParameter (String name)

Cette méthode permet de récupérer la valeur ValueParam du paramètre name de l'applet donné par la
balise <PARAM NAME="name" VALUE="ValueParam">. null est renvoyé si le paramètre name n'existe pas.
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 4

public AppletContext getAppletContext ()

Renvoie l'instance de la classe AppletContext, représentant le contexte dans lequel l'applet tourne
(fourni par le navigateur ou par appletviewer ).

public boolean isActive ()

Renvoie true si une applet est active. Une applet devient active à l'appel de sa méthode start ().

public void showStatus (String msg)

Cette méthode affiche le message msg dans la fenêtre d'état du navigateur (en général, cette fenêtre est
une barre d'état située en bas des fenêtres des navigateurs).

public Image getImage (URL url)


public Image getImage (URL url, String name)

Ces méthodes permettent d'obtenir l'image à l'URL url ou l'image name relative à url. L'image n'est
effectivement chargée qu'à sa première utilisation (voir le chargement des images). Les images peuvent
être aux formats GIF animé ou non, ou JPEG.

public AudioClip getAudioClip (URL url)


public AudioClip getAudioClip (URL url, String name)
public void play (URL url)
public void play (URL url, String name)

Ces méthodes permettent d'obtenir ou de jouer directement le fichier son à l'URL url ou le fichier son
name relatif à url.

public final void setStub (AppletStub stub)

Permet de donner à l'applet une instance stub de la classe implémentant AppletStub. Cette méthode est
appelée par le navigateur ou par appletviewer pour lier l'applet aux fonctionnalités fournies par les
interfaces AppletContext, AppletStub et AudioClip.

Les méthodes start () et stop () des classes Applet et Thread n'ont aucun lien entre elles. Une
applet n'est pas un thread, mais par contre, il est vrai que la plupart des navigateurs crée un thread pour
chaque applet d'une page HTML.

Exemples

Vous avez l'embarras du choix dans la liste des applets fournie dans la table des matières !

L'intégration des applets dans les navigateurs

La plupart des méthodes de la classe Applet font appel aux méthodes des interfaces suivantes. Les
classes implémentant ces interfaces sont fournies avec les navigateurs ou avec appletviewer .

L'interface java.awt.AppletContext

L'interface AppletContext représente le contexte dans lequel l'applet tourne.

Méthodes

public AudioClip getAudioClip (URL url)


public Image getImage (URL url)
public void showStatus (String status)

Voir la classe Applet.

public Applet getApplet (String name)


public Enumeration getApplets ()
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 5

Ces méthodes renvoient soit une applet en fonction de son nom (donné par la valeur de NAME dans la
balise <APPLET ...>), soit une énumération de toutes les applets du contexte dans lequel une applet
tourne. Par exemple, une fois que vous avez trouvé une applet, il est possible de communiquer avec elle
(voir l'exemple qui suit).

public void showDocument (URL url)


public void showDocument (URL url, String target)

Ces méthodes permettent de charger dans le navigateur ou dans appletviewer un nouveau document à
l'adresse url. target peut être égal à une des valeurs suivantes :

"_self" : le document est chargé dans le frame courant.


"_parent" : le document est chargé dans le frame parent.
"_top" : le document est chargé dans le frame de niveau le plus élevé.
"_blank" : le document est chargé dans une nouvelle fenêtre sans nom.
Une autre chaîne "Titre" : le document est chargé dans le cadre (frame ) dénommé Titre.

showDocument () est utilisé typiquement pour charger un document quand un utilisateur clique sur un
bouton défini dans une applet.

Exemples

Applets PlayApplet et BoutonsNavigation .

L'interface java.applet.AppletStub

L'interface AppletStub est utilisée pour intégrer une applet dans un navigateur ou dans appletviewer .

Méthodes

public boolean isActive ()


public URL getDocumentBase ()
public URL getCodeBase ()
public String getParameter (String name)
public AppletContext getAppletContext ()

Voir la classe Applet pour la définition de ces méthodes.

public void appletResize (int width, int height)

Cette méthode est appelée par les méthodes resize () de la classe Applet pour redimensionner une
applet dans la fenêtre d'un navigateur.

Voici un exemple d'utilisation des méthodes getParameter () et getApplet (), qui permet de retrouver
une applet appletControlee dont le nom est donné en paramètre. Une fois trouvée cette applet, on lui
associe un bouton qui appelle alternativement les méthodes start () ou stop () de l'applet
appletControlee pour la démarrer ou l'arrêter. Ici, l'applet de la classe Observable est reprise pour
illustrer cet exemple :

et le programme Java correspondant (à copier dans un fichier dénommé PlayApplet.java et invoqué à


partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class PlayApplet extends Applet


{
private Applet appletControlee = null;
private Button boutonDemarrerArreter;

public void init ()


{
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 6

String nomApplet = getParameter ("Applet");


String action = getParameter ("Action");

if (nomApplet != null)
try
{
Applet applet;
do
// Recherche de l'applet dont le nom égale nomApplet
if ( (applet = getAppletContext ()
.getApplet (nomApplet)) == null
|| !applet.isActive ())
// Si l'applet recherchée n'existe pas, attendre 1 seconde
// (l'applet n'est peut-être pas encore chargée)
Thread.sleep (1000);
else
appletControlee = applet;
while (appletControlee == null);

// L'applet a été trouvée, ajouter un bouton pour la contrôler


add (boutonDemarrerArreter = new Button (action));
// Si le paramètre action indique de démarrer l'applet au départ,
// arrêter l'applet pour l'utilisateur puisse la démarrer
if (action.equals ("D\u00e9marrer"))
appletControlee.stop ();
}
catch (InterruptedException e)
{ }
}

// Méthode appelée quand on clique sur le bouton


public boolean action (Event event, Object arg)
{
if (event.target == boutonDemarrerArreter)
{
// Arrêter ou démarrer l'applet suivant le nom du bouton
if ("Arr\u00eater".equals (arg))
appletControlee.stop ();
else
{
appletControlee.start ();
appletControlee.validate ();
}

// Changer le nom du bouton et le réafficher


boutonDemarrerArreter.setLabel ("Arr\u00eater".equals (arg)
? "D\u00e9marrer" : "Arr\u00eater");
boutonDemarrerArreter.invalidate ();
validate ();
return true;
}
return super.action (event, arg);
}
}

Si vous consultez le source de ce fichier HTML , vous pourrez voir comment utiliser l'applet
PlayApplet avec ses paramètres. Le paramètre de nom Applet vous permettant d'indiquer quel est le nom
de la classe de l'applet que vous voulez contrôler :

<APPLET CODE="ObservateurCalcul" CODEBASE="../classes"


NAME="calcul"
ALT="ObservateurCalcul" WIDTH=250 HEIGHT=30 ALIGN=middle>
</APPLET>
<APPLET CODE="PlayApplet" CODEBASE="../classes"
ALT="PlayApplet" WIDTH=80 HEIGHT=30 ALIGN=middle>
<PARAM NAME="Applet" VALUE="calcul">
<PARAM NAME="Action" VALUE="D&eacute;marrer">
</APPLET>

L'applet PlayApplet n'a pas qu'un intérêt didactique : elle permet de contrôler sans les modifier, des
applets qui utilisent des threads créés dans la méthode start () et arrêtés dans la méthode stop ().
Quand, comme dans certains exemples de ce manuel, vous utilisez des applets gourmandes en temps de
calcul, ceci permet de proposer à l'utilisateur que ces applets aient leur threads qui ne fonctionnent qu'à
sa demande (malheureusement la méthode getParameter () ne fonctionne pas avec les premières
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 7

versions de certains navigateurs sous Windows).

L'interface java.applet.AudioClip

L'interface AudioClip permet de jouer les fichiers sons chargés. Le seul type de son reconnu sous Java
1.0 et 1.1 est le type de fichier Sun .au (8 bit, law, 8000 Hz, mono). Si vous voulez convertir des
fichiers existants dans ce format, il existe des outils de conversion disponibles en shareware ou en
freeware sur Internet. Il est possible de mixer plusieurs sons ensemble (si vous exécutez les méthodes
play () ou loop () ensemble sur différents sons, les différents sons seront mélangés).

Méthodes

public void play ()

Cette méthode démarre le son de l'objet AudioClip. A chaque fois, que cette méthode est appelée, le son
redémarre.

public void loop ()

Démarre le son et le joue en boucle.

public void stop ()

Arrête le son. N'oubliez pas d'arrêter de jouer un son dans la méthode stop () d'une applet, si ce son ne
doit être joué qu'avec cette applet et surtout s'il est joué en boucle.

Voici un exemple d'utilisation de l'interface AudioClip, qui joue le son correspondant à la note d'un
octave de piano sur lequel on clique (il faut bien sûr une carte son sur votre ordinateur !):

et le programme Java correspondant (à copier dans un fichier dénommé Piano.java et invoqué à partir
d'un fichier HTML) :

import java.applet.*;
import java.awt.*;

public class Piano extends Applet


{
AudioClip [ ] octave = new AudioClip [12];
String [ ] notes = {"do.au", "dodiese.au", "re.au", "rediese.au", "mi.au",
"fa.au", "fadiese.au", "sol.au", "soldiese.au",
"la.au", "ladiese.au", "si.au"};
Rectangle [ ] rectNotes = new Rectangle [12];
boolean [ ] notesBlanches = {true, false, true, false, true,
true, false, true, false, true, false, true};

public void init ()


{
// Chargement des 12 fichiers de notes
for (int i = 0; i < notes.length; i++)
octave [i] = getAudioClip (getCodeBase (), notes [i]);

// Changement de la couleur de fond


setBackground (Color.white);
}

// Méthode appelée par la Machine Virtuelle quand l'applet change de taille


public void reshape (int newx, int newy, int largeur, int hauteur)
{
int largeurBlanche = largeur / 7;
int largeurNoire = largeurBlanche / 2;

// Calcul des rectangles représentant les 12 notes (noires ou blanches)


for (int x = 0, i = 0; i < notes.length;
x += notesBlanches [i++] ? largeurBlanche : 0)
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 8

if (notesBlanches [i])
rectNotes [i] = new Rectangle (x, 0, largeurBlanche - 1, hauteur - 1);
else
rectNotes [i] = new Rectangle (x - largeurNoire / 2, 0,
largeurNoire - 1, 2 * hauteur / 3);
// Rappel de la méthode de la super classe
super.reshape (newx, newy, largeur, hauteur);
}

// Méthode appelée par la Machine Virtuelle quand l'applet doit être dessinée
public void paint (Graphics gc)
{
gc.setColor (Color.black);
// Dessin des blanches
for (int i = 0; i < notes.length; i++)
if (notesBlanches [i])
gc.drawRect (rectNotes [i].x, rectNotes [i].y,
rectNotes [i].width, rectNotes [i].height);
// Dessin des noires
for (int i = 0; i < notes.length; i++)
if (!notesBlanches [i])
gc.fillRect (rectNotes [i].x, rectNotes [i].y,
rectNotes [i].width, rectNotes [i].height);
}

// Méthode recherchant la note à la position (x,y)


private AudioClip rechercherNote (int x, int y)
{
// Recherche d'abord parmi les noires puis les blanches car les
// rectangles des noires sont au-dessus de ceux des blanches
for (int i = 0; i < notes.length; i++)
if ( !notesBlanches [i]
&& rectNotes [i].inside (x, y))
return octave [i];
for (int i = 0; i < notes.length; i++)
if (rectNotes [i].inside (x, y))
return octave [i];
return null;
}

// Méthode appelée par la Machine Virtuelle


// quand le bouton de la souris est enfoncé
public boolean mouseDown (Event evt, int x, int y)
{
AudioClip note = rechercherNote (x, y);
if (note != null)
note.play ();
return true;
}
}

Les méthodes paint () et reshape () sont décrites dans le chapitre suivant. Les méthodes utilisées dans
la méthode paint () ainsi que la méthode outrepassée mouseDown () sont décrites dans le chapitre sur la
gestion de l'interface utilisateur.

Bien que le seul format reconnu actuellement réduise la taille (et la qualité) des fichiers sons, ceux-ci
sont très gourmands en octets. Rien que l'applet Piano qui utilise douze (petits) fichiers sons de 6 K en
moyenne, doit charger 72 K pour fonctionner. Donc faites attention à la taille de vos sons, si vous
voulez utiliser vos applets sur Internet.

Transformer une applet en application

Il est partiellement possible de créer une classe d'applet qui puisse aussi fonctionner comme application
isolée, en définissant une méthode main () qui crée une fenêtre dans laquelle l'applet est ajoutée (voir
l'exemple de la classe Frame).
Les méthodes suivantes de la classe Applet sont en fait des appels indirects aux classes qui
implémentent les interfaces AppletContext, AppletStub et AudioClip. Ces classes fournies par les
navigateurs ou Appletviewer , sont liées à une applet après sa création grâce à la méthode setStub () de la
classe Applet :
vendredi 11 février 2000 Du C/C++ à Java : Les applications et les applets Page: 9

getDocumentBase () interface AppletStub


getCodeBase () interface AppletStub
getParameter () interface AppletStub
getAppletContext () interface AppletStub
isActive () interface AppletStub
getImage () interface AppletContext
getAudioClip () interface AppletContext
showStatus () interface AppletContext
play () interface AudioClip

Si vous utilisez dans votre applet une de ces méthodes, pour éviter le déclenchement d'exception il vous
faut créer la ou les classes qui implémentent les méthodes des interfaces correspondantes, même si leur
implémentation ne fait rien (seule la méthode getImage () est aussi disponible dans la classe Toolkit).
Une instance de la classe implémentant AppletStub est associée à chaque applet grâce à la méthode
setStub (), une instance de la classe implémentant AppletContext est associée à chaque fenêtre de
navigateur et une instance de la classe implémentant AudioClip est associée à chaque son
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 1

Les composants de l'interface utilisateur

Les composants Java


La classe java.awt.Component
Les composants prédéfinis
Comment ça marche ?

Les composants Java

Le package java.awt (Abstract Window Toolkit) est le plus riche des packages Java. Il définit toutes les
classes permettant de construire et de gérer l'interface utilisateur. Les classes les plus importantes de ce
package sont celles qui permettent de créer les différents composants d'une interface utilisateur. Les
composants se répartissent en trois catégories :

1. Les composants prédéfinis sur lesquels l'utilisateur agit directement pour communiquer avec le
programme (dans d'autres environnements comme Windows, on les appelle des contrôles). Voici la
liste de ceux fournis avec le package java.awt :

Les boutons (de classe Button) : un programme déclenche une action quand
l'utilisateur clique sur un bouton (on entend par cliquer enfoncer et relâcher le
bouton de la souris avec le pointeur toujours positionné sur le bouton).
Les boites à cocher (de classe Checkbox) : ce sont des composants ayant deux
états possibles (coché ou non coché), qui permettent de proposer à l'utilisateur
d'activer ou désactiver une option par simple clic.
Les boutons radios (de classe Checkbox associés à un ensemble de classe
CheckboxGroup). A un moment donné, un seul bouton radio ne peut être choisi
parmi ceux de l'ensemble auquel il appartient. Ceci permet de proposer à
l'utilisateur un choix parmi plusieurs options.

Les composants de classe Choice (appelés aussi Combo box ou Drop down list
dans d'autres environnements) : Ce type de composant propose à l'utilisateur de
faire un choix parmi un certains nombres de chaînes de caractères affichées
dans une liste déroulante. Cette liste apparaît quand l'utilisateur clique sur la
flèche associée au composant et disparaît une fois que celui-ci clique sur une
des chaînes de la liste.
Les listes (de classe List) : ce sont des composants qui permettent par simple
clic, de sélectionner ou de déselectionner une ou plusieurs chaînes de
caractères affichées dans une liste.Contrairement au composant de classe
Choice, la liste reste toujours affichée. Les chaînes sélectionnées sont affichées
en inverse vidéo.
Les labels (de classe Label) : Ce sont les composants les plus simples ; un label
permet d'afficher une chaîne de caractères (titre, message, information décrivant
un composant juxtaposé,...).
Les zones de saisies de texte (composant avec une seule ligne TextField ou
avec plusieurs lignes TextArea) : Ces composants permettent à l'utilisateur de
saisir une chaîne de caractères.

Les ascenseurs (de classe Scrollbar) : ces composants sont utilisés en


association avec un autre composant (en général un container), quand la zone
d'affichage de celui-ci est trop petite pour contenir tout ce qu'il peut afficher.
L'ascenseur (horizontal et/ou vertical) permet alors à l'utilisateur de déplacer la
zone d'affichage sur d'autres portions du composant (comme on bouge des
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 2

jumelles pour voir différents détails d'un paysage).

2. Les composants dérivés de la classe Canvas : Si les composants prédéfinis ne satisfont pas un de
vos besoins, vous devez utiliser cette classe comme super classe pour créer vos propres
composants que vous dessinez en utilisant les méthodes de la classe Graphics.
3. Les containers : Contrairement aux autres composants, les containers sont utilisés pour contenir
d'autres composants de n'importe quelle catégorie ou pour afficher directement des dessins
fabriqués avec les méthodes de la classe Graphics. Les containers se subdivisent en deux sous
catégories qui héritent toutes deux de la classe abstract Container :
Les containers de type fenêtre (de classe Window ou ses dérivées) : Les fenêtres sont des
zones d'affichage indépendantes les unes des autres. Parmi les fenêtres, on distingue les
boites de dialogue (de classe Dialog) comportant un cadre et un titre, et les fenêtres de classe
Frame comportant un cadre, un titre, un menu éventuel, un pointeur de souris propre,... (et
correspondant aux fenêtres que vous avez l'habitude d'utiliser).
Les containers de classe Panel : Ce type de containers est une zone d'affichage occupant tout
ou partie d'une fenêtre. Notamment, la classe Applet dérive de Panel et permet d'afficher des
composants ou un dessin dans une fenêtre d'un navigateur.

Toutes les classes de composants citées ci-dessus dérivent directement ou indirectement d'une unique
classe : la classe Component. Cette classe décrit tous les comportements communs à tous les composants.

Voici la hiérarchie des classes qui dérivent de la classe Component :

java.awt.Component
java.awt.Button
java.awt.Canvas
java.awt.Checkbox
java.awt.Choice
java.awt.Container
java.awt.Panel
java.applet.Applet
java.awt.Window
java.awt.Dialog
java.awt.FileDialog
java.awt.Frame
java.awt.Label
java.awt.List
java.awt.Scrollbar
java.awt.TextComponent
java.awt.TextArea
java.awt.TextField

Quand vous recherchez les méthodes disponibles sur une classe dérivée de Component, n'oubliez pas
de consulter toutes les super classes dont elle hérite, pour connaître l'ensemble complet de ses
fonctionnalités.

Tous ces composants prennent l'aspect de ceux du système sur lequel la Machine Virtuelle fonctionne.
Et les menus dans tout ça ?! Les menus et sous-menus d'une fenêtre sont des instances des classes
dérivant de la classe abstract MenuComponent qui n'a pas de lien d'héritage avec la classe Component,
c'est pourquoi ils sont traités dans le paragraphe sur les menus.
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 3

Contrairement à d'autres systèmes (Windows, MacOS,...), il n'existe pas en Java 1.0 de concept de
fichier ressource utilisé pour décrire et créer l'interface utilisateur, et séparé du programme qui s'en
sert. Vous devez créer vous-même tous les composants et les ajouter au container qui les contient. Par
contre, leur disposition (position et taille) peut être gérée automatiquement par un layout associé à
chaque container.

La classe java.awt.Component

Cette classe abstract est la super classe de toutes les classes de composants citées ci-dessus, et
implémente l'interface ImageObserver. Elle comporte un très grand nombre de méthodes, qui sont
souvent outrepassées par les classes dérivées de Component.
Si vous devez créer une applet ou un programme avec une interface graphique, vous utiliserez les
classes de composants ou vous créerez de nouvelles classes qui en dérivent (de la classe Applet
notamment). Comme vous aurez à outrepasser ces méthodes ou vous les invoquerez directement ou
indirectement, il vous faut bien maîtriser l'utilité de la plupart d'entre elles.

Méthodes

public Container getParent ()


public ComponentPeer getPeer ()
public Toolkit getToolkit ()

Ces méthodes permettent d'obtenir le composant parent (par exemple le container auquel appartient un
bouton), le peer d'un composant et le kit d'outils (Toolkit ) gérant la création des composants à l'écran.

public boolean isValid ()


public boolean isVisible ()
public boolean isShowing ()
public boolean isEnabled ()

Ces méthodes permettent de savoir si un composant est valide, s'il est visible, s'il est affiché et s'il est
utilisable.
Un composant est invalide, quand son image à l'écran ne correspond pas aux valeurs de ses variables,
par exemple quand on change sa taille et que l'image du composant n'a pas encore mis à jour.
Tous les composants sont visibles par défaut sauf les fenêtres dont la classe est Window ou ses dérivées.
Attention, pour un composant qui n'est pas une fenêtre, visible n'implique pas que l'objet est à l'écran,
cela veut dire qu'il faudra l'afficher quand son container sera créé à l'écran
Un composant est affiché quand il est à écran, c'est-à-dire qu'il est visible ET que son container est à
l'écran.
Un composant est utilisable (enabled ), quand l'utilisateur peut s'en servir ; un composant qui n'est pas
utilisable (disabled ) est généralement grisé.

public Point location ()


public Dimension size ()
public Rectangle bounds ()

Ces méthodes renvoient la position, la taille et le rectangle englobant d'un composant. Les coordonnées
sont exprimées dans le système de coordonnées du parent du composant, sachant que le point (0,0) est
en haut à gauche et que l'axe des y descend vers le bas.

public synchronized void enable ()


public synchronized void disable ()
public void enable (boolean cond)

Vous pouvez rendre utilisable ou inutilisable un composant grâce à ces méthodes. Un composant
inutilisable (disabled ) est généralement grisé.

public synchronized void show ()


public synchronized void hide ()
public void show (boolean cond)

Ces méthodes permettent d'afficher ou d'effacer un composant. Ces méthodes sont surtout utilisées pour
afficher ou effacer une fenêtre à l'écran.
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 4

public Color getForeground ()


public synchronized void setForeground (Color c)
public Color getBackground ()
public synchronized void setBackground (Color c)
public Font getFont ()
public synchronized void setFont (Font f)
public synchronized ColorModel getColorModel ()

Utilisez ces méthodes pour obtenir ou modifier la couleur d'un composant, sa couleur de fond et la
police de caractères qu'il utilise pour afficher son texte (voir aussi la classe Graphics).

public void move (int x, int y)


public void resize (int width, int height)
public void resize (Dimension d)
public synchronized void reshape (int x, int y, int width, int height)

Ces méthodes sont utilisées pour déplacer et modifier la taille d'un composant. Généralement, on ne les
appelle pas directement pour placer un composant dans un container : ces composants sont disposés
automatiquement par le layout associé au container. Par contre, vous devez donner une dimension aux
fenêtres que vous créez.
Si vous voulez utiliser les nouvelles dimensions d'un composant comme dans l'applet Piano, outrepassez
la méthode reshape () car les trois autres méthodes appellent cette méthode.

public Dimension preferredSize ()


public Dimension minimumSize ()

Ces méthodes renvoient la taille préférée et la taille minimum d'un composant, pour que son affichage se
fasse correctement. Ces méthodes sont appelées par certains classes de layout, pour connaître les
dimensions correctes d'un composant.

public void layout ()

L'implémentation de cette méthode ne fait rien dans la classe Component. Elle est outrepassée notamment
par la classe Container, pour disposer les composants appartenant à un container.

public void validate ()

Si un composant est invalide (son image à l'écran ne correspond pas aux valeurs de ses variables), cette
méthode appelle layout (). Elle est outrepassée dans la classe Container pour mettre à jour tous les
composants d'un container invalide.

public void invalidate ()

Marque un composant et ses parents comme étant invalide. Ainsi, un appel à validate () pourra mettre
à jour ces composants invalides.

public Graphics getGraphics ()

Permet de d'obtenir un contexte graphique pour un composant, pour y effectuer des dessins. Cette
méthode renvoie null si le composant n'a pas encore été affiché.

public FontMetrics getFontMetrics (Font font)

Permet de récupérer les renseignements concernant une police de caractère (hauteur, largeur,...).

public void paint (Graphics gc)

Cette méthode est appelée quand le dessin d'un composant doit être mis à jour (à la première
visualisation d'un composant, quand il change de taille, quand une portion d'un composant cachée par
une fenêtre, est réaffichée,...). L'implémentation de la méthode paint () de la classe Component ne fait
rien. Quand vous créez de nouvelles classes de composants (dérivées d'une des classes Canvas, Applet,
Frame,...), vous devez outrepasser cette méthode pour dessiner le composant avec les méthodes de la
classe Graphics, si vous ne voulez pas vous en servir comme container.
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 5

Les composants Java n'utilisent pas le double-buffering (système utilisant une image cachée, copie de
l'image d'un composant à l'écran). C'est à dire que, par défaut, à chaque fois qu'un composant a besoin
d'être redessiné (même si ce dessin n'a pas changé), la méthode paint () de sa classe est appelée et
donc que toutes les instructions de cette méthode sont exécutées. Ceci implique deux inconvénients
majeurs :

Si le dessin est inchangé (comme par exemple, quand un composant caché par une fenêtre est
réaffichée), toutes les instructions de paint () sont réexécutées et ceci peut prendre un certain
temps.
L'utilisateur voit le dessin se fabriquer à l'écran, ce qui peut être gênant si notamment, vous
voulez faire des animations.

Par contre, l'avantage est que moins de mémoire est utilisée pour l'affichage d'un composant (pas de
besoin de mémoriser une image copie du composant à l'écran).
Le chapitre sur les images, décrit comment simuler un effet de double-buffering , en créant une image
dans laquelle vous dessinez avant de la copier à l'écran.

public void update (Graphics gc)

Cette méthode est appelée indirectement par les méthodes repaint (). La méthode update () remplit
d'abord le composant avec sa couleur de fond avant d'appeler paint (). Si le dessin réalisé par les
instructions de la méthode paint () occupe la totalité de la surface du composant, outrepassez la
méthode update () en appelant directement paint (), ceci évitera que le fond soit rempli inutilement.

public void paintAll (Graphics gc)

Redessine un composant et pour un container tous les composants lui appartenant.

public void repaint ()


public void repaint (long millisec)
public void repaint (int x, int y,
int width, int height)
public void repaint (long millisec, int x, int y,
int width, int height)

Redessine un composant. Ces méthodes provoque indirectement un appel à update (), soit le plus
rapidement possible, soit après au moins millisec millisecondes. x, y, width et height délimite la zone à
redessiner.

public void print (Graphics gc)

Imprime le composant. L'implémentation de print () de la classe Component appelle paint ().

public void printAll (Graphics gc)

Imprime un composant et pour un container tous les composants lui appartenant.

public synchronized boolean inside (int x, int y)

Vérifie si le point de coordonnées (x,y) appartient à la boite englobante du composant.

public Component locate (int x, int y)

L'implémentation de cette méthode renvoie le composant lui-même si le point de coordonnées (x,y)


appartient à sa boite englobante. Elle est outrepassée dans la classe Container pour rechercher le
composant situé au point de coordonnées (x,y) d'un container.

public Image createImage (int width, int height)


public Image createImage (ImageProducer producer)
public boolean prepareImage (Image image, ImageObserver observer)
public boolean prepareImage (Image image, int width, int height,
ImageObserver observer)
public int checkImage (Image image, ImageObserver observer)
public int checkImage (Image image, int width, int height,
ImageObserver observer)
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 6

public boolean imageUpdate (Image img, int flags,


int x, int y,
int width, int height)

Ces méthodes permettent de manipuler les images. Voir le chapitre sur les images pour plus de détails.
La méthode imageUpdate () est l'implémentation de la méthode de l'interface ImageObserver.

public void deliverEvent (Event evt)


public boolean postEvent (Event evt)
public boolean handleEvent (Event evt)
public boolean mouseDown (Event evt, int x, int y)
public boolean mouseDrag (Event evt, int x, int y)
public boolean mouseUp (Event evt, int x, int y)
public boolean mouseMove (Event evt, int x, int y)
public boolean mouseEnter (Event evt, int x, int y)
public boolean mouseExit (Event evt, int x, int y)
public boolean keyDown (Event evt, int key)
public boolean keyUp (Event evt, int key)
public boolean action (Event evt, Object what)
public boolean gotFocus (Event evt, Object what)
public boolean lostFocus (Event evt, Object what)

Ces méthodes sont utilisées pour la gestion événementielle des composants (voir le chapitre sur la
gestion de l'interface utilisateur).

public void requestFocus ()

Permet à un composant de lancer une requête pour obtenir le focus. Si le focus lui est accordé, la
méthode gotFocus () sera appelée.

public void nextFocus ()

Donne le focus au composant suivant parmi ceux de la liste des composants d'un container.

public void addNotify ()

Cette méthode est outrepassée par les classes dérivées de Component pour créer le peer d'un composant,
qui est sa représentation à l'écran. Elle est appelée indirectement par les méthodes qui gèrent l'affichage
des composants.

public synchronized void removeNotify ()

Cette méthode est appelée pour supprimer le peer d'un composant.

protected String paramString ()


public String toString ()

Ces méthodes sont utilisées pour fabriquer une chaîne de caractères décrivant le composant (toString
() outrepasse la méthode de la classe Object et appelle la méthode paramString () qui est outrepassée
par les classes dérivées de Component).

public void list ()


public void list (PrintStream out)
public void list (PrintStream out, int indent)

Imprime sur System.out ou sur out, la chaîne de caractères décrivant le composant.

Les composants prédéfinis

La classe java.awt.Button

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer et
de manipuler des boutons. Une fois créé un objet de classe Button, vous devez l'ajouter à un container
avec la méthode add () de la classe Container, pour le visualiser.

Constructeurs
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 7

public Button ()
public Button (String label)

Ces constructeurs permettent de créer un bouton avec ou sans label (texte affiché dans le bouton).

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un
bouton.

public String getLabel ()


public void setLabel (String label)

Ces méthodes permettent d'interroger ou de modifier le label d'un bouton.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
bouton.

Exemples

Applets PlayApplet , AppletButton , TraitementTexte , MessageBoxApplet , BorderBuilder , ShowMenu et


MiseAJourHorloge .

La classe java.awt.Checkbox

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer et
de manipuler des boites à cocher et les boutons radios. Une fois créé un objet de classe Checkbox, vous
devez l'ajouter à un container avec la méthode add () de la classe Container, pour le visualiser. Une
boîte à cocher ou un bouton radio ont deux états (coché ou non) représentés par une valeur boolean
(true ou false).
Pour qu'un objet de classe Checkbox soit un bouton radio, il doit être associé à une instance de
CheckboxGroup.

Constructeurs

public Checkbox ()
public Checkbox (String label)

Ces constructeurs permettent de créer une boite à cocher avec ou sans label (texte affiché à côté de la
boite à cocher). Par défaut, la boite à cocher n'est pas cochée.

public Checkbox (String label, CheckboxGroup group,


boolean state)

Ce constructeur permet de construire une boite à cocher, ou un bouton radio si group est différent de
null. Si state est égal à true la boite à cocher est cochée, ou le bouton radio est le bouton coché parmi
tous les boutons radios qui appartiennent au groupe group.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
boite à cocher ou d'un bouton radio.

public String getLabel ()


public void setLabel (String label)

Ces méthodes permettent d'interroger ou de modifier le label d'une boite à cocher ou d'un bouton radio.

public boolean getState ()


vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 8

public void setState (boolean state)

Ces méthodes permettent d'interroger ou de modifier l'état (coché ou non) d'une boite à cocher ou d'un
bouton radio.

public CheckboxGroup getCheckboxGroup ()


public void setCheckboxGroup (CheckboxGroup group)

Ces méthodes permettent d'interroger ou de modifier le groupe auquel appartient un bouton radio.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
composant.

Exemple

Applet DrawIt .

La classe java.awt.CheckboxGroup

Cette classe est la classe du package java.awt qui permet de gérer un ensemble de boutons radios. Parmi
tous les boutons radios (de classe Checkbox) associés à une instance de CheckboxGroup, au plus un seul
peut être coché.

Constructeur

public CheckboxGroup ()

Méthodes

public Checkbox getCurrent ()


public synchronized void setCurrent (Checkbox box)

Ces méthodes permettent d'interroger ou de modifier le bouton radio qui est actuellement coché.

public String toString ()

Exemple

Applet DrawIt .

La classe java.awt.Choice

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer des
composants proposant un choix parmi une liste déroulante. Une fois créé un objet de classe Choice,
vous devez l'ajouter à un container avec la méthode add () de la classe Container, pour le visualiser. A
chaque choix de la liste est associé un indice, compris entre 0 et le nombre de choix - 1.

Constructeur

public Choice ()

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
instance de la classe Choice.

public int countItems ()

Renvoie le nombre de choix possibles dans le composant.

public String getItem (int index)


vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 9

Renvoie la chaîne de caractères correspondante au choix d'indice index.

public synchronized void addItem (String item)

Permet d'ajouter en fin de liste un nouveau choix égal à la chaîne de caractères item.

public String getSelectedItem ()


public int getSelectedIndex ()

Ces méthodes renvoient soit la chaîne de caractères soit l'indice du choix courant.

public synchronized void select (int index)


throws IllegalArgumentException
public void select (String str)

Ces méthodes permettent de désigner le choix courant du composant, soit en donnant son indice index,
soit en donnant sa valeur str.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
composant.

Exemple

Applet CalculetteSimple .

La classe java.awt.List

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer des
listes permettant de sélectionner un ou plusieurs choix parmi plusieurs chaînes de caractères. Une fois
créé un objet de classe List, vous devez l'ajouter à un container avec la méthode add () de la classe
Container, pour le visualiser. A chaque choix de la liste est associé un indice, compris entre 0 et le
nombre de choix - 1. Il est possible de définir le nombre de lignes qu'une liste peut afficher en même
temps, sans utiliser l'ascenseur associée à la liste, mais ce nombre de lignes et la taille du composant
dépendent aussi du layout utilisé.

Constructeur

public List ()

Ce constructeur construit une liste dont le nombre de lignes affichées n'est pas déterminé, et qui
n'autorise pas la sélection multiple.

public List (int rows, boolean multipleSelections)

Ce constructeur construit une liste dont le nombre de lignes affichées est égal à rows, et qui autorise la
sélection multiple si multipleSelections est égal à true.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une liste.

public synchronized void removeNotify ()

Cette méthode outrepasse la méthode removeNotify () de la classe Component pour supprimer le peer
d'une liste.

public int countItems ()

Renvoie le nombre de choix possibles dans la liste.


vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 10

public String getItem (int index)

Renvoie la chaîne de caractères correspondante au choix d'indice index.

public synchronized void addItem (String item)

Permet d'ajouter en fin de liste un nouveau choix égal à la chaîne de caractères item.

public synchronized void addItem (String item, int index)

Permet d'insérer à l'indice index un nouveau choix égal à la chaîne de caractères item (si index est égal à
-1, item est ajouté en fin de liste).

public synchronized void replaceItem (String newValue, int index)

Remplace la chaîne de caractères à l'indice index par newValue.

public synchronized void clear ()

Supprime toutes les chaînes de caractères de la liste.

public synchronized void delItem (int index)


public synchronized void delItems (int start, int end)

Supprime le choix à l'indice index, ou tous les choix compris entre les indices start et end.

public synchronized String getSelectedItem ()


public synchronized int getSelectedIndex ()

Ces méthodes renvoient soit la chaîne de caractères sélectionnée soit son indice (en cas de sélection
multiple seule la première est renvoyée).

public synchronized String [ ] getSelectedItems ()


public synchronized int [ ] getSelectedIndexes ()

Ces méthodes renvoient soit un tableau des chaînes de caractères sélectionnées soit un tableau de leur
indice.

public synchronized void select (int index)


public synchronized void deselect (int index)

Ces méthodes permettent de sélectionner ou de désélectionner une chaîne de caractères en donnant son
indice index.

public synchronized boolean isSelected (int index)

Renvoie true si la chaîne de caractères d'indice index est sélectionnée.

public int getRows ()

Renvoie le nombre de lignes visibles dans la liste.

public boolean allowsMultipleSelections ()

Renvoie true si une liste autorise la sélection multiple.

public void setMultipleSelections (boolean v)

Permet d'autoriser ou non la sélection multiple dans une liste.

public int getVisibleIndex ()

Renvoie l'indice de la chaîne de caractères qui a été rendue visible lors d'un précédent appel à la
méthode makeVisible ().

public void makeVisible (int index)


vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 11

Force la chaîne de caractères d'indice index à être visible à l'écran.

public Dimension preferredSize (int rows)


public Dimension preferredSize ()
public Dimension minimumSize (int rows)
public Dimension minimumSize ()

Méthodes de la classe Component, outrepassées pour renvoyer la taille préférée ou la taille minimum
d'une liste.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant la liste.

Voici un programme Java simple utilisant une liste : c'est celui de l'applet donnant la liste des caractères
Unicode compris entre '\u0080' et '\u00ff'(à copier dans un fichier dénommé Unicode.java et invoqué
à partir d'un fichier HTML)

import java.awt.*;
import java.applet.*;

public class Unicode extends Applet


{
public void init ()
{
// Utilisation d'un layout grille 1 x 1 qui permet
// à la liste d'occuper tout l'espace de l'applet
setLayout (new GridLayout (1, 1));

// Changement de police de caractères


setFont (new Font("Courier", Font.PLAIN, 12));

// Création d'une liste affichant les caractères


// Unicode compris entre '\u0080' et '\u00ff'
List listeCaracteres = new List ();
for (short i = 0x0080; i <= 0x00ff; i++)
listeCaracteres.addItem ( "\\u00" + Integer.toHexString (i)
+ " " + new Character ((char)i));
// Ajout de la liste au container
add (listeCaracteres);
}
}

Autre exemple

Applet ListePolices .

La classe java.awt.Label

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer et
de manipuler des labels. Une fois créé un objet de classe Label, vous devez l'ajouter à un container avec
la méthode add () de la classe Container, pour le visualiser.

Variables

public final static int LEFT


public final static int CENTER
public final static int RIGHT

Constantes utilisées pour l'alignement du texte dans le composant.

Constructeurs

public Label ()
public Label (String label)
public Label (String label, int alignment)

Ces constructeurs permettent de créer un label avec ou sans texte, et en précisant éventuellement
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 12

l'alignement du texte dans le composant (égal à LEFT, CENTER ou RIGHT).

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un label.

public int getAlignment ()


public void setAlignment (int alignment) throws IllegalArgumentException

Ces méthodes permettent d'interroger ou de modifier l'alignement du texte dans un label (égal à LEFT,
CENTER ou RIGHT).

public String getText ()


public void setText (String label)

Ces méthodes permettent d'interroger ou de modifier le texte d'un label.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le label.

Exemples

Applets CalculetteSimple , TraitementTexte , MessageBoxApplet , MiseAJourHorloge , ListePolices et Nuancier .

La classe java.awt.TextComponent

Cette classe qui dérive de la classe Component, est la super classe des classes de composants TextField
et TextArea qui permettent de créer des zones de saisies de texte. Cette classe regroupe l'ensemble des
méthodes communes à ces deux classes. Comme elle n'a pas de constructeur public, vous ne pouvez
créer d'instance de cette classe.

Méthodes

public synchronized void removeNotify ()

Cette méthode outrepasse la méthode removeNotify () de la classe Component pour supprimer le peer
d'une zone de saisie.

public void setText (String t)


public String getText ()

Ces méthodes permettent d'interroger ou de modifier le texte d'une zone de saisie.

public boolean isEditable ()

Renvoie true si le texte d'une zone de saisie est éditable.

public void setEditable (boolean t)

Permet de spécifier si le texte d'une zone de saisie est éditable ou non.

public String getSelectedText ()


public int getSelectionStart ()
public int getSelectionEnd ()

Ces méthodes permettent d'interroger le texte en cours de sélection dans une zone de saisie ou l'indice
du début et de la fin du texte sélectionné.

public void select (int selStart, int selEnd)


public void selectAll ()

Ces méthodes permettent de sélectionner une partie du texte de la zone de saisie (comprise entre les
indices selStart et selEnd) ou tout le texte de la zone de saisie.
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 13

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant la zone
de saisie.

La classe java.awt.TextField

Cette classe qui dérive des classes TextComponent et Component, est la classe du package java.awt qui
permet de créer et de manipuler des zones de saisies de texte d'une seule ligne. Une fois créé un objet
de classe TextField, vous devez l'ajouter à un container avec la méthode add () de la classe Container,
pour le visualiser. Il est possible d'utiliser les zones de saisie de cette classe pour la saisie de mots de
passe en précisant un caractère qui masque les caractères saisis. Toutes les méthodes permettant de
manipuler le texte de la zone de saisie sont dans la classe TextComponent.

Constructeurs

public TextField ()
public TextField (int cols)
public TextField (String text)
public TextField (String text, int cols)

Ces constructeurs permettent de créer une zone de saisie avec ou sans un texte de départ, et en
précisant éventuellement le nombre de colonnes que doit occuper la zone de saisie.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
zone de saisie d'une seule ligne.

public boolean echoCharIsSet ()

Renvoie true si un caractère de masque est positionné.

public char getEchoChar ()


public void setEchoCharacter (char c)

Ces méthodes permettent d'interroger ou de modifier le caractère de masque en cours d'utilisation.

public int getColumns ()

Renvoie le nombre de colonnes occupées par la zone de saisie.

public Dimension preferredSize (int cols)


public Dimension preferredSize ()
public Dimension minimumSize (int cols)
public Dimension minimumSize ()

Méthodes de la classe Component, outrepassées pour renvoyer la taille préférée ou la taille minimum
d'une zone de saisie.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant la zone
de saisie.

L'applet CalculetteSimple qui suit est un exemple simple de l'utilisation des classes TextField, Label et
Choice. Elle permet d'afficher le résultat d'une opération simple (+, -, * et /) entre deux opérandes. Cet
exemple montre au passage comment convertir une chaîne de caractères en un nombre d'un type de base
grâce à la méthode valueOf () de la classe Float (les classes Integer, Long et Double fournissent aussi
des méthodes similaires de conversion).
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 14

et le programme Java correspondant (à copier dans un fichier dénommé CalculetteSimple.java et invoqué à


partir d'un fichier HTML) :

import java.awt.*;
import java.applet.*;

public class CalculetteSimple extends Applet


{
TextField operande1 = new TextField ("2000");
TextField operande2 = new TextField ("1000");
Choice choixOperateur = new Choice ();
Label labelResultat = new Label ("3000 ");

public void init ()


{
// Ajout des opérateurs à la liste
choixOperateur.addItem (" + ");
choixOperateur.addItem (" - ");
choixOperateur.addItem (" * ");
choixOperateur.addItem (" / ");

// Ajout des composants à l'applet


add (operande1);
add (choixOperateur);
add (operande2);
add (labelResultat);
}

// Méthode appelée par la machine virtuelle quand on effectue


// une action sur un des composants
public boolean action (Event event, Object eventArg)
{
try
{
// Récupération des valeurs saisies dans les deux champs de saisies
// La méthode static valueOf () de la classe Float est utilisée pour
// convertir le texte saisi en un nombre. Cette méthode déclenche une
// exception de classe NumberFormatException si la chaîne de caractères
// à convertir n'est pas un nombre
float valeur1 = Float.valueOf (operande1.getText ()).floatValue ();
float valeur2 = Float.valueOf (operande2.getText ()).floatValue ();

float resultat = 0;
// Calcul suivant l'opérateur choisi
switch (choixOperateur.getSelectedItem ().charAt (1))
{
case '+' : resultat = valeur1 + valeur2;
break;
case '-' : resultat = valeur1 - valeur2;
break;
case '*' : resultat = valeur1 * valeur2;
break;
case '/' : resultat = valeur1 / valeur2;
break;
}

labelResultat.setText (" = " + resultat);


}
catch (NumberFormatException e)
{
// Exception si un des deux opérandes n'est pas un nombre
labelResultat.setText (" (Calcul impossible)");
}
return true;
}
}

Autre exemple

Applet ListePolices .
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 15

La classe java.awt.TextArea

Cette classe qui dérive des classes TextComponent et Component, est la classe du package java.awt qui
permet de créer et de manipuler des zones de saisies de texte de plusieurs lignes. Une fois créé un objet
de classe TextArea, vous devez l'ajouter à un container avec la méthode add () de la classe Container,
pour le visualiser. Il est possible d'utiliser les zones de saisie de cette classe pour la saisie de mots de
passe en précisant un caractère qui masque les caractères saisis. Toutes les méthodes permettant de
manipuler le texte de la zone de saisie sont dans la classe TextComponent. Un exemple plus complet
utilisant la classe TextArea est donné au chapitre suivant.

Constructeurs

public TextArea ()
public TextArea (int rows, int cols)
public TextArea (String text)
public TextArea (String text, int rows, int cols)

Ces constructeurs permettent de créer une zone de saisie avec ou sans un texte de départ, et en
précisant éventuellement le nombre de colonnes et de lignes que doit occuper la zone de saisie.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
zone de saisie de plusieurs lignes.

public void insertText (String str, int pos)

Permet d'insérer la chaîne de caractères str à la position pos dans le texte de la zone de saisie.

public void appendText (String str)

Permet d'ajouter la chaîne de caractères str à la fin du texte de la zone de saisie.

public void replaceText (String str, int start, int end)

Permet de remplacer par la chaîne de caractères str la partie du texte de la zone de saisie comprise entre
les indices start et end.

public int getRows ()


public int getColumns ()

Ces méthodes permettent d'interroger le nombre de colonnes ou le nombre de lignes occupées par la
zone de saisie.

public Dimension preferredSize (int cols)


public Dimension preferredSize ()
public Dimension minimumSize (int cols)
public Dimension minimumSize ()

Méthodes de la classe Component, outrepassées pour renvoyer la taille préférée ou la taille minimum
d'une zone de saisie.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant la zone
de saisie.

Exemple

Applet TraitementTexte .

La classe java.awt.Scrollbar
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 16

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer et
de manipuler des ascenseurs. Une fois créé un objet de classe Scrollbar, vous devez l'ajouter à un
container avec la méthode add () de la classe Container, pour le visualiser.

Variables

public final static int HORIZONTAL


public final static int VERTICAL

Constantes utilisées pour préciser l'orientation d'un ascenseur.

Constructeurs

public Scrollbar ()
public Scrollbar (int orientation)
public Scrollbar (int orientation, int value, int visible,
int minimum, int maximum )

Ces constructeurs permettent de créer un ascenseur en précisant éventuellement son orientation égale à
HORIZONTAL ou VERTICAL (valeur par défaut).
minimum et maximum sont des entiers représentant les valeurs logiques minimale et maximale que peut
prendre l'ascenseur (par exemple, 0 et 100 ou -500 et 500). On entend par logique que ces valeurs ne
sont pas des valeurs en pixels ou représentant une grandeur physique de l'ordinateur, ce sont des valeurs
arbitraires qui n'ont un sens que dans la logique de votre programme (nombre de lignes, valeurs sur un
axe de coordonnées,...).
value représente la position logique initiale de l'ascenseur (comprise entre minimum et maximum).
visible représente la taille logique de la portion visible ; par exemple, si un ascenseur verticale est
associé à un dessin dont vous ne pouvez afficher que le tiers en hauteur, visible sera égal à (maximum -
minimum) / 3.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un
ascenseur.

public int getOrientation ()

Renvoie l'orientation de l'ascenseur ( HORIZONTAL ou VERTICAL).

public int getValue ()


public void setValue (int value)

Ces méthodes permettent d'interroger ou de modifier la position courante d'un ascenseur. Si value est
plus petite que le minimum de l'ascenseur ou plus grande que le maximum de l'ascenseur, ces valeurs
prennent respectivement value pour nouvelle valeur (ceci permet de rajouter facilement des "pages").

public int getMinimum ()


public int getMaximum ()
public int getVisible ()

Ces méthodes permettent d'interroger le minimum, le maximum et la taille logique de la portion visible
d'un ascenseur.

public int getLineIncrement ()


public void setLineIncrement (int increment)

Ces méthodes permettent d'interroger ou de modifier la valeur increment à ajouter ou à retrancher à la


valeur de la position courante, quand l'utilisateur clique sur les flèches de l'ascenseur.

public int getPageIncrement ()


public void setPageIncrement (int increment)

Ces méthodes permettent d'interroger ou de modifier la valeur increment à ajouter ou à retrancher à la


vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 17

valeur de la position courante, quand l'utilisateur clique dans l'ascenseur.

public void setValues (int value, int visible,


int minimum, int maximum)

Modifie la valeur courante, la taille logique de la portion visible, le minimum et le maximum d'un
ascenseur.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant
l'ascenseur.

Comment ça marche ?

Afin de comprendre l'enchaînement des différentes méthodes appelées par le navigateur et une applet,
l'applet suivante vous montre le comportement d'une applet simple utilisant un bouton, dont voici le
programme (simplifié) :

import java.applet.Applet;
import java.awt.*;

public class AppletButton extends Applet


{
public void init ()
{
Button bouton = new Button ("OK");
add (bouton);
}
}

Sous le bouton, une liste affichant l'ensemble des appels aux méthodes a été ajoutée.

Le peer d'un composant

Si vous pouvez visualiser cette page sous différents systèmes, vous verrez que les composants du
package java.awt prennent à chaque fois l'aspect de ceux du système sur lequel le navigateur
fonctionne. En regardant le programme Java des classes Component ou de ses dérivées, vous découvrirez
que toutes ces classes ne comportent aucune instruction spécifique à chacun des systèmes : par exemple,
la classe Button décrite dans le fichier Button.java, est la même pour toutes les Machines Virtuelles
Java que vous soyez sous Windows, MacOS, UNIX,... Comment ceci est-il possible alors qu'un bouton
n'a pas le même aspect et est géré différemment sous chacun de ces systèmes ?
En fait, chaque composant du package java.awt a son pair (peer en anglais) auquel il est associé. La
classe de ce deuxième objet implémente l'interface Peer associée à la classe du composant (par exemple
l'interface ButtonPeer pour un composant de classe Button). Cet objet est mémorisé dans la variable
private peer de la classe Component. L'ensemble des interfaces Peer sont déclarées dans le package
java.awt.peer.
La variable peer d'interface ComponentPeer permet de mémoriser un objet dont la classe implémente
cette interface ou une de ses dérivées comme par exemple ButtonPeer. Pour créer un objet d'interface
ButtonPeer, il faut qu'il existe quelque part une classe implémentant cette interface. Si vous effectuez
une recherche dans toutes les classes du package java.awt, vous verrez que Java ne fournit aucune
classe implémentant ButtonPeer !?! Et c'est là qu'est l'astuce : chaque Machine Virtuelle fournit des
classes dont l'utilisateur n'a pas connaissance et qui implémentent chacune des interfaces Peer . Ces
classes déclarent des méthodes native, dont l'implémentation fait appel aux fonctions du système sur
lequel la Machine Virtuelle fonctionne (comme par exemple la fonction CreateWindow () utilisée pour
créer un composant sous Windows).
Reste une chose : Comment sont liées les classes de composant du package java.awt et les classes de la
machine virtuelle implémentant les interfaces Peer ? Par exemple, comment la classe Button fait pour
retrouver la classe qui implémente l'interface ButtonPeer, pour pouvoir créer un objet de cette classe ?
Il existe une classe Toolkit qui est abstract. En observant cette classe vous verrez qu'il existe toutes
les méthodes qui permettent de créer un objet d'interface Peer, et notamment la méthode createButton
() qui renvoie un objet d'interface ButtonPeer. Toutes ces méthodes de création sont abstract. La
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 18

Machine Virtuelle définit une classe dérivant de Toolkit qui implémente toutes ces méthodes abstract.
Par exemple, la méthode createButton () va être implémentée de manière à créer une instance de la
classe implémentant ButtonPeer.
Le nom de la classe dérivée de Toolkit est mémorisée dans la propriété "awt.toolkit". Grâce à cette
propriété, la méthode static getDefaultToolkit () de la classe Toolkit peut, en appelant
System.getProperty ("awt.toolkit"), retrouver le nom de cette classe et l'instancier avec
Class.forName (nomClasseDeriveeDeToolkit).newInstance (). Ainsi, après avoir obtenu une instance
de Toolkit, un bouton crée son pair en appelant createButton (), et le tour est joué !
L'appel aux fonctions de la classe Toolkit, s'effectue dans la méthode addNotify () d'un composant et
non dans le constructeur d'un composant. Cette méthode est invoquée soit quand on ajoute un
composant à un container avec la méthode add (), soit pour les fenêtres dont la classe dérive de Window
quand on appelle la méthode show (). La méthode show () appelle addNotify () pour la fenêtre et pour
tous les composants qui lui ont été ajoutés.
En conclusion, les classes de composants du package java.awt sont en fait des représentations logiques
des composants et leur objet peer est une représentation à l'écran du composant.

Pour mieux visualiser le raisonnement précédent, la figure suivante montre l'enchaînement des méthodes
appelées pour créer un bouton :

figure 14. Etapes de la création d'un bouton

Si vous ne maîtrisez pas encore la notion d'interface ou le fonctionnement des composants d'une
interface utilisateur, vous risquez sûrement de trouver fumeuse l'explication précédente. Après avoir
écrit quelques programmes Java, votre curiosité vous amènera peut-être à y revenir.
De toute façon, ce raisonnement a été construit en consultant le code source de Java alors n'hésitez
pas à faire de même pour plus de renseignements.
Ce système comporte deux inconvénients importants :

Vous ne pouvez pas modifier l'aspect d'un composant prédéfini car c'est le peer de ces
composants qui gère leur dessin.
Donc, si par exemple, vous voulez créer un bouton affichant un dessin à la place d'un texte, ne
créez pas une classe dérivée de Button en outrepassant la méthode paint (), mais plutôt une
classe dérivée de Canvas. Ceci implique que dans cette classe, vous devrez non seulement
redessiner entièrement le bouton en outrepassant la méthode paint (), mais en plus
reprogrammer son comportement (enfoncement, relâchement, ...).
La taille d'un composant prédéfini ne peut être connue ou modifiée tant que sa méthode
addNotify () n'a pas encore été appelée. En effet, la taille d'un composant est établie qu'une fois
que son peer a été créé. Ce n'est qu'à partir de ce moment que vous pouvez interroger sa taille
préférée ou modifier sa taille.

La classe java.awt.Toolkit
vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 19

Cette classe abstract déclare un ensemble de méthodes abstract appelées pour créer le peer des
composants, des menus du package java.awt, gérer les images et obtenir des informations sur l'interface
graphique. Pour obtenir une instance de cette classe, vous pouvez appelez la méthode getToolkit () de
la classe Component ou la méthode static getDefaultToolkit () de cette classe.

Méthodes

protected abstract ButtonPeer createButton (Button target)


protected abstract ChoicePeer createChoice (Choice target)
protected abstract ListPeer createList (List target)
protected abstract CheckboxPeer createCheckbox (Checkbox target)
protected abstract LabelPeer createLabel (Label target)
protected abstract TextAreaPeer createTextArea (TextArea target)
protected abstract TextFieldPeer createTextField (TextField target)
protected abstract ScrollbarPeer createScrollbar (Scrollbar target)
protected abstract CanvasPeer createCanvas (Canvas target)
protected abstract PanelPeer createPanel (Panel target)
protected abstract FramePeer createFrame (Frame target)
protected abstract WindowPeer createWindow (Window target)
protected abstract DialogPeer createDialog (Dialog target)
protected abstract FileDialogPeer createFileDialog (FileDialog target)
protected abstract MenuBarPeer createMenuBar (MenuBar target)
protected abstract MenuItemPeer createMenuItem (MenuItem target)
protected abstract MenuPeer createMenu (Menu target)
protected abstract CheckboxMenuItemPeer createCheckboxMenuItem
(CheckboxMenuItem target)

Ces méthodes sont appelées dans les méthodes addNotify () des classes de composants passés en
paramètre, pour créer le peer de ce composant. Vous n'avez pas à appeler ces méthodes.

public abstract Dimension getScreenSize ()

Renvoie la taille de l'écran.

public abstract int getScreenResolution ()

Renvoie la résolution de l'écran en points par pouce (dpi).

public abstract ColorModel getColorModel ()

Renvoie le modèle de couleur utilisé.

public abstract String [ ] getFontList ()

Renvoie la liste des polices de caractères disponibles.

public abstract FontMetrics getFontMetrics (Font font)

Renvoie la taille de la police de caractères font.

public abstract void sync ()

Synchronise l'affichage graphique ; cette méthode peut être utile pour réaliser des animations.

public abstract Image getImage (String filename)


public abstract Image getImage (URL url)

Ces méthodes permettent d'obtenir l'image du fichier filename ou à l'URL url. L'image n'est
effectivement chargée qu'à sa première utilisation (voir le chargement des images).

public abstract boolean prepareImage (Image image, int width, int height,
ImageObserver observer)
public abstract int checkImage (Image image, int width, int height,
ImageObserver observer)

Ces méthodes permettent de gérer le chargement d'une image.

public abstract Image createImage (ImageProducer producer)


vendredi 11 février 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 20

Permet de créer une image.

public synchronized static Toolkit getDefaultToolkit ()

Renvoie le toolkit par défaut.

Exemples

Applets ListePolices et BoutonsNavigation .


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 1

Les containers
et la disposition des composants

Les containers
La disposition des composants : les layouts
Les menus

Les containers

L'architecture container/composant

Les containers sont des composants qui contiennent d'autres composants. Ce type d'architecture donne
aux containers un ensemble de caractéristiques qui leur sont propres :

Chaque container mémorise la liste des composants qu'on lui ajoute avec les méthodes add () ou
qu'on lui enlève avec les méthodes remove () et removeAll ().
Un container dispose à l'écran les composants qu'il contient, grâce à un layout qui lui est associé.
Il s'occupe d'envoyer les événements qui surviennent (clic de souris, touche du clavier,...) au
composant concerné ou de les traiter lui-même.

Il existe ainsi une relation entre un container et ses composants. Comme un composant peut être
lui-même un container contenant d'autres composants, l'ensemble de ces relations parent-enfant peut se
décrire sous forme d'un arbre, comme dans l'applet suivante, qui reproduit les commandes Couper /
Copier / Coller / Effacer d'un traitement de texte :

Voici le programme Java correspondant (à copier dans un fichier dénommé TraitementTexte.java et


invoqué à partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.util.Date;

public class TraitementTexte extends Applet


{
private TextArea texte = new TextArea ();
private String texteCopie = "";

public void init ()


{
// Choix d'un layout BorderLayout (FlowLayout par défaut)
setLayout (new BorderLayout ());
// Création d'une barre de boutons avec les commandes
// Couper/Copier/Coller/Effacer (layout FlowLayout par défaut)
Panel panel = new Panel ();
panel.add (new Button ("Couper"));
panel.add (new Button ("Copier"));
panel.add (new Button ("Coller"));
panel.add (new Button ("Effacer"));
// Ajout en haut de la barre de boutons
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 2

add ("North", panel);


// Ajout au centre de la zone de saisie
add ("Center", texte);
// Ajout en bas d'un label
add ("South", new Label ( "Du C/C++ a Java "
+ new Date ().toLocaleString ()));
}

// Méthode appelée par la machine virtuelle quand on clique sur un bouton


public boolean action (Event event, Object eventArg)
{
if ("Couper".equals (eventArg))
{
// Simulation d'une action Copier/Effacer
postEvent (new Event (this, Event.ACTION_EVENT, "Copier"));
postEvent (new Event (this, Event.ACTION_EVENT, "Effacer"));
}
else if ("Copier".equals (eventArg))
// Récupération du texte sélectionné
texteCopie = texte.getSelectedText ();
else if ("Coller".equals (eventArg))
// Remplacement de la sélection par texteCopie
texte.replaceText (texteCopie, texte.getSelectionStart (),
texte.getSelectionEnd ());
else if ("Effacer".equals (eventArg))
// Remplacement de la sélection par rien
texte.replaceText ("", texte.getSelectionStart (),
texte.getSelectionEnd ());
return true;
}
}

Les relations entre les composants de l'applet précédente décrivent l'arbre suivant :

figure 15. Arbre des composants de l'applet TraitementTexte

Ne mélangez pas les concepts de graphe d'héritage entre classes et de graphe de parenté entre
composants !... Ces liens de parenté ne sont le résultat que de la composition entre classes : chaque
composant mémorise dans une variable private, une référence sur son parent que l'on peut obtenir
grâce à la méthode getParent () de la classe Component.

Chaque composant et par conséquent chaque container possède son propre système de coordonnées.
L'origine de ce repère est situé en haut à gauche du composant, l'axe des x va vers la droite, et l'axe des
y vers le bas. Ceci forme un repère indirect, ce qui n'est pas très pratique pour afficher des courbes
mathématiques, mais comme presque tous les systèmes informatiques utilisent cette convention...
Chaque composant (container ou pas) est positionné dans le repère du container qui le contient,
c'est-à-dire que dans l'exemple précédent, le bouton Copier est positionné par rapport au repère du
container de classe Panel qui le contient, et ce container est positionné dans le repère de l'applet. La
méthode location () de la classe Component invoquée sur ce bouton vous renverra donc les
coordonnées du bouton dans son parent et non dans l'applet ; de même, si vous utilisez la méthode move
(x, y) de la classe Component pour placer un composant comp, il faut fournir des coordonnées (x,y)
relatives au container qui contient comp.
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 3

Les containers sont généralement utilisés pour y ajouter des composants mais vous pouvez aussi y
afficher des dessins fabriqués avec les méthodes de la classe Graphics en outrepassant par exemple la
méthode paint () de la classe Component. Mais évitez de mélanger ces deux styles, car les composants
sont disposés par un layout à des positions qui ne sont pas toujours les mêmes suivant les systèmes, et
vos dessins pourraient être recouverts par des composants.
Il vaut mieux dans ce cas créer une nouvelle classe de composant CanvasDessin dérivant de Canvas,
où vous effectuez vos dessins, puis ajouter une instance de CanvasDessin au container.

La classe java.awt.Container

Cette classe abstract qui dérive de la classe Component, est la super classe de toutes les classes de
container. Elle définit notamment un certains nombres de méthodes, qui permettent d'ajouter ou
d'enlever des composants aux containers, ou qui disposent les composants dans un container grâce à son
layout associé.

Méthodes

public Component add (Component comp)


public synchronized Component add (Component comp, int pos)
public synchronized Component add (String name, Component comp)

Ces méthodes permettent d'ajouter le composant comp à un container et de créer le lien parent-enfant. Le
container garde une liste ordonnée des composants qui lui sont ajoutés, ce qui permet aux layouts de les
disposer dans cet ordre si nécessaire. Par défaut, pos = -1, ce qui implique que le composant est ajouté à
la fin de la liste des composants.
La troisième méthode, en plus d'ajouter le composant en fin de liste, appelle la méthode
addLayoutComponent (name, comp) de l'interface LayoutManager, si le container a un layout. Certaines
classes de layout comme BorderLayout, implémentent cette méthode pour utiliser les informations
contenues dans name et placer le composant autrement qu'en utilisant uniquement son numéro d'ordre.
Si le peer du container existe déjà, ces méthodes appellent la méthode addNotify () du composant comp.

public synchronized void remove (Component comp)


public synchronized void removeAll ()

Ces méthodes permettent d'enlever le composant comp ou tous les composants d'un container.

Si vous ajoutez ou enlevez un composant d'un container qui est déjà à l'écran, n'oubliez pas d'appeler
la méthode validate () sur le container, sinon votre container restera inchangé à l'écran (voir
l'exemple de la classe BorderLayout).

public int countComponents ()

Renvoie le nombre de composants que contient un container.

public synchronized Component getComponent (int n)


throws ArrayIndexOutOfBoundsException
public synchronized Component [ ] getComponents ()

Ces méthodes renvoient le nième composant ou tous les composants d'un container.

public LayoutManager getLayout ()


public void setLayout (LayoutManager manager)

Ces méthodes permettent d'interroger ou de modifier le layout utilisé par un container. Toutes les
classes dérivées de container ont un layout par défaut.

public synchronized void layout ()

Méthode de la classe Component, outrepassée pour disposer les composants d'un container.

public synchronized void validate ()

Méthode de la classe Component, outrepassée pour mettre à jour tous les composants d'un container
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 4

invalide.

public Insets insets ()

Renvoie la largeur des bordures du container en haut, en bas, à gauche et à droite. Comme cette méthode
interroge le peer du container, les valeurs renvoyées ne sont valides que si son peer existe déjà.

public synchronized Dimension preferredSize ()


public synchronized Dimension minimumSize ()

Ces méthodes renvoient la taille préférée et la taille minimum d'un container. Si le container a un layout,
ces méthodes renvoient les dimensions respectivement données par les méthodes preferredLayoutSize
() et minimumLayoutSize () de l'interface LayoutManager.

public void paintComponents (Graphics g)


public void printComponents (Graphics g)

Ces méthodes redessine ou imprime tous les composants d'un container.

public void deliverEvent (Event e)

Cette méthode qui outrepasse celle de la classe Component est utilisée pour la gestion événementielle
(voir le chapitre suivant).

public Component locate (int x, int y)

Méthode de la classe Component outrepassée pour rechercher le composant situé au point de


coordonnées (x,y) d'un container.

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour appeler la méthode
addNotify () sur chacun des composants du container.

public synchronized void removeNotify ()

Cette méthode outrepasse la méthode removeNotify () de la classe Component pour appeler la méthode
removeNotify () sur chacun des composants du container.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
container.

public void list (PrintStream out, int indent)

Méthode de la classe Component, outrepassée pour imprimer sur out la chaîne de caractères décrivant le
container et tous composants.

La classe java.awt.Panel

Cette classe qui dérive des classes Container et Component, permet de créer un container utilisé comme
zone d'affichage pour y ajouter des composants ou pour y dessiner directement avec les méthodes de
classe Graphics. Le layout utilisé par défaut par la classe Panel est la classe FlowLayout. Cette classe est
la super classe Applet.

Constructeur

public Panel ()

Méthode

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Container pour créer le peer d'un panel
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 5

puis appeler la méthode addNotify () de la classe Container.

Exemples

Applets TraitementTexte , DrawIt , ListePolices et Nuancier .

La classe java.awt.Window

Cette classe qui dérive des classes Container et Component, permet de créer une fenêtre sans bord, ni
titre, ni menu. Contrairement à ses classes dérivées Dialog et Frame, elle est peu utile mais peut servir
par exemple comme bannière au lancement d'un programme, pour afficher une image. Le layout utilisé
par défaut par la classe Window est la classe BorderLayout.

N'oubliez pas d'appeler la méthode show () pour afficher une fenêtre et de la dimensionner à la taille
correcte avec les méthodes pack () ou les méthodes resize () ou reshape () de la classe Component.

Constructeur

public Window (Frame parent)

Ce constructeur construit une fenêtre invisible dont le parent parent doit être de classe Frame ou ses
dérivées.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Container pour créer le peer d'une
fenêtre puis appeler la méthode addNotify () de la classe Container.

public synchronized void pack ()

Cette méthode permet de dimensionner la fenêtre à la taille donnée par la méthode preferredSize () de
la classe Container. Si les peers de la fenêtre et des composants qu'elle contient n'existent pas encore,
ceux-ci sont créés pour que la taille renvoyée soit correcte.

public void show ()

Méthode de la classe Component, outrepassée pour afficher la fenêtre et les composants qu'elle contient.
Si la fenêtre est déjà visible, la fenêtre est ramenée devant toutes les autres.
Cette méthode appelle la méthode addNotify (), ce qui a pour conséquence de créer à l'écran la fenêtre
et tous les composants qui lui ont été ajoutés.
Vous devez appeler au moins une fois cette méthode par fenêtre car elles sont créées invisibles.
Le parent de la fenêtre n'a pas forcément besoin d'être visible pour que cette méthode fonctionne.

public synchronized void dispose ()

Détruit une fenêtre. Vous devez appeler cette méthode pour que la fenêtre à l'écran soit effectivement
détruite.

public void toFront ()


public void toBack ()

Ces méthodes ramène une fenêtre devant ou derrière toutes les autres.

public Toolkit getToolkit ()

Méthode de la classe Component, outrepassée pour renvoyer le kit d'outils (Toolkit ) par défaut utilisé
pour la création du peer d'un composant.

public final String getWarningString ()

Renvoie le warning d'une fenêtre. Ce type de message est affiché dans les fenêtres créées dans un
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 6

navigateur.

La classe java.awt.Frame

Cette classe qui dérive des classes Window, Container et Component et implémente l'interface
MenuContainer permet de créer une fenêtre indépendante avec un cadre, un titre, et éventuellement un
menu et un pointeur de souris propre.

Variables

public final static int DEFAULT_CURSOR


public final static int CROSSHAIR_CURSOR
public final static int TEXT_CURSOR
public final static int WAIT_CURSOR
public final static int SW_RESIZE_CURSOR
public final static int SE_RESIZE_CURSOR
public final static int NW_RESIZE_CURSOR
public final static int NE_RESIZE_CURSOR
public final static int N_RESIZE_CURSOR
public final static int S_RESIZE_CURSOR
public final static int W_RESIZE_CURSOR
public final static int E_RESIZE_CURSOR
public final static int HAND_CURSOR
public final static int MOVE_CURSOR

Ces constantes représentent les pointeurs de souris prédéfinis que vous pouvez choisir dans une fenêtre
avec la méthode setCursor ().

Constructeurs

public Frame ()
public Frame (String title)

Ces constructeurs créent une fenêtre avec ou sans titre title.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Window pour créer le peer d'une fenêtre
et son éventuel menu, puis appeler la méthode addNotify () de la classe Window.

public String getTitle ()


public void setTitle (String title)

Ces méthodes permettent d'interroger ou de modifier le titre d'une fenêtre.

public Image getIconImage ()


public void setIconImage (Image image)

Ces méthodes permettent d'interroger ou de modifier l'image utilisée quand une fenêtre est icônifiée,
mais tous les systèmes n'utilisent pas cette caractéristique.

public MenuBar getMenuBar ()


public synchronized void setMenuBar (MenuBar menubar)

Ces méthodes permettent d'interroger ou de modifier la barre de menu utilisée pour une fenêtre.

public synchronized void remove (MenuComponent menu)

Enlève le menu menu de la fenêtre.

public synchronized void dispose ()

Méthode de la classe Window, outrepassée pour détruire une fenêtre. Vous devez appeler cette méthode
pour que la fenêtre à l'écran soit effectivement détruite.

public boolean isResizable ()


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 7

Renvoie true si la fenêtre est redimensionnable interactivement.

public void setResizable (boolean resizable)

Permet de spécifier si une fenêtre est redimensionnable interactivement ou non.

public int getCursorType ()


public void setCursor (int cursorType)

Ces méthodes permettent d'interroger ou de modifier le curseur de souris utilisé dans une fenêtre.
cursorType doit être une des constantes dont la liste est donnée avec les variables.

protected String paramString ()

Méthode de la classe Container, outrepassée pour renvoyer une chaîne de caractères décrivant la
fenêtre.

Exemples

Applets BorderBuilder et ShowMenu .


Application TraitementTexte .

Transformer une applet en application isolée

La classe d'une applet héritant de la classe Panel, il est possible d'ajouter une applet à une fenêtre de
classe Frame. Grâce à cette caractéristique, vous pouvez ajouter à la plupart de vos applets la faculté
d'être utilisées comme applications isolées, en leur ajoutant une méthode main () qui crée une instance
de Frame pour y ajouter une instance de votre applet (les limitations de ce système sont décrites au
chapitre sur les applets).
Voici par exemple comment transformer l'applet de classe TraitementTexte définie au début de ce
chapitre pour en faire une application isolée :

public class TraitementTexte extends Applet


{
// Code précédemment défini...

public static void main (String [ ] args)


{
// Création d'une fenêtre et d'une instance de l'applet
Frame fenetreApplet = new Frame ("TraitementTexte");
Applet applet = new TraitementTexte ();

// Ajout de l'applet à la fenêtre puis affichage de la fenêtre


fenetreApplet.add ("Center", applet);
fenetreApplet.show ();
fenetreApplet.resize (300, 200);

// Démarrage de l'applet
applet.init ();
fenetreApplet.validate ();
}
}

La classe java.awt.Dialog

Cette classe qui dérive des classes Window, Container et Component permet de créer une boite de
dialogue avec un cadre et un titre. Les boites de dialogues ont un parent de classe Frame, et peuvent être
modale ou non. Une boite de dialogue qui est modale bloque toute entrée dans les autres fenêtres tant
qu'elle est ouverte.

Quand une boite de dialogue est modale, l'utilisateur ne peut plus manipuler que cette boite de
dialogue et plus les autres fenêtres créées par la Machine Virtuelle Java. A l'appel de la méthode show
() sur une boite de dialogue modale dialog1, la boite de dialogue est affichée et les instructions qui
suivent show () ne sont exécutées qu'une fois que dialog1 est détruite. N'oubliez donc pas de détruire
vos boites de dialogues une fois son traitement terminé avec la méthode dispose ().
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 8

Constructeurs

public Dialog (Frame parent, boolean modal)


public Dialog (Frame parent, String title, boolean modal)

Ce constructeur construit une boite de dialogue invisible avec ou sans titre title, dont le parent parent
doit être de classe Frame ou ses dérivées. Cette boite de dialogue peut être modale ou non.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Window pour créer le peer d'une boite de
dialogue, puis appeler la méthode addNotify () de la classe Window.

public boolean isModal ()

Renvoie true si la boite de dialogue est modale.

public String getTitle ()


public void setTitle (String title)

Ces méthodes permettent d'interroger ou de modifier le titre d'une boite de dialogue.

public boolean isResizable ()

Renvoie true si la boite de dialogue est redimensionnable interactivement.

public void setResizable (boolean resizable)

Permet de spécifier si une boite de dialogue est redimensionnable interactivement ou non.

protected String paramString ()

Méthode de la classe Container, outrepassée pour renvoyer une chaîne de caractères décrivant la boite
de dialogue.

La bibliothèque Java ne fournissant pas en standard une boite de dialogue affichant simplement un
message, voilà l'applet MessageBoxApplet qui peut vous servir de base pour en réaliser une :

en cliquant sur le bouton, on obtient le résultat suivant :

Voici le programme Java correspondant (à copier dans un fichier dénommé MessageBoxApplet.java et


invoqué à partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class MessageBoxApplet extends Applet


{
public void init ()
{
add (new Button ("Afficher message"));
}

// Méthode appelée par la machine virtuelle quand on clique sur le bouton


public boolean action (Event event, Object eventArg)
{
MessageBox.showMessageBox(this, "Message", "Ceci est un message...");
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 9

return true;
}
}

class MessageBox extends Dialog


{
private MessageBox (Frame frame, String titre)
{
// Appel du constructeur de la classe Dialog en mode modal
super (frame, titre, true);
}

static public void showMessageBox (Component parent,


String titre,
String message)
{
// Recherche du parent qui est un Frame
Component frame = parent;
while ( frame != null
&& !(frame instanceof Frame))
frame = frame.getParent ();

// Création d'une instance de MessageBox


MessageBox boite = new MessageBox ((Frame)frame, titre);

// Ajout du message et d'un bouton Ok


boite.add ("Center", new Label (message));
boite.add ("East", new Button ("Ok"));

// Affichage de la boite de message


// show () ne rend la main que quand la boite est détruite
boite.setResizable (false);
boite.pack ();
boite.show ();
}

// Méthode appelée quand on clique sur Ok


public boolean action (Event event, Object eventArg)
{
// Destruction de la boite de message
dispose ();
return true;
}
}

La classe java.awt.FileDialog

Cette classe qui dérive des classes Dialog, Window, Container et Component permet de créer une boite de
dialogue de saisie de fichier. Comme pour tous les composants du package java.awt, cette boite de
dialogue est celle communément utilisée avec le système sur lequel fonctionne l'applet. Comme cette
classe donne accès au système de fichiers, certains navigateurs interdisent tout simplement de s'en servir.

Variables

public final static int LOAD


public final static int SAVE

Ces constantes sont utilisées pour choisir le mode de saisie du fichier de la boite de dialogue (Ouvrir ou
Enregistrer ).

Constructeurs

public FileDialog (Frame parent, String title)


public FileDialog (Frame parent, String title, int mode)

Ce constructeur construit une boite de dialogue invisible de saisie de fichier avec ou sans titre title,
dont le parent parent doit être de classe Frame ou ses dérivées. Par défaut, le mode saisie est LOAD.

Méthodes

public synchronized void addNotify ()


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 10

Cette méthode outrepasse la méthode addNotify () de la classe Dialog pour créer le peer d'une boite de
dialogue de saisie de fichier, puis appeler la méthode addNotify () de la classe Dialog.

public int getMode ()

Ces méthodes permettent d'interroger le mode saisie du fichier (LOAD ou SAVE).

public String getDirectory ()


public void setDirectory (String dir)

Ces méthodes permettent d'interroger ou de modifier le répertoire courant de la boite de dialogue.

public String getFile ()


public void setFile (String file)

Ces méthodes permettent d'interroger ou de modifier le fichier saisie dans la boite de dialogue.

public FilenameFilter getFilenameFilter ()


public void setFilenameFilter (FilenameFilter filter)

Ces méthodes permettent d'interroger ou de modifier le filtre utilisé pour la saisie du fichier, par exemple
pour n'accepter que les fichiers se terminant par .java (ce qui correspondrait à *.java ).

protected String paramString ()

Méthode de la classe Container, outrepassée pour renvoyer une chaîne de caractères décrivant la boite
de dialogue de saisie de fichier.

La disposition des composants : les layouts

Associé à chaque container, existe un gestionnaire qui permet de disposer les éventuels composants que
contient le container : ce sont les layouts . Il est conseillé d'utiliser ce type de gestionnaire pour indiquer
la position des composants plutôt que les coordonnées pixel qui ne garantissent pas le même rendu sur
chacune des plateformes où peuvent fonctionner une applet (par exemple, un bouton n'a pas le même
aspect ni la même taille sous tous les systèmes).
Globalement, il existe trois solutions pour donner une position à un composant :

En utilisant une des cinq classes de layout fournies avec Java : FlowLayout, BorderLayout,
GridLayout, GridBagLayout ou CardLayout.
En créant et en utilisant une classe de layout qui implémente l'interface LayoutManager. Cette
interface déclare un ensemble de méthodes qui permettent de gérer et de placer les composants
d'un container (Les cinq classes précédentes implémentent cette interface).
En n'utilisant aucun layout (grâce à setLayout (null)) et en plaçant les composants "à la main"
dans le container, au pixel près grâce aux méthodes move (), resize () ou reshape () de la classe
Component. Dans ce cas, n'oubliez pas de prendre en compte la largeur des bordures du container
renvoyées par la méthode insets () de la classe Container.

Le choix d'un layout se fait grâce à la méthode setLayout () de la classe Container.


Les composants d'un container container1 sont positionnés à l'appel de la méthode layout () de la
classe Container sur container1. Cette méthode appelle le gestionnaire qui dispose les composants dans
le container. Elle est appelée par la méthode validate () de la classe Component si le container ou un de
ses composants ne sont pas valides (à leur première visualisation, quand ils changent de taille,...).
Quand le container est déjà visualisé, l'ajout (ou la suppression) d'un composant à ce container n'appelle
pas validate () automatiquement : il vous faut donc appeler cette méthode dans ce cas de figure pour
mettre à jour la disposition des composants dans le container (voir l'exemple de la classe BorderLayout).

Les classes de Java dérivant de la classe container utilisent un layout par défaut : Les classes Panel et
Applet utilisent la classe FlowLayout, et les classes Window, Dialog et Frame la classe BorderLayout.
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 11

Si dans certains cas, les classes de layout fournies avec Java ne semblent pas vous convenir, n'oubliez
pas avant d'envisager de créer un layout de toute pièce que vous pouvez très bien combiner les layouts
existants. En effet, chaque container étant lui-même un composant vous pouvez agencer une partie de
vos composants en les incluant dans un sous-container (de classe Panel par exemple) qui disposera ces
composants de manière différente que le layout du container principal. Donc, étudiez bien l'effet de
chacun des layouts suivants et soyez imaginatifs ! Toutes les combinaisons sont possibles...
L'exemple du paragraphe sur les containers combine deux layouts, l'un de classe BorderLayout et
l'autre de classe FlowLayout.
Si vous voulez n'utiliser aucun layout pour un container, il est très fortement conseillé d'outrepasser la
méthode layout () de la classe Container pour y disposer vos composants. En effet, pour pouvoir
placer sans problème vos composants leur peer doit exister. Le peer d'un container et de ses
composants est créé à l'appel de la méthode addNotify () de la classe Container. Cette méthode n'est
pas appelée au même moment pour une applet et une fenêtre indépendante, mais elle est toujours
suivie d'un appel à validate () et donc d'un appel à la méthode layout () :

Pour une applet, addNotify () est appelée juste avant que le navigateur n'appelle les méthodes
validate () et init () de la classe Applet (voir l'exemple décrivant l'ensemble des méthodes
appelées à la création d'une applet).
Pour une fenêtre indépendante (de classe Frame ou Dialog par exemple), addNotify () est
invoquée par la méthode show () de la classe Window, qui appelle ensuite la méthode validate
().

L'interface java.awt.LayoutManager

L'interface LayoutManager déclare cinq méthodes que doit définir toute classe qui l'implémente (même si
ces méthodes ne font rien).

Méthodes

public void addLayoutComponent (String name, Component component)

Méthode appelée quand le composant component est ajouté à un container par la méthode add (name,
component) de la classe Container. Utiliser cette méthode si vous voulez mémoriser les informations
supplémentaires décrites dans name et associées au composant component pour le placer. Cette méthode
est utilisée par exemple par la classe BorderLayout. Rappelez-vous qu'il n'est pas obligatoire de
mémoriser la liste de tous les composants d'un container, car la classe Container fournit la méthode
getComponents () qui renvoient la liste de tous les composants ajoutés à un container.

public void removeLayoutComponent (Component component)

Méthode appelée quand le composant component est enlevé d'un container par la méthode remove
(component) de la classe Container.

public Dimension preferredLayoutSize (Container parent)

Doit renvoyer les dimensions préférées du container parent auquel ont été ajoutés des composants.

public Dimension minimumLayoutSize (Container parent)

Doit renvoyer les dimensions minimum du container parent auquel ont été ajoutés des composants.

public void layoutContainer (Container parent)

Doit positionner les composants du container parent. Cette méthode est appelée par la méthode layout
() de la classe Container si le container parent utilise un layout. C'est donc cette méthode qui place les
composants dans le container parent, en utilisant les méthodes move (), resize () ou reshape () de la
classe Component et en prenant en compte la largeur des bordures du container renvoyées par la méthode
insets () de la classe Container.

La classe java.awt.FlowLayout
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 12

Cette classe qui implémente l'interface LayoutManager, est le type de layout le plus simple. Les
composants d'un container container1 géré par un FlowLayout sont positionnés les uns derrière les
autres, en partant du coin en haut à gauche de container1, puis en occupant horizontalement au
maximum la largeur de container1, avant de passer à une ligne suivante.
Chaque composant prend les dimensions que renvoie sa méthode preferredSize ().

Variables

public final static int LEFT


public final static int CENTER
public final static int RIGHT

Constantes utilisées pour l'alignement des composants dans le container (sur le bord gauche, au centre,
ou sur le bord droit). Par défaut, l'alignement est CENTER.

Constructeurs

public FlowLayout ()
public FlowLayout (int align)
public FlowLayout (int align, int horizontalgap, int verticalgap)

Ces constructeurs permettent de créer un layout FlowLayout avec un alignement align (LEFT, CENTER ou
RIGHT) et un espacement horizontal et vertical entre les composants de horizontalgap et verticalgap
pixels (par défaut égal à 5).

Méthodes

public void addLayoutComponent (String name, Component comp)


public void removeLayoutComponent (Component comp)
public Dimension preferredLayoutSize (Container target)
public Dimension minimumLayoutSize (Container target)
public void layoutContainer (Container target)

Implémentation des méthodes de l'interface LayoutManager. addLayoutComponent () et


removeLayoutComponent () ne font rien.

public String toString ()

Exemples

Les applets BorderBuilder , MiseAJourHorloge et BoutonsNavigation affichent leurs composants avec


leur layout par défaut de classe FlowLayout.
L'applet TraitementTexte utilise une barre de boutons qui sont ajoutés à un sous-container de classe
Panel utilisant son layout par défaut de classe FlowLayout.

La classe java.awt.BorderLayout

Cette classe qui implémente l'interface LayoutManager, est un type de layout qui permet de gérer la
position d'au plus cinq composants : La position d'un composant component est déterminée grâce la
chaîne de caractères name passée en argument à la méthode add (name, component) de la classe
Container. Cette chaîne de caractères peut être égal à "North", "South", "East", "West" ou "Center".
Les cinq composants (ou moins) sont redimensionnés pour occuper l'espace du container en respectant
dans l'ordre, les règles suivantes :

Les composants "North" et "South" gardent la hauteur renvoyée par leur méthode preferredSize
() et sont agrandis en horizontal pour occuper toute la largeur du container.
Les composants "West" et "East" gardent la largeur renvoyée par leur méthode preferredSize ()
et sont agrandis en vertical pour occuper toute la hauteur restante du container.
Le composant "Center" occupe le reste de l'espace du container.

Cette classe de layout peut servir dans de nombreux types d'interfaces utilisateurs : Le composant
"Center" peut représenter par exemple une zone de dessin ou d'édition de texte, les composants "North"
et "West" être utilisés pour afficher des barres d'outils et les composants "East" et "South" pour des
ascenseurs (composant Scrollbar).
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 13

Constructeurs

public BorderLayout ()
public BorderLayout (int horizontalgap, int verticalgap)

Ces constructeurs permettent de créer un layout BorderLayout avec un espacement horizontal et vertical
entre les composants de horizontalgap et verticalgap pixels (par défaut égal à 0).

Méthodes

public void addLayoutComponent (String name, Component comp)


public void removeLayoutComponent (Component comp)
public Dimension preferredLayoutSize (Container target)
public Dimension minimumLayoutSize (Container target)
public void layoutContainer (Container target)

Implémentation des méthodes de l'interface LayoutManager. L'implémentation de la méthode


addLayoutComponent () mémorise pour chacune des cinq directions le composant qui lui est associé.

public String toString ()

Pour illustrer l'utilisation de la classe BorderLayout, voici un exemple d'applet permettant d'ajouter à une
fenêtre indépendante des boutons aux cinq positions possibles (La fenêtre s'affiche quand vous cliquez
sur un des boutons et est indépendante pour que vous puissiez la redimensionner et observer les effets
de la classe BorderLayout) :

donne le résultat suivant :

et le programme Java correspondant (à copier dans un fichier dénommé BorderBuilder.java et invoqué à


partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class BorderBuilder extends Applet


{
// Création des boutons de l'applet et du Frame
Button [] appletButtons = {new Button ("Add North"),
new Button ("Add South"),
new Button ("Add West"),
new Button ("Add East"),
new Button ("Add Center")};
Button [] frameButtons = {new Button ("North"),
new Button ("South"),
new Button ("West"),
new Button ("East"),
new Button ("Center")};
Frame buttonsFrame;

public void init ()


{
// Ajout des boutons à l'applet (par défaut utilise FlowLayout)
for (int i = 0; i < appletButtons.length; i++)
add (appletButtons [i]);

// Création d'une fenêtre (par défaut utilise BorderLayout)


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 14

buttonsFrame = new Frame ("BorderLayout window");


buttonsFrame.resize (200, 200);
}

public void stop ()


{
// A la fin de l'applet, destruction de la fenêtre
buttonsFrame.dispose ();
}

// Méthode appelée par la machine virtuelle quand on clique sur un bouton


public boolean action (Event event, Object eventArg)
{
// Affichage de la fenêtre
buttonsFrame.show ();

for (int i = 0; i < appletButtons.length; i++)


if (event.target == appletButtons [i])
{
// Si le label du bouton commence par Add,
if (appletButtons [i].getLabel ().startsWith ("Add"))
{
// Ajouter le bouton correspondant à buttonsFrame
buttonsFrame.add (frameButtons [i].getLabel (), frameButtons [i]);
// Changer le nom du bouton de l'applet
appletButtons [i].setLabel ("Remove " + frameButtons [i].getLabel ());
}
else
{
// Sinon enlever le bouton correspondant de buttonsFrame
buttonsFrame.remove (frameButtons [i]);
// Changer le nom du bouton de l'applet
appletButtons [i].setLabel ("Add " + frameButtons [i].getLabel ());
}

// Mettre à jour les composants de l'applet et de la fenêtre


appletButtons [i].invalidate ();
validate ();
buttonsFrame.validate ();
return true;
}

return super.action (event, eventArg);


}
}

La classe BorderLayout vous oblige à vous servir de la méthode add (String name, Component
component) de la classe Container pour ajouter les composants à un container.

Autres exemples

Applets TraitementTexte , MessageBoxApplet , DrawIt , ListePolices et Nuancier .

La classe java.awt.GridLayout

Cette classe qui implémente l'interface LayoutManager, est un layout permettant d'afficher les
composants que contient un container sur une grille régulière. Le nombre de lignes et de colonnes étant
passés en argument au constructeur d'un layout GridLayout, la hauteur et la largeur d'un container
container1 géré par un GridLayout sont divisées par le nombre de lignes et de colonnes pour connaître
la dimension de chaque cellule. Les composants de container1 sont ensuite positionnés dans les
cellules, en partant du coin en haut à gauche de container1, puis colonne après colonne et ligne par
ligne.
Chaque composant prend les dimensions de la cellule qu'il occupe.

Constructeurs

public GridLayout (int rows, int cols)


public GridLayout (int rows, int cols, int horizontalgap, int verticalgap)

Ces constructeurs permettent de créer un layout GridLayout avec une grille de rows lignes par cols
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 15

colonnes, et un espacement horizontal et vertical entre les composants de horizontalgap et verticalgap


pixels (par défaut égal à 0). Il n'est pas obligatoire de remplir entièrement la grille d'un layout
GridLayout, mais attention les lignes inoccupées restent vides ; donc évitez de créer un layout
GridLayout avec de grandes dimensions en prévision de son remplissage, sinon les cellules seront toutes
petites !

Méthodes

public void addLayoutComponent (String name, Component comp)


public void removeLayoutComponent (Component comp)
public Dimension preferredLayoutSize (Container target)
public Dimension minimumLayoutSize (Container target)
public void layoutContainer (Container target)

Implémentation des méthodes de l'interface LayoutManager. addLayoutComponent () et


removeLayoutComponent () ne font rien.

public String toString ()

Exemples

Applets Unicode , DrawIt , ListePolices .


L'applet GridBagBuilder de la classe GridBagLayout contient aussi un sous-container de classe Panel qui
affiche les composants des 17 premières lignes et des 2 colonnes avec un layout de classe GridLayout.

La classe java.awt.GridBagLayout

Cette classe qui implémente l'interface LayoutManager, est le layout le plus complet et le plus complexe
à utiliser des layouts fournis avec Java.
A chaque composant ajouté à un container géré par un layout GridBagLayout, sont associées des
contraintes (constraints en anglais) décrivant comment placer ce composant dans le container. Ces
contraintes sont décrites par une instance de la classe GridBagConstraint et positionnées sur le
composant grâce à la méthode setConstraints () de la classe GridBagLayout.

Vu le nombre de paramètres utilisés par les contraintes, le placement des composants gérés par un
layout GridBagLayout est complexe et c'est pourquoi cette classe définit de nombreuses variables et
méthodes protected, vous permettant de modifier le comportement de cette classe dans une classe
dérivée.

Variables

public int columnWidths [ ]


public int rowHeights [ ]
public double columnWeights [ ]
public double rowWeights [ ]

protected final static int MAXGRIDSIZE


protected final static int MINSIZE
protected final static int PREFERREDSIZE

protected Hashtable comptable


protected GridBagConstraints defaultConstraints
protected GridBagLayoutInfo layoutInfo

Constructeur

public GridBagLayout ()

Méthodes

public void addLayoutComponent (String name, Component comp)


public void removeLayoutComponent (Component comp)
public Dimension preferredLayoutSize (Container target)
public Dimension minimumLayoutSize (Container target)
public void layoutContainer (Container target)

Implémentation des méthodes de l'interface LayoutManager. addLayoutComponent () et


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 16

removeLayoutComponent () ne font rien.

public void setConstraints (Component comp, GridBagConstraints constraints)

Positionne les contraintes sur un composant comp géré par un layout GridBagLayout.

public GridBagConstraints getConstraints (Component component)

Renvoie une copie des contraintes positionnées sur un composant géré par un layout GridBagLayout.

public Point getLayoutOrigin ()


public int [ ][ ] getLayoutDimensions ()
public double [ ][ ] getLayoutWeights ()
public Point location (int x, int y)

public String toString ()

protected GridBagLayoutInfo GetLayoutInfo (Container parent,


int sizeflag)
protected void AdjustForGravity (GridBagConstraints constraints,
Rectangle r)
protected Dimension GetMinSize (Container parent,
GridBagLayoutInfo info)
protected void ArrangeGrid (Container parent)

Pour illustrer l'utilisation de la classe GridBagLayout, voici l'applet GridBagBuilder permettant d'ajouter à
une fenêtre indépendante le composant de votre choix et de positionner sur chacun des composants les
contraintes proposées par la classe GridBagConstraints. La fenêtre s'affiche à la première utilisation d'un
des composants de l'applet.

Mode d'emploi

Une fois la classe de votre composant choisie avec le champ Type de composant, vous pouvez saisir
dans le champ Label, text ou item le label (pour les classes Button, Checkbox et Label), le texte (pour
les classes TextArea et TextField) ou le premier item (pour les classe Choice et List) des nouveaux
composants.Vous pouvez aussi positionner une ou plusieurs contraintes (gridx à weighty) sur un
composant avant de l'ajouter à la fenêtre Composants avec le bouton Ajouter.
Le champ Variable permet de saisir un nom de variable pour chacun des composants que vous ajoutez ;
ce nom est celui utilisé pour intituler chacune des variables du programme généré dans le champ
Programme à copier : Vous pouvez sélectionner le code Java écrit dans ce champ, le copier puis le
coller dans la déclaration d'une classe dérivant de la classe Container (par exemple, une applet), pour
créer les mêmes composants que ceux de la fenêtre Composants .
Le champ Composant courant permet de choisir un des composants créés pour le supprimer avec le
bouton Supprimer ou le modifier avec le bouton Modifier après avoir positionner une ou plusieurs
contraintes (gridx à weighty) sur ce composant.
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 17

N'hésitez pas à utiliser cet exemple pour bien comprendre (et utiliser dans vos programmes) le
fonctionnement des classes GridBagLayout et GridBagConstraints.

La classe java.awt.GridBagConstraints

Cette classe qui implémente l'interface Cloneable, est utilisée pour décrire les contraintes sur le
positionnement d'un composant inclus dans à un container géré par un layout GridBagLayout Les
différentes contraintes étant plutôt complexes à gérer, il vous est conseillé d'essayer l'applet
GridBagBuilder pour essayer les différentes combinaisons possibles et comprendre leur effet. Cette
applet génère le code correspondant aux différentes contraintes choisies dans le champ Programme à
copier.

Variables

public final static int RELATIVE


public final static int REMAINDER

public final static int NONE


public final static int BOTH
public final static int HORIZONTAL
public final static int VERTICAL

public final static int CENTER


public final static int NORTH
public final static int NORTHEAST
public final static int EAST
public final static int SOUTHEAST
public final static int SOUTH
public final static int SOUTHWEST
public final static int WEST
public final static int NORTHWEST

public int gridx


public int gridy

Permet de donner la position en x et y de la cellule que doit occuper un composant, relativement au coin
supérieur gauche du container (dont la position est (0, 0)). Si gridx (ou respectivement gridy) est égal à
RELATIVE, le composant sera placé à droite (ou respectivement en dessous) du composant précédemment
ajouté au container par la méthode add () de la classe Container.
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 18

public int gridwidth


public int gridheight

Permet de donner le nombre de cellules en largeur et en hauteur qu'un composant occupe. Si gridwith
(ou respectivement gridheight) est égal à REMAINDER, le composant sera le dernier sur une ligne (ou
respectivement sur une colonne). Si gridwith (ou respectivement gridheight) est égal à RELATIVE, le
composant sera placé à gauche (ou respectivement au dessus) du composant suivant.

public int fill

Permet de spécifier si un composant est redimensionné ou non pour occuper la(ou les) cellules qu'il
occupe. Si fill est égal à NONE, le composant prend la taille qu'il donne par la méthode preferredSize
(). Si fill est égal BOTH, le composant est redimensionné pour occuper tout l'espace de la (ou des)
cellules. Si fill est égal HORIZONTAL (ou respectivement VERTICAL) le composant prend la largeur (ou
respectivement la hauteur) de la (ou les) cellules qu'il occupe.

public int ipadx


public int ipady

Permet de donner la largeur et la hauteur supplémentaire que doit prendre le composant par rapport à la
taille qu'il donne par la méthode preferredSize ().

public Insets insets

Permet de donner l'espacement vide autour du composant.

public int anchor

Quand le composant est plus petit que la (ou les) cellules qu'il occupe, anchor permet de donner à quel
coin ou à quel bord le composant est ancré (ou attaché). anchor peut être égal à CENTER ou aux 8 points
cardinaux : NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST ou NORTHWEST.

public double weightx


public double weighty

Permet de donner un poids au composant par rapport aux autres pour répartir les composants
horizontalement ou verticalement. Si tous les composants ont leurs contraintes weightx et weighty
égales à 0, l'ensemble des composants d'un container géré par un layout GridBagLayout occupe le
minimum d'espace au milieu du container.

Constructeur

public GridBagConstraints ()

Le constructeur affecte aux variables de contraintes les valeurs par défaut suivantes :

gridx, gridy : RELATIVE


gridwidth, gridheight : 1
fill : NONE
ipadx, ipady : 0
insets : new Insets (0, 0, 0, 0)
anchor : CENTER
weightx, weighty : 0

Méthode

public Object clone ()

A l'usage, vous vous rendrez compte que certaines contraintes ne se complètent pas toujours de
manière très heureuse... Evitez notamment d'utiliser, le mélange du positionnement absolu de gridx et
gridy, avec l'utilisation de REMAINDER pour gridwidth et gridheight.
Si l'utilisation des classes GridBagLayout et GridBagConstraints vous semble trop compliqué, n'hésitez
pas à utiliser plutôt une combinaison de layouts.
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 19

La classe java.awt.CardLayout

Cette classe qui implémente l'interface LayoutManager, est un layout permettant d'ajouter à un container
une liste de sous-containers ou de composants qui sont affichés un par un, de manière similaire aux
boites de dialogues à onglets (mais sans les onglets).

Constructeurs

public CardLayout ()
public CardLayout (int horizontalgap, int verticalgap)

Ces constructeurs permettent de créer un layout CardLayout avec un espacement horizontal et vertical
entre les composants de horizontalgap et verticalgap pixels (par défaut égal à 0).

Méthodes

public void addLayoutComponent (String name, Component comp)


public void removeLayoutComponent (Component comp)
public Dimension preferredLayoutSize (Container target)
public Dimension minimumLayoutSize (Container target)
public void layoutContainer (Container target)

Implémentation des méthodes de l'interface LayoutManager. La méthode addLayoutComponent () utilise


le paramètre name pour associer une chaîne de caractères au composant ou au container comp.

public void first (Container parent)


public void next (Container parent)
public void previous (Container parent)
public void last (Container parent)

Ces méthodes permettent d'afficher le premier, le suivant, le précédent ou le dernier de la liste des
composants ou des sous-containers du layout. parent doit être égal au container auquel est associé ce
layout.

public void show (Container parent, String name)

Affiche le composant ou le sous-container associé à la chaîne de caractères name. parent doit être égal
au container auquel est associé ce layout. Si vous voulez utiliser cette méthode, il faut que le composant
ait été ajouté au container avec la méthode add (String name, Component component) de la classe
Container.

public String toString ()

Les menus

La création de menus se fait très simplement en Java, à l'aide de 4 classes différentes qui héritent toutes
de la classe MenuComponent. Une barre de menu se compose d'une instance de la classe MenuBar auquel
vous pouvez ajouter grâce à la méthode add () un ou plusieurs menus instances de la classe Menu.
Finalement, vous pouvez ajouter à chaque menu grâce à la méthode add () de la classe Menu un
ensemble d'éléments instances des classes MenuItem (pour créer des éléments simples),
CheckboxMenuItem (pour créer des éléments à cocher) ou Menu (pour créer un sous-menu).

figure 16. Les différents composants d'un menu

La classe Frame est la seule classe de fenêtre pour laquelle vous pouvez spécifier une barre de menu,
grâce à la méthode setMenuBar (). Par conséquent, vous ne pouvez pas donner de menu aux applets,
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 20

puisque leur classe n'hérite pas de la classe Frame.

L'applet suivante permet de tester tous les différents types de menu décrit ci-dessus (cliquez sur le
bouton Nouveau Menu pour afficher la fenêtre utilisant un menu) :

Voici le programme Java correspondant (à copier dans un fichier dénommé ShowMenu.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class ShowMenu extends Applet


{
// Création d'une instance de MenuFrame
Frame fenetreMenu = new MenuFrame ();

public void init ()


{
// Ajout d'un bouton pour ouvrir une fenêtre
add (new Button ("Nouveau Menu"));
}

public void stop ()


{
// A la fin de l'applet, destruction de la fenêtre
fenetreMenu.dispose ();
}

// Méthode appelée par la machine virtuelle quand on clique sur un bouton


public boolean action (Event event, Object eventArg)
{
// Affichage de la fenêtre
if ("Nouveau Menu".equals (eventArg))
fenetreMenu.show ();
return true;
}
}

// Classe MenuFrame utilisant un menu


class MenuFrame extends Frame
{
public MenuFrame ()
{
// Création d'une barre de menu et de 3 menus
MenuBar menuBar = new MenuBar ();
Menu menu1 = new Menu ("Menu 1");
Menu menu2 = new Menu ("Menu 2");
Menu menu3 = new Menu ("Sous menu");

// Ajout au premier menu de 2 éléments et d'un séparateur


menu1.add ("Choix 1");
menu1.addSeparator ();
menu1.add ("Choix 2");

// Ajout au second menu d'un élément à cocher


// et d'un sous-menu
menu2.add (new CheckboxMenuItem ("Choix"));
menu2.add (menu3);

// Ajout au sous-menu de 2 éléments


menu3.add (new MenuItem ("Sous choix 1"));
menu3.add (new MenuItem ("Sous choix 2"));

// Ajout à la barre de menu des 2 premiers menus


menuBar.add (menu1);
menuBar.add (menu2);

// Choix de la barre de menu pour cette fenêtre


setMenuBar (menuBar);

resize (200, 50);


}
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 21

L'interface java.awt.MenuContainer

Cette interface doit être implémentée par les classes de containers permettant l'utilisation des menus,
comme c'est le cas pour la classe Frame.

Méthodes

public Font getFont ()


public boolean postEvent (Event evt)
public void remove (MenuComponent comp)

La classe java.awt.MenuComponent

La classe MenuComponent qui est abstract est la super classe de toutes les classes de composants
utilisables dans un menu (MenuBar, MenuItem, Menu et CheckboxMenuItem). Elle ressemble dans le principe
à la classe Component en plus simple.

Constructeur

public MenuComponent ()

Méthodes

public MenuContainer getParent ()


public MenuComponentPeer getPeer ()

Ces méthodes permettent d'obtenir le container auquel est rattaché un composant de menu et le peer
d'un composant de menu.

public Font getFont ()


public void setFont (Font font)

Ces méthodes permettent d'interroger ou de modifier la police de caractère utilisée pour l'affichage d'un
composant de menu.

public void removeNotify ()

Cette méthode est appelée pour supprimer le peer d'un composant de menu.

protected String paramString ()


public String toString ()

Ces méthodes sont utilisées pour fabriquer une chaîne de caractères décrivant le composant de menu
(toString () outrepasse la méthode de la classe Object et appelle la méthode paramString () qui est
outrepassée par les classes dérivées de MenuComponent).

public boolean postEvent (Event evt)

La classe java.awt.MenuBar

Cette classe qui dérive de la classe MenuComponent, est la classe du package java.awt qui permet de créer
et de manipuler une barre de menu. Une fois que vous avez créez une barre de menu vous pouvez y
ajouter un ou plusieurs menus. La méthode setMenuBar () de classe Frame permet de spécifier la barre
de menu que doit utiliser une fenêtre.

Constructeur

public MenuBar ()

Méthodes

public synchronized void addNotify ()


public void removeNotify ()
vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 22

Ces méthodes permettent de créer et de détruire le peer d'une barre de menu (removeNotify ()
outrepasse la méthode de la classe MenuComponent).

public synchronized Menu add (Menu menu)

Permet d'ajouter un menu à une barre de menu.

public synchronized void remove (int index)


public synchronized void remove (MenuComponent menu)

Ces méthodes permettent de supprimer un menu d'une barre de menu, soit en donnant son numéro
d'ordre index, soit en donnant directement l'instance de menu à supprimer.

public int countMenus ()

Renvoie le nombre de menus courant d'une barre de menu.

public Menu getMenu (int index)

Renvoie le menu dont le numéro d'ordre est index.

public Menu getHelpMenu ()


public synchronized void setHelpMenu (Menu m)

Ces méthodes permettent d'interroger ou de modifier le menu d'aide.

Exemple

Applet ShowMenu .

La classe java.awt.MenuItem

Cette classe qui dérive de la classe MenuComponent, est la classe du package java.awt qui permet de créer
et de manipuler un élément simple de menu. Un élément de menu a un label qui est le texte affiché dans
le menu et peut être éventuellement grisé pour indiquer qu'il n'est pas utilisable à un moment donné.

Constructeur

public MenuItem (String label)

Ce constructeur permet de donner un label au nouveau menu créé.

Méthodes

public synchronized void addNotify ()

Permet de créer le peer d'un élément de menu.

public String getLabel ()


public void setLabel (String label)

Ces méthodes permettent d'interroger ou de modifier le label d'un menu.

public boolean isEnabled ()

Renvoie true si un élément de menu est utilisable.

public void enable ()


public void disable ()
public void enable (boolean cond)

Vous pouvez rendre utilisable ou inutilisable un composant grâce à ces méthodes. Un élément de menu
inutilisable (disabled ) est généralement grisé.

public String paramString ()


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 23

Méthode de la classe MenuComponent, outrepassée pour renvoyer une chaîne de caractères décrivant
l'élément de menu.

Exemple

Applet ShowMenu .

La classe java.awt.Menu

Cette classe qui dérive des classes MenuItem et MenuComponent est la classe du package java.awt qui
permet de créer et de manipuler un menu ou un sous-menu. Un menu a un label et peut contenir un ou
plusieurs éléments ; les éléments qui sont eux-mêmes des instances de la classe Menu deviennent des
sous-menus.

Constructeurs

public Menu (String label)


public Menu (String label, boolean tearOff)

Méthodes

public synchronized void addNotify ()


public synchronized void removeNotify ()

Ces méthodes permettent de créer et de détruire le peer d'un menu (removeNotify () outrepasse la
méthode de la classe MenuComponent).

public boolean isTearOff ()

public int countItems ()

Renvoie le nombre d'éléments d'un menu.

public MenuItem getItem (int index)

Renvoie l'élément du menu de numéro d'ordre index.

public synchronized MenuItem add (MenuItem menuItem)


public void add (String label)

Ces méthodes permettent d'ajouter un élément à un menu.

public void addSeparator ()

Permet d'ajouter un séparateur à un menu. Equivalent à l'appel de la méthode add ("-").

public synchronized void remove (int index)


public synchronized void remove (MenuComponent item)

Ces méthodes permettent de retirer d'un menu l'élément de numéro d'ordre index ou l'élément item.

Exemple

Applet ShowMenu .

La classe java.awt.CheckboxMenuItem

Cette classe qui dérive des classes MenuItem et MenuComponent est la classe du package java.awt qui
permet de créer et de manipuler un élément de menu à cocher. Comme un composant de classe
Checkbox, ce type d'élément de menu a un état coché ou non.

Constructeur

public CheckboxMenuItem (String label)


vendredi 11 février 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 24

Méthodes

public synchronized void addNotify ()

Permet de créer le peer d'un élément de menu à cocher.

public boolean getState ()


public void setState (boolean t)

Ces méthodes permettent d'interroger ou de modifier l'état coché ou non de l'élément de menu.

public String paramString ()

Méthode de la classe MenuComponent, outrepassée pour renvoyer une chaîne de caractères décrivant
l'élément de menu à cocher.

Exemple

Applet ShowMenu .
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 1

La gestion de l'interface utilisateur

La gestion événementielle
La classe Graphics : tout pour dessiner
Les polices de caractères
La couleur
Les classes manipulant des dimensions
La création de nouveaux composants

La gestion événementielle

Maintenant que vous avez vu comment créer et disposer un ensemble de composants à l'écran, il reste à
aborder comment gérer l'interactivité de ces composants avec votre programme pour que vous puissiez
réagir aux actions d'un utilisateur, quand il utilise tel ou tel composant.

Les événements

La classe Component déclare tout un ensemble de méthodes qui permettent de réagir aux différents
événements que peut recevoir un composant. Dans les différents exemples déjà cités, l'une de ces
méthodes, action () a été souvent outrepassée pour traiter les actions d'un utilisateur sur un composant,
mais en fait il existe tout un ensemble de méthodes traitant chacune des événements qui peuvent survenir
lors de l'utilisation de l'ordinateur. On regroupe sous le terme générique événement tout type d'entrée
qu'un programme peut s'attendre à recevoir.
Les événements peuvent se regrouper en plusieurs catégories :

Les événements provoqués par un périphérique de l'ordinateur : Entrée clavier


(Enfoncement-relâchement d'une touche du clavier) et entrée souris (Enfoncement-relâchement
d'un bouton de la souris et déplacement du pointeur de la souris).
Les événements déclenchés par le gestionnaire de fenêtre du système, suite à un enchaînement
d'actions de l'utilisateur sur une fenêtre (changement de taille d'une fenêtre, obtention du focus
dans une fenêtre,...) ou un composant (clic sur un bouton, choix dans une liste,...).

Avant l'avènement des systèmes utilisant une interface graphique, les programmes attendaient que
l'utilisateur appuie sur une touche de clavier pour effectuer telle ou telle action suivant la touche
enfoncée. Similairement, un programme utilisant les fonctionnalités d'une interface graphique utilise une
boucle pour attendre un événement quelconque qui permettra de déclencher telle ou telle action d'un
programme.
Quand un programme Java reçoit un événement, il exécute toujours le même scénario que vous devez
bien comprendre pour que vous puissiez programmer correctement. La Machine Virtuelle Java utilise
l'arbre des composants que vous avez créé (comme celui de l'applet TraitementTexte du chapitre
précédent) pour déterminer les différents composants auxquels peut se destiner un nouvel événement.
Quand un événement est reçu par un composant comp d'un programme Java, la méthode handleEvent ()
de la classe Component est appelée par la Machine Virtuelle Java. handleEvent () renvoie false par
défaut ce qui provoque l'appel de la méthode handleEvent () du composant parent du composant comp,
ainsi de suite jusqu'à ce que le composant n'est pas de parent. Si la classe d'un des composants sur
lequel handleEvent () est invoqué outrepasse cette méthode, c'est cette méthode qui sera appelée, ce
qui vous permet de modifier le comportement par défaut d'un composant qui dérive de Component.
Quand vous outrepassez cette méthode, vous pouvez terminer votre méthode de trois manières suivant
ce que vous voulez faire :

return true; généralement utilisé après le traitement d'un événement pour indiquer que vous avez
pris en compte l'événement et que vous ne voulez pas que le système continue à envoyer
l'événement au parent du composant (par exemple, si vous avez traité un événement
WINDOW_DESTROY envoyé quand l'utilisateur veut fermer une fenêtre indépendante). Dans ce cas, la
méthode handleEvent () du parent du composant n'est pas appelée.
return false; dans ce cas, la méthode handleEvent () du parent du composant est appelée.
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 2

return super.handleEvent (event); dans ce cas, la méthode handleEvent () de la classe dont


hérite le composant est appelée.

L'enchaînement des appels aux méthodes handleEvent () sur chacun des parents d'un composant est
très pratique car il évite de traiter les événements au niveau de chacun des composants où ils
surviennent. Vous pouvez ainsi traiter le clic d'un bouton dans la classe de son container, ce qui évite de
créer une nouvelle classe pour ce bouton.
Pour vous le montrer, voici une applet simple qui affiche l'heure courante dans un label quand vous
cliquez sur le bouton Mise à l'heure :

Cette applet peut être réalisée de deux manières différentes comme suit (à copier dans un fichier
dénommé MiseAJourHorloge.java et invoqué à partir d'un fichier HTML) :

import java.applet.Applet; import java.applet.Applet;


import java.awt.*; import java.awt.*;
import java.util.Date; import java.util.Date;

public class MiseAJourHorloge public class MiseAJourHorloge


extends Applet extends Applet
{ {
private Label heure = new Label ( private Label heure = new Label (
new Date ().toLocaleString ()); new Date ().toLocaleString ());
private Button miseAHeure = private Button miseAHeure =
new BoutonMiseAJour ("Mise \u00e0 l'heure", new Button ("Mise \u00e0 l'heure");
heure);

public void init () public void init ()


{ {
// Ajout des deux composants // Ajout des deux composants
add (miseAHeure); add (miseAHeure);
add (heure); add (heure);
} }
}

class BoutonMiseAJour extends Button


{
private Label heure;

// Constructeur
public BoutonMiseAJour (String label,
Label heure)
{
super (label);
this.heure = heure;
}

// Méthode appelée par la Machine // Méthode appelée par la Machine


// Virtuelle quand un événement survient // Virtuelle quand un événement survient
public boolean handleEvent (Event event) public boolean handleEvent (Event event)
{ {
// Si l'événement est un clic sur le // Si l'événement est un clic sur le
// bouton, mise à jour de l'horloge // bouton, mise à jour de l'horloge
if (event.id == Event.ACTION_EVENT) if ( event.target == miseAHeure
{ && event.id == Event.ACTION_EVENT)
heure.setText ( {
new Date ().toLocaleString ()); heure.setText (
return true; new Date ().toLocaleString ());
} return true;
return super.handleEvent (event); }
} return super.handleEvent (event);
} }
}

La première méthode respecte plus le style de programmation orientée objet : en effet, on modifie le
comportement par défaut d'un bouton en créant une nouvelle classe dérivée de Button. La seconde
méthode est plus pratique : on modifie le comportement du container qui a créé tous les composants en
lui indiquant comment gérer ses composants.
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 3

Un container contenant généralement plusieurs composants qui interagissent entre eux (comme l'applet
TraitementTexte ), on utilise plus souvent le second style de programmation en décrivant dans une seule
méthode le traitement des événements que reçoivent les différents composants gérés par le container.

Pour faciliter la clarté du traitement de certains types d'événements, la méthode handleEvent () de la


classe Component appelle certaines méthodes suivant le type d'événement reçu. Ces méthodes, comme la
méthode action (), peuvent être outrepassées dans vos classes à la place (ou en plus) de la méthode
handleEvent (). Elles doivent toutes renvoyer une valeur booléenne indiquant comme pour
handleEvent () si l'événement doit être renvoyé au parent du composant (return false;) ou non
(return true;). En voici la liste :

EVENEMENT METHODE DESCRIPTION


Event.MOUSE_ENTER public boolean mouseEnter Méthode appelée quand le pointeur de la souris
(Event evt, int x, int y) entre dans un composant (utilisé par exemple
pour changer l'aspect du pointeur).
Event.MOUSE_EXIT public boolean mouseExit Méthode appelée quand le pointeur de la souris
(Event evt, int x, int y) sort d'un composant.
Event.MOUSE_DOWN public boolean mouseDown Méthode appelée le bouton de la souris est
(Event evt, int x, int y) enfoncé.
Event.MOUSE_UP public boolean mouseUp Méthode appelée le bouton de la souris est
(Event evt, int x, int y) relâché.
Event.MOUSE_MOVE public boolean mouseMove Méthode appelée quand le pointeur de la souris
(Event evt, int x, int y) est déplacé.
Event.MOUSE_DRAG public boolean mouseDrag Méthode appelée quand le pointeur de la souris
(Event evt, int x, int y) est déplacé, avec le bouton de la souris
enfoncé.
Event.KEY_PRESS public boolean keyDown Méthode appelée quand une touche du clavier
Event.KEY_ACTION (Event evt, int key) est enfoncée.
Event.KEY_RELEASE public boolean keyUp Méthode appelée quand une touche du clavier
Event.KEY_ACTION_RELEASE (Event evt, int key) est relâchée.
Event.GOT_FOCUS public boolean gotFocus Méthode appelée quand un composant obtient
(Event evt, Object arg) le focus. Un composant qui a le focus est celui
qui reçoit les entrées du clavier.
Event.LOST_FOCUS public boolean lostFocus Méthode appelée quand un composant perd le
(Event evt, Object arg) focus.
Event.ACTION_EVENT public boolean action Méthode appelée quand l'utilisateur effectue
(Event evt, Object arg) une action dans un composant : Suivant la
classe du composant, ceci peut prendre un sens
différent.

Nota : La liste de tous les événements reconnus en Java est donnée dans la classe Event décrite
ci-après.

Pour les événements MOUSE_..., x et y représentent les coordonnées du pointeur de la souris relatives
au repère du composant où vous recevez l'événement.
Pour les événements KEY_..., key représente le code UNICODE de la touche enfoncée ou l'une des
valeurs Event.HOME à Event.F12 pour les touches spéciales du clavier.
Pour l'événement ACTION_EVENT, arg peut prendre une valeur différente suivant la classe du composant
où est survenu l'événement :
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 4

Pour les composants de classe Button, l'événement ACTION_EVENT est envoyé quand l'utilisateur
clique sur un bouton. arg est alors égal au label du bouton (de classe String).
Pour les composants de classe Checkbox (que ce soit une boite à cocher ou un bouton radio)
l'événement ACTION_EVENT est envoyé quand l'utilisateur clique sur le composant. arg est alors égal
à l'état (coché ou non) de ce composant (arg de classe Boolean).
Pour les composants de classe Choice, l'événement ACTION_EVENT est envoyé quand l'utilisateur
choisit un élément de la liste. arg est alors égal à la valeur de ce choix (de classe String).
Pour les composants de classe List, l'événement ACTION_EVENT est envoyé quand l'utilisateur a
double-cliqué sur un élément de la liste. arg est alors égal à la valeur de cet élément (de classe
String).
Pour les composants de classe TextField, l'événement ACTION_EVENT est envoyé quand l'utilisateur
a appuyé sur return dans le champ de saisie. arg est alors égal à la chaîne saisie (de classe String).
Pour les composants de classe MenuItem, l'événement ACTION_EVENT est envoyé quand l'utilisateur
choisit un élément de menu. arg est alors égal au label de cet élément de menu (de classe String).
Pour les composants de classe CheckboxMenuItem, deux événements ACTION_EVENT sont envoyés
quand l'utilisateur coche ou décoche un élément de menu. arg du premier événement est égal au
label de cet élément de menu (de classe String) et arg du second événement est égal à l'état
(coché ou non) de de cet élément (arg de classe Boolean)

Finalement, l'applet MiseAJourHorloge peut être programmée comme ceci :

import java.applet.Applet;
import java.awt.*;
import java.util.Date;

public class MiseAJourHorloge extends Applet


{
private Label heure = new Label (new Date ().toLocaleString ());
private Button miseAHeure = new Button ("Mise \u00e0 l'heure");

public void init ()


{
// Ajout des deux composants
add (miseAHeure);
add (heure);
}

// Méthode appelée par la machine virtuelle à la suite d'une action


public boolean action (Event event, Object arg)
{
// Si l'événement est un clic sur le bouton, mise à jour de l'horloge
if ("Mise \u00e0 l'heure".equals (arg))
heure.setText (new Date ().toLocaleString ());
return true;
}
}

Si vous outrepassez handleEvent () ET une des méthodes précédentes dans la même classe, n'oubliez
pas l'appel super.handleEvent (event); pour les événements que vous ne traitez pas dans
handleEvent (). Sinon ces méthodes ne seront pas appelées.

Les applets DrawIt et PaperBoardClient sont des exemples d'utilisation des méthodes mouseDown (),
mouseDrag () et mouseUp ().
L'applet PaperBoardClient est un exemple d'utilisation des méthodes mouseDown (), mouseUp (),
mouseEnter () et mouseExit ().
L'applet EchoClient est un exemple d'utilisation de la méthode keyDown ().

La classe java.awt.Event

La classe Event est la classe qui regroupe tous les événements qui peuvent survenir dans un programme.
Les méthodes de gestion événementielle (handleEvent () ou autre) que vous outrepassez dans vos
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 5

classes reçoivent un événement de cette classe créé par la Machine Virtuelle Java.
Vous pouvez aussi créer vous-même des événements que vous envoyez à un composant par la méthode
postEvent () de la classe Component. Le principal avantage d'envoyer un événement à un composant de
classe Comp1 à partir d'une autre classe CompEmetteur plutôt que d'appeler une méthode que vous créez
dans la classe Comp1, est que vous rendez indépendantes ces deux classes l'une de l'autre.

Contrairement à la plupart des classes de la librairie Java, les variables (non constantes) de la classe
Event sont public, et vous pouvez consulter directement les caractéristiques d'un événement par ces
variables.

Variables

public Object target

Désigne l'objet cible de l'événement (généralement le composant où est survenu l'événement).

public long when

Représente le moment où est survenu l'événement.

public int id

Caractérise le type d'événement qui est survenu (égal aux valeurs WINDOW_DESTROY à LOST_FOCUS décrites
ci-après).

public int x
public int y

Pour les événements souris, x et y sont les coordonnées du pointeur de la souris. Ces coordonnées sont
relatives au repère du composant où vous consultez l'événement.

public int key

Pour les événements clavier, key représente le code UNICODE de la touche enfoncée ou l'une des
valeurs HOME à F12 décrites ci-après pour les touches spéciales du clavier.

public int modifiers

Représente la combinaison des valeurs SHIFT_MASK à ALT_MASK, représentant l'état enfoncé ou non des
touches Shift, Control, Meta et Alt. Si par exemple les touches Shift et Control sont enfoncées
modifiers sera égal à SHIFT_MASK | CTRL_MASK.

public int clickCount

Pour les événements MOUSE_DOWN, représente le nombre de clics consécutifs (égal à 2 pour un double
clic,...).

public Object arg

arg peut prendre n'importe quelle valeur. Il est utilisé comme argument utilisateur pour fournir
éventuellement des renseignements supplémentaires au composant récepteur de l'événement courant.
Par exemple, l'événement ACTION_EVENT émis par la plupart des composants Java l'utilise pour
communiquer le label du composant.

public Event evt

Désigne l'événement suivant quand les événements sont mis dans une liste chaînée.

public final static int SHIFT_MASK


public final static int CTRL_MASK
public final static int ALT_MASK

Ces constantes représentent la combinaison de l'état enfoncé ou non des touches Shift (Majuscule),
Control et Alt.
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 6

public final static int META_MASK

Cette constante représente la combinaison de l'état enfoncé ou non de la touche Pomme sous MacOS ou
du bouton droit de la souris sur les autres systèmes.

public final static int HOME


public final static int END
public final static int PGUP
public final static int PGDN
public final static int UP
public final static int DOWN
public final static int LEFT
public final static int RIGHT
public final static int F1
public final static int F2
public final static int F3
public final static int F4
public final static int F5
public final static int F6
public final static int F7
public final static int F8
public final static int F9
public final static int F10
public final static int F11
public final static int F12

Ces constantes représentent les touches spéciales du clavier.

public final static int WINDOW_DESTROY


public final static int WINDOW_EXPOSE
public final static int WINDOW_ICONIFY
public final static int WINDOW_DEICONIFY
public final static int WINDOW_MOVED
public final static int KEY_PRESS
public final static int KEY_RELEASE
public final static int KEY_ACTION
public final static int KEY_ACTION_RELEASE
public final static int MOUSE_DOWN
public final static int MOUSE_UP
public final static int MOUSE_MOVE
public final static int MOUSE_ENTER
public final static int MOUSE_EXIT
public final static int MOUSE_DRAG
public final static int SCROLL_LINE_UP
public final static int SCROLL_LINE_DOWN
public final static int SCROLL_PAGE_UP
public final static int SCROLL_PAGE_DOWN
public final static int SCROLL_ABSOLUTE
public final static int LIST_SELECT
public final static int LIST_DESELECT
public final static int ACTION_EVENT
public final static int LOAD_FILE
public final static int SAVE_FILE
public final static int GOT_FOCUS
public final static int LOST_FOCUS

Ces constantes représentent tous les événements qui peuvent survenir dans un programme Java. Pour
certains de ces événements, la méthode handleEvent () de la classe Component appelle des méthodes
que vous pouvez outrepasser.

Constructeurs

public Event (Object target, long when, int id,


int x, int y, int key, int modifiers,
Object arg)
public Event (Object target, long when, int id,
int x, int y, int key, int modifiers)
public Event (Object target, int id, Object arg)

Ces constructeurs permettent de créer un événement. Les paramètres correspondent aux variables de la
classe Event.
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 7

Méthodes

public void translate (int x, int y)

Effectue une translation de (x,y) sur les coordonnées de l'événement.

public boolean shiftDown ()


public boolean controlDown ()
public boolean metaDown ()

Ces méthodes permettent de savoir si lors d'un événement les touches Shift, Control et Meta sont
enfoncées (true) ou non.

protected String paramString ()


public String toString ()

Ces méthodes sont utilisées pour fabriquer une chaîne de caractères décrivant le composant (toString
() outrepasse la méthode de la classe Object et appelle la méthode paramString ()).

Pour essayer le traitement des événements, vous pouvez par exemple améliorer l'applet du piano du
chapitre sur les applets, en ajoutant des raccourcis clavier aux différentes notes (ceci vous permettra de
tester le mixage des sons en jouant des accords).

A partir de la version de Java 1.1 un traitement des événements différents de Java 1.0 a été mis en
place. Ce sujet sera traité dans une version ultérieure de ce manuel.

Exemples

Applets Piano , PaperBoardClient , TraitementTexte , BorderBuilder , MiseAJourHorloge , ListePolices ,


Nuancier et BoutonsNavigation . Voir aussi ci-dessus.

La classe Graphics : tout pour dessiner

La classe java.awt.Graphics

La classe Graphics qui est abstract représente un contexte graphique et regroupe toutes les méthodes
permettant de dessiner (ligne, rectangle, arc de cercle, polygone, texte et image), quelque soit le support
final (écran, imprimante, image en mémoire). Il n'est pas possible de créer directement d'objet de cette
classe grâce à l'opérateur new, mais vous pouvez récupérer une instance de cette classe en paramètre des
méthodes paint () et update () ou par la méthode getGraphics () de la classe Component.

Constructeur

protected Graphics ()

Méthodes

public abstract Graphics create ()

Crée une copie d'un contexte graphique.

public Graphics create (int x, int y, int width, int height)

Crée une copie d'un contexte graphique, en effectuant en plus une translation (x,y) sur le repère
courant et en créant une zone de clipping de largeur width et hauteur height. Seuls les dessins effectués
à l'intérieur de cette zone de clipping seront affichés.

public abstract void translate (int x, int y)

Cette méthode effectue une translation (x,y) sur le repère courant. Tous les ordres graphiques seront
ensuite calculés par rapport à ce nouveau repère.

public abstract Rectangle getClipRect ()


vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 8

public abstract void clipRect (int x, int y, int width, int height)

Ces méthodes permettent d'interroger ou de modifier la zone de clipping courante. Seuls les dessins
effectués à l'intérieur de cette zone de clipping seront affichés. S'il existe déjà un rectangle de clipping,
le rectangle de clipping résultant de l'appel à la méthode clipRect () est l'intersection entre le rectangle
courant et celui passé en paramètre.

public abstract Color getColor ()


public abstract void setColor (Color c)

Ces méthodes permettent d'interroger ou de modifier la couleur courante utilisée pour dessiner.

public abstract void setPaintMode ()


public abstract void setXORMode (Color c1)

Ces méthodes permettent de choisir le mode d'utilisation de la couleur au cours d'un dessin.
setPaintMode () positionne le mode le plus usuel où tout dessin est effectué avec la couleur courante.
setXORMode () positionne le mode XOR : Dans ce mode, tout ordre graphique effectué deux fois aux
mêmes coordonnées s'annule ; si la couleur d'un pixel (ou d'un point) à dessiner est égale à la couleur
courante, il prend la couleur c1, les pixels d'une autre couleur prennent une couleur non prévisible. Ce
mode est surtout utilisé pour dessiner des rectangles élastiques quand l'utilisateur doit désigner un
ensemble d'objets ou dessiner un rectangle élastique. A chaque déplacement du pointeur de la souris,
avant de dessiner un rectangle aux nouvelles coordonnées, vous annulez le rectangle précédent en le
redessinant à la même position.

public abstract Font getFont ()


public abstract void setFont (Font font)

Ces méthodes permettent d'interroger ou de modifier la police de caractère courante utilisée pour
dessiner les textes.

public FontMetrics getFontMetrics ()


public abstract FontMetrics getFontMetrics (Font font)

Ces méthodes permettent d'interroger les tailles de la police de caractère courante ou de la police de
caractère font.

public abstract void drawLine (int x1, int y1, int x2, int y2)

Permet de dessiner une ligne reliant les points de coordonnées (x1,y1) et (x2,y2).

public void drawRect (int x, int y, int width, int height)

Permet de dessiner le contour du rectangle au point de coordonnées (x,y), de largeur (vers la droite)
width et de hauteur (vers le bas) height.
Ce rectangle s'étend en fait du pixel de coordonnées (x,y) à celui de coordonnées (x + width,y +
height), ce qui donne une largeur de width + 1 pixels et une hauteur de height + 1 pixels.

public abstract void fillRect (int x, int y, int width, int height)
public abstract void clearRect (int x, int y, int width, int height)

Ces méthodes permettent de remplir le rectangle au point de coordonnées (x,y), de largeur width et de
hauteur height. fillRect () utilise la couleur courante comme couleur de remplissage et clearRect ()
utilise la couleur de fond.
Contrairement à la méthode drawRect (), ce rectangle s'étend du pixel de coordonnées (x,y) à celui de
coordonnées (x + width - 1,y + height - 1), ce qui donne une largeur de width pixels et une hauteur
de height pixels.

public abstract void drawRoundRect (int x, int y, int width int height,
int arcWidth, int arcHeight)
public abstract void fillRoundRect (int x, int y, int width, int height,
int arcWidth, int arcHeight)

Ces méthodes permettent de dessiner ou de remplir un rectangle dont les bords sont arrondis.
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 9

public void draw3DRect (int x, int y, int width, int height, boolean raised)
public void fill3DRect (int x, int y, int width, int height, boolean raised)

Ces méthodes permettent de dessiner ou de remplir un rectangle avec effet 3D. Si raised est égal à
true, le rectangle sera en relief, sinon il sera en creux.

public abstract void drawOval (int x, int y, int width, int height)
public abstract void fillOval (int x, int y, int width, int height)

Ces méthodes permettent de dessiner le contour d'un oval ou de remplir un ovale (ou une ellipse)
contenu dans le rectangle de coordonnées (x,y), de largeur width et de hauteur height.
Pour les deux méhodes, ce rectangle s'étend en fait du pixel de coordonnées (x,y) à celui de
coordonnées (x + width,y + height), ce qui donne une largeur de width + 1 pixels et une hauteur de
height + 1 pixels.

public abstract void drawArc (int x, int y, int width, int height,
int startAngle, int arcAngle)
public abstract void fillArc (int x, int y, int width, int height,
int startAngle, int arcAngle)

Ces méthodes permettent de dessiner le contour d'un arc ou de remplir un arc d'ellipse contenu dans le
rectangle de coordonnées (x,y), de largeur width et de hauteur height. L'arc est dessiné à partir de
l'angle startAngle degrés (correspondant à l'angle de 3 heures), et s'étend sur arcAngle degrés dans le
sens trigonométrique, si arcAngle est positif.
Le rectangle englobant s'étend du pixel de coordonnées (x,y) à celui de coordonnées (x + width,y +
height), ce qui donne une largeur de width + 1 pixels et une hauteur de height + 1 pixels.

public abstract void drawPolygon (int xPoints [ ], int yPoints [ ],


int nPoints)
public void drawPolygon (Polygon p)
public abstract void fillPolygon (int xPoints [ ], int yPoints [ ],
int nPoints)
public void fillPolygon (Polygon p)

Ces méthodes permettent de dessiner ou de remplir un polygone. Les points du polygone peuvent être
donnés soit dans les tableaux xPoints et yPoints représentant les coordonnées (x,y) de chaque point,
soit par uns instance de la classe Polygon.

public abstract void drawString (String str, int x, int y)


public void drawChars (char data [ ], int offset,
int length, int x, int y)
public void drawBytes (byte data [ ], int offset,
int length, int x, int y)

Ces méthodes permettent de dessiner une chaîne de caractères avec la police de caractère courante au
point de coordonnées (x,y) : l'ordonnée y de ce point est la ligne de base sur laquelle sera dessinée la
chaîne. La chaîne de caractères peut être donnée soit par une instance de String, soit par les length
premiers caractères à partir de l'indice offset du tableau data.

public abstract void copyArea (int x, int y, int width, int height,
int dx, int dy)

Permet de copier au point de coordonnées (x + dx,y + dy) la partie du composant contenue dans le
rectangle au point de coordonnées (x,y), de largeur width et de hauteur height.

public abstract boolean drawImage (Image img, int x, int y,


ImageObserver observer)
public abstract boolean drawImage (Image img, int x, int y,
int width, int height,
ImageObserver observer)
public abstract boolean drawImage (Image img, int x, int y,
Color bgcolor,
ImageObserver observer)
public abstract boolean drawImage (Image img, int x, int y,
int width, int height,
Color bgcolor,
ImageObserver observer)
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 10

Ces méthodes permettent de dessiner l'image img, au point de coordonnées (x,y). La largeur et la
hauteur width et height permettent éventuellement de d'agrandir ou de diminuer la taille de l'image. Si
vous voulez ne dessiner qu'une partie d'une image, utiliser éventuellement la méthode clipRect (). Voir
aussi le chapitre sur les images.

public abstract void dispose ()

Permet de détruire le contexte graphique. Une fois détruit le contexte graphique n'est plus utilisable et la
translation et la zone de clipping éventuellement actives sur ce contexte graphique sont annulées.

public void finalize ()

Détruit le contexte graphique (appel de dispose ()). Cette méthode outrepasse celle de la classe Object.

public String toString ()

Renvoie une chaîne de caractère représentant le contexte graphique. Cette méthode outrepasse celle de
la classe Object.

Les méthodes de dessin (comme drawRect (), drawOval ()) prenant en paramètre une largeur et une
hauteur (width et height), n'acceptent que des valeurs positives pour ces paramètres.

Voici l'applet DrawIt , vous permettant de dessiner interactivement des formes en désignant un rectangle
englobant avec la souris. Les boutons radios à droite vous permettent de choisir le type de dessin que
vous voulez réaliser. Cette applet permet de tester le mode XOR, la plupart des méthodes de dessins de
la classe Graphics et l'utilisation des méthodes mouseDown (), mouseDrag () et mouseUp () de la classe
Component :

et le programme Java correspondant (à copier dans un fichier dénommé DrawIt.java et invoqué à partir
d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.util.Random;

public class DrawIt extends Applet


{
private CheckboxGroup choixDessin = new CheckboxGroup ();
private Panel zoneDessin = new DrawItComponent (choixDessin);

public void init ()


{
// Choix d'un layout BorderLayout (FlowLayout par défaut)
setLayout (new BorderLayout ());
// Création de boutons radios permettant de choisir
// la fonction de dessin à utiliser
Panel panel = new Panel ();
panel.setLayout (new GridLayout (7, 1));
panel.add (new Checkbox ("Ligne", choixDessin, true));
panel.add (new Checkbox ("Rectangle", choixDessin, false));
panel.add (new Checkbox ("Rectangle 3D", choixDessin, false));
panel.add (new Checkbox ("Polygone", choixDessin, false));
panel.add (new Checkbox ("Ovale", choixDessin, false));
panel.add (new Checkbox ("Arc de cercle", choixDessin, false));
panel.add (new Checkbox ("Texte", choixDessin, false));
// Ajout à droite des boutons radios
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 11

add ("East", panel);


// Ajout au centre de la zone de saisie
add ("Center", zoneDessin);
}
}

// Classe DrawItComponent
class DrawItComponent extends Panel
{
private int xDebut, yDebut,
xFin, yFin;
private CheckboxGroup choixDessin;
private Color couleurDessin;
private int angleDebut, angle;
private String typeDessin = "";

// Constructeur
DrawItComponent (CheckboxGroup choixDessin)
{
this.choixDessin = choixDessin;
}

// Méthodes mouseDown (), mouseDrag (), mouseUp ()


// appelées à l'enfoncement du bouton de la souris,
// au déplacement du pointeur, et au relâchement du bouton
public boolean mouseDown (Event evt, int x, int y)
{
xDebut = x;
yDebut = y;
dessinerElastique (x , y);
return true;
}

public boolean mouseDrag (Event evt, int x, int y)


{
// Effacement du rectangle aux anciennes coordonnées
// puis dessin d'un nouveau rectangle aux coordonnées courantes
dessinerElastique (xFin, yFin);
dessinerElastique (x, y);
return true;
}

public boolean mouseUp (Event evt, int x, int y)


{
// Effacement du dernier rectangle dessiné
dessinerElastique (xFin, yFin);
// Récupération du type de dessin demandé, d'une couleur et deux angles
// et redessin du composant
typeDessin = choixDessin.getCurrent ().getLabel ();
Random generateur = new Random ();
couleurDessin = new Color (generateur.nextFloat (),
generateur.nextFloat (),
generateur.nextFloat ());
angleDebut = generateur.nextInt () % 360;
angle = generateur.nextInt () % 360;
repaint ();
return true;
}

// Méthode dessinerElastique () : mémorise les coordonnées


// courantes, puis trace un rectangle en mode XOR
private void dessinerElastique (int xFin, int yFin)
{
this.xFin = xFin;
this.yFin = yFin;
Graphics gc = getGraphics ();
gc.setXORMode (getBackground ());
gc.drawRect (Math.min (xDebut, xFin), Math.min (yDebut, yFin),
Math.abs (xFin - xDebut), Math.abs (yFin - yDebut));
}

// Méthode appelée à la mise à jour du composant


public void paint (Graphics gc)
{
// Sélection de la couleur
gc.setColor (couleurDessin);
// Appel de la méthode de dessin suivant le type de dessin choisi
int x = Math.min (xDebut, xFin);
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 12

int y = Math.min (yDebut, yFin);


int largeur = Math.abs (xFin - xDebut);
int hauteur = Math.abs (yFin - yDebut);
if (typeDessin.equals ("Ligne"))
gc.drawLine (x, y, x + largeur, y + hauteur);
else if (typeDessin.equals ("Rectangle"))
gc.fillRect (x, y, largeur, hauteur);
else if (typeDessin.equals ("Rectangle 3D"))
gc.fill3DRect (x, y, largeur, hauteur, true);
else if (typeDessin.equals ("Polygone"))
{
int xPoints [ ] = {x, x , x + largeur, x + largeur};
int yPoints [ ] = {y, y + hauteur, y, y + hauteur};
gc.fillPolygon (xPoints, yPoints, xPoints.length);
}
else if (typeDessin.equals ("Ovale"))
gc.fillOval (x, y, largeur, hauteur);
else if (typeDessin.equals ("Arc de cercle"))
{
gc.drawOval (x, y, largeur, hauteur);
gc.fillArc (x, y, largeur, hauteur, angleDebut, angle);
}
else if (typeDessin.equals ("Texte"))
{
gc.setFont (new Font ("Helvetica", Font.PLAIN, hauteur));
FontMetrics tailles = gc.getFontMetrics ();
gc.drawString ("Texte", x, y + tailles.getAscent ()
+ tailles.getLeading ());
}
}
}

Autres exemples

Les applets Chrono , EchoClient , PaperBoardClient , ListePolices et ScrollText sont des exemples
d'utilisation de la méthode drawString ().
Les applets AfficheurDeCalcul , ObservateurCalcul, PaperBoardClient , Piano , ListePolices et Horloge
sont des exemples de tracés de lignes et de polygones.
Les applets BoutonsNavigation , MultiImages , ImageSimple , ChargementImage , ImageTableau ,
ImageNoirEtBlanc , NegatifImage , AnimationFleche et Horloge sont des exemples d'utilisation de la
méthode drawImage ().

Le package java.awt définit aussi un ensemble de classes utiles pour la classe Graphics décrites
ci-après, qui permettent de manipuler les polices de caractères, les couleurs et les objets représentant
des dimensions (point, rectangle, polygone,...) :

Les polices de caractères

La classe java.awt.Font

Cette classe permet de manipuler les polices de caractères. Une police de caractères se caractérise par
un nom, un style (normal, gras, italique) et une taille exprimée en points (voir aussi la classe
FontMetrics).

Toutes les polices de caractères du système ne sont pas disponibles en Java. Vous pouvez en obtenir la
liste (généralement, Courier, TimesRoman et Helvetica) grâce à la méthode getFontList () de la
classe Toolkit, utilisé dans l'applet ListePolices .

Variables

public final static int PLAIN


public final static int BOLD
public final static int ITALIC

Ces constantes permettent de spécifier le style de la police de caractères à sa création (normal, gras ou
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 13

italique). Il est possible de combiner les styles avec l'opérateur | (par exemple, Font.BOLD |
Font.ITALIC).

protected String name


protected int style
protected int size

Ces variables protected permettent d'accéder aux caractéristiques d'une police de caractères, dans les
classes dérivées de Font.

Constructeur

public Font (String name, int style, int size)

Construit une police de caractères de nom name, de style style et de taille size.

Méthodes

public String getName ()


public int getStyle ()
public int getSize ()

Ces méthodes permettent d'interroger le nom, le style et la taille d'une police de caractères`.

public boolean isPlain ()


public boolean isBold ()
public boolean isItalic ()

Ces méthodes renvoient true si une police de caractères a un style normal, gras ou italique.

public String getFamily ()

Renvoie le nom de la famille à laquelle appartient une police de caractères. La famille d'une police
dépend du système sur lequel fonctionne la Machine Virtuelle Java, tandis que le nom d'une police est
un nom logique qui est toujours le même quelque soit le système pour rendre un programme Java
portable.

public static Font getFont (String prop)


public static Font getFont (String prop, Font font)

Ces méthodes renvoient la police de caractère mémorisée par la propriété prop du système (voir la classe
System). Si cette propriété n'existe pas, la seconde méthode renvoie la valeur par défaut font.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer une
police de caractères à un objet ou renvoyer une chaîne de caractères décrivant une police.

Exemples

Applets DrawIt , ListePolices . et ScrollText .

La classe java.awt.FontMetrics

Cette classe décrit les différentes tailles (hauteurs par rapport à la ligne de base, largeurs diverses) d'une
police de caractères (de classe Font).

Variable

protected Font font

Mémorise la police de caractères décrite.

Constructeur
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 14

protected FontMetrics (Font font)

Ce constructeur est protected ce qui empêche de créer directement une instance de la classe
FontMetrics. Les méthodes getFontMetrics () des classes Graphics et Toolkit vous permettent
d'interroger les tailles d'une police de caractères.

Méthodes

public Font getFont ()

Renvoie la police de caractères décrite par une instance de la classe FontMetrics.

public int getHeight ()


public int getLeading ()
public int getAscent ()
public int getDescent ()

Ces méthodes revoient la hauteur, l'interligne, la hauteur au-dessus de la ligne de base et le jambage
d'une police de caractères. La hauteur, différente de la taille de la police donnée à sa création, est égale à
la somme des trois autres dimensions.

public int getMaxAscent ()


public int getMaxDescent ()

Ces méthodes revoient la hauteur maximum au-dessus de la ligne de base et le jambage maximum de
tous les caractères d'une police.

public int getMaxAdvance ()

Renvoie la largeur du plus large des caractères d'une police.

public int charWidth (int ch)


public int charWidth (char ch)

Ces méthodes renvoient la largeur du caractère ch d'une police.

public int stringWidth (String str)


public int charsWidth (char data [ ], int offset, int length)
public int bytesWidth (byte data [ ], int offset, int length)

Ces méthodes renvoient la largeur d'une chaîne de caractères. La chaîne de caractères peut être donnée
soit par une instance de String, soit par les length premiers caractères à partir de l'indice offset du
tableau data.

public int [ ] getWidths ()

Renvoie un tableau décrivant la taille des 256 premiers caractères d'une police.

public String toString ()

Cette méthode outrepasse la méthode toString () de la classe Object, pour renvoyer une chaîne de
caractères décrivant les principales tailles d'une police de caractères.

L'applet suivante décrit la liste de l'ensemble des polices de caractères disponibles avec Java. Cliquez sur
un élément de la liste ou saisissez une taille de police de caractères dans le champ Size, pour afficher un
exemple de texte et les caractéristiques de la police choisie :

Voici le programme Java correspondant (à copier dans un fichier dénommé ListePolices.java et invoqué à
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 15

partir d'un fichier HTML) :

import java.awt.*;
import java.applet.*;

public class ListePolices extends Applet


{
AfficheurTexte exemple = new AfficheurTexte ();
List listePolices = new List ();
TextField taillePolice = new TextField ("24");
Label labelHeight = new Label ();
Label labelLeading = new Label ();
Label labelAscent = new Label ();
Label labelDescent = new Label ();
Label labelMaxAdvance = new Label ();

public void init ()


{
// Choix d'un layout BorderLayout (FlowLayout par défaut)
setLayout (new BorderLayout (5, 0));

// Création d'un panel contenant la liste des polices


// et la zone de saisie de la taille
Panel panelSaisie = new Panel ();
panelSaisie.setLayout (new BorderLayout ());

// Récupération de la liste des polices disponibles


String [ ] ensemblePolices = getToolkit ().getFontList ();
for (int i = 0; i < ensemblePolices.length; i++)
listePolices.addItem (ensemblePolices [i]);

panelSaisie.add ("Center", listePolices);


Panel panelTaille = new Panel ();

panelTaille.add (new Label ("Size :"));


panelTaille.add (taillePolice);
panelSaisie.add ("South", panelTaille);
// Ajout du panel de saisie
add ("West", panelSaisie);

// Ajout du composant affichant une chaîne d'exemple


add ("Center", exemple);

// Création d'un panel utilisant un layout de grille


// et affichant un ensemble de labels qui permettent
// de décrire les caractéristiques de la police sélectionnée
Panel panelResultat = new Panel ();
panelResultat.setLayout (new GridLayout (5, 2));
panelResultat.add (new Label ("Height :"));
panelResultat.add (labelHeight);
panelResultat.add (new Label ("Leading :"));
panelResultat.add (labelLeading);
panelResultat.add (new Label ("Ascent :"));
panelResultat.add (labelAscent);
panelResultat.add (new Label ("Descent :"));
panelResultat.add (labelDescent);
panelResultat.add (new Label ("MaxAdvance :"));
panelResultat.add (labelMaxAdvance);
// Ajout du panel de résultat
add ("East", panelResultat);
}

// Gestion des événements sur la liste et la zone de saisie


public boolean handleEvent (Event event)
{
// Si un événement de sélection de la liste ou un
// événement de changement de texte survient,
// mise à jour des labels décrivant la police et de l'exemple
if ( event.id == Event.LIST_SELECT
|| ( event.id == Event.ACTION_EVENT
&& event.target == taillePolice))
{
Font police = new Font (listePolices.getSelectedItem (),
Font.PLAIN,
Integer.parseInt (taillePolice.getText ()));
FontMetrics metrics = getToolkit ().getFontMetrics (police);
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 16

labelHeight.setText (String.valueOf (metrics.getHeight ()));


labelLeading.setText (String.valueOf (metrics.getLeading ()));
labelAscent.setText (String.valueOf (metrics.getAscent ()));
labelDescent.setText (String.valueOf (metrics.getDescent ()));
labelMaxAdvance.setText (String.valueOf (metrics.getMaxAdvance ()));
exemple.modifierTexte (police, "Bonjour");
return true;
}
return super.handleEvent (event);
}
}

// Classe AfficheurTexte affiche un texte avec une police de caractères


class AfficheurTexte extends Panel
{
Font police;
String texte;

// La méthode modifierTexte () permet de modifier


// la police de caractères et le texte du composant
public void modifierTexte (Font police, String texte)
{
this.police = police;
this.texte = texte;
repaint ();
}

public void paint (Graphics gc)


{
if (texte != null)
{
// Dessin d'un cadre autour du composant
gc.drawRect (0, 0, size ().width - 1, size ().height - 1);

// Dessin des rectangles représentant les tailles


FontMetrics metrics = gc.getFontMetrics (police);
int y = (size ().height - metrics.getHeight ()) / 2;
int largeurTexte = metrics.stringWidth (texte);
gc.setColor (Color.blue.darker ());
gc.drawRect (5, y, largeurTexte, metrics.getHeight ());
gc.drawRect (5, y + metrics.getLeading (), largeurTexte,
metrics.getAscent ());

// Dessin du texte dans les rectangles


gc.setColor (Color.black);
gc.setFont (police);
gc.drawString (texte, 5, y + metrics.getAscent ()
+ metrics.getLeading ());
}
}
}

Autres exemples

Applets DrawIt et ScrollText .

La couleur

La classe java.awt.Color

Cette classe permet de manipuler une couleur. Elle déclare un ensemble de constantes décrivant les
couleurs les plus communément utilisées. Ses constructeurs permettent de créer des couleurs à partir de
leurs composantes RGB (Red Green Blue : Rouge Vert Bleu), mais les méthodes brighter () et darker
() sont aussi très pratiques pour créer des couleurs plus claire ou plus foncée à partir d'une couleur
existante.

Variables

public final static Color white


public final static Color lightGray
public final static Color gray
public final static Color darkGray
public final static Color black
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 17

public final static Color red


public final static Color pink
public final static Color orange
public final static Color yellow
public final static Color green
public final static Color magenta
public final static Color cyan
public final static Color blue

Ces constantes permettent de récupérer directement des couleurs prédéfinies (blanc, gris clair, gris, gris
foncé, noir, rouge, rose, orange, jaune, vert, magenta, cyan et bleu).

Constructeurs

public Color (int r, int g, int b)

Construit une couleur à partir de ses composantes entières rouge r, vert g et bleu b comprises entre 0 et
255.

public Color (int rgb)

Construit une couleur à partir de l'entier rgb combinaison des composantes rouge (bits 16 à 23), vert (bits
8 à 15) et bleu (bits 0 à 7).

public Color (float r, float g, float b)

Construit une couleur à partir de ses composantes décimales rouge r, vert g et bleu b comprises entre
0.0 et 1.0.

Méthodes

public int getRed ()


public int getGreen ()
public int getBlue ()

Ces méthodes renvoient les composantes rouge, vert ou bleu d'une couleur comprises entre 0 et 255.

public int getRGB ()

Renvoie un entier combinaison des composantes rouge (bits 16 à 23), vert (bits 8 à 15) et bleu (bits 0 à 7)
d'une couleur.

public Color brighter ()


public Color darker ()

Ces méthodes renvoient une couleur plus claire ou plus foncée d'une couleur.

public static Color getColor (String name)


public static Color getColor (String name, Color v)
public static Color getColor (String name, int v)

public static int HSBtoRGB (float hue, float saturation,


float brightness)

Renvoie un entier combinaison des composantes rouge, vert et bleu d'une couleur exprimée par sa teinte
hue, sa saturation saturation et sa luminosité brightness comprises entre 0.0 et 1.0.

public static float [ ] RGBtoHSB (int r, int g, int b,


float hsbvals [ ])

Renvoie un tableau décrivant la teinte, la saturation et la luminosité (aux indices 0, 1 et 2) d'une couleur à
partir de ses composantes rouge, vert et bleu comprises entre 0 et 255. Les valeurs sont stockées dans
hsbvals s'il est différent de null, sinon un nouveau tableau est créé.

public static Color getHSBColor (float hue, float saturation,


float brightness)

Crée une couleur à partir de sa teinte hue, sa saturation saturation et sa luminosité brightness
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 18

comprises entre 0.0 et 1.0.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer une
une couleur à un objet ou renvoyer une chaîne de caractères décrivant la couleur.

L'applet suivante dessine un nuancier. Les couleurs utilisent le mode HSB (Teinte, Saturation,
Luminosité) pour avoir plus simplement les couleurs de toute la palette à partir d'une teinte. Quand vous
cliquez sur une couleur, le label en dessous du nuancier indique la couleur choisie. Cette applet vous
permet de tester la palette des couleurs disponibles sur une Machine Virtuelle :

Voici le programme Java correspondant (à copier dans un fichier dénommé Nuancier.java et invoqué à
partir d'un fichier HTML) :

import java.awt.*;
import java.applet.*;

public class Nuancier extends Applet


{
// Label bidon utilisé pour afficher en couleur de fond la couleur choisie
Label labelColorise = new Label (" ");
Label labelCouleur = new Label ("Cliquez sur une couleur...");

public void init ()


{
// Choix d'un layout BorderLayout (FlowLayout par défaut)
setLayout (new BorderLayout ());
// Ajout d'un composant affichant le nuancier
add ("Center", new ComposantNuancier ());

// Création et ajout d'un panel de deux labels d'information


Panel panelInfo = new Panel ();
panelInfo.setLayout (new BorderLayout (5, 0));
panelInfo.add ("West", labelColorise);
panelInfo.add ("Center", labelCouleur);
add ("South", panelInfo);
}

public boolean action (Event event, Object arg)


{
// Si une action est reçue du composant nuancier
// modifier la couleur de fond du label sans texte
// et afficher les composantes RGB de la couleur choisie
if (event.target instanceof ComposantNuancier)
{
Color couleur = (Color)arg;
labelColorise.setBackground (couleur);
labelColorise.repaint ();
labelCouleur.setText ("Couleur : Rouge " + couleur.getRed ()
+ ", Vert " + couleur.getGreen ()
+ ", Bleu " + couleur.getBlue ());
}

return true;
}
}

// Classe ComposantNuancier
class ComposantNuancier extends Panel
{
private static final int TAILLE = 40;
private Rectangle [ ] carreaux = new Rectangle [TAILLE * TAILLE];
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 19

private Color [ ] couleurCarreaux = new Color [TAILLE * TAILLE];

// Constructeur : crée l'ensemble des couleurs des carreaux


ComposantNuancier ()
{
for (int i = 0; i < TAILLE; i++)
{
// Première boucle réalisant un fondu du noir vers la teinte (moitié supérieure)
int j;
for (j = 0; j < TAILLE / 2; j++)
couleurCarreaux [i * TAILLE + j] =
Color.getHSBColor ((float)i / TAILLE, // Teinte
1, // Saturation
(float)j / TAILLE * 2); // Luminosité

// Seconde boucle diminuant la saturation de la couleur (moitié inférieure)


for ( ; j < TAILLE; j++)
couleurCarreaux [i * TAILLE + j] =
Color.getHSBColor ((float)i / TAILLE, // Teinte
(float)(TAILLE - 1 - j) / TAILLE * 2, // Saturation
1); // Luminosité
}
}

// Méthode appelée quand le composant change de taille


public void reshape (int x, int y, int width, int height)
{
super.reshape (x, y, width, height);
// Calcul de la largeur et de la hauteur des carreaux
int largeur = size ().width / TAILLE;
int hauteur = size ().height / TAILLE;
// Création des rectangles représentant les carreaux de couleur
for (int i = 0; i < TAILLE; i++)
for (int j = 0; j < TAILLE; j++)
carreaux [i * TAILLE + j] =
new Rectangle (largeur * i, hauteur * j, largeur, hauteur);
}

public void paint (Graphics gc)


{
for (int i = 0; i < carreaux.length; i++)
{
// Sélection d'une couleur et dessin du carreau
gc.setColor (couleurCarreaux [i]);
gc.fillRect (carreaux [i].x, carreaux [i].y,
carreaux [i].width, carreaux [i].height);
}
}

public boolean mouseDown (Event evt, int x, int y)


{
// Recherche du carreau dans lequel l'utilisateur à cliquer
// et envoi d'un événement d'action
for (int i = 0; i < carreaux.length; i++)
if (carreaux [i].inside (x, y))
postEvent (new Event (this, Event.ACTION_EVENT, couleurCarreaux [i]));
return true;
}
}

Autres exemples

Applets ObservateurCalcul , DrawIt , ListePolices , MultiImages et ScrollText .

Les classes manipulant des dimensions

La classe java.awt.Dimension

Cette classe permet de manipuler les dimensions d'un objet (largeur et hauteur) grâce à ses deux
variables public width et height.

Variables

public int width


vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 20

public int height

Constructeurs

public Dimension ()
public Dimension (Dimension d)
public Dimension (int width, int height)

Méthode

public String toString ()

Cette méthode outrepasse la méthode toString () de la classe Object, pour renvoyer une chaîne de
caractères décrivant les dimensions.

Exemples

Applets BoutonsNavigation , MultiImages , ImageTableau , ImageNoirEtBlanc , ScrollText et Horloge .

La classe java.awt.Insets

Cette classe qui implémente l'interface Cloneable, permet de manipuler les bordures d'un container, qui
délimitent une zone dans laquelle il n'est pas possible de dessiner.

Variables

public int top


public int left
public int bottom
public int right

Ces variables public désignent les bordures en haut, à gauche, en bas et à droite.

Constructeur

public Insets (int top, int left, int bottom, int right)

Méthodes

public String toString ()


public Object clone ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer une chaîne de caractères décrivant
les valeurs des bordures ou un clone d'une instance de la classe Insets.

La classe java.awt.Point

Cette classe permet de manipuler des points de coordonnées (x,y).

Variables

public int x
public int y

Constructeur

public Point (int x, int y)

Méthodes

public void move (int x, int y)

Déplace le point en (x,y).

public void translate (int x, int y)

Effectue une translation (x,y) sur un point.


vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 21

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
point à un objet ou renvoyer une chaîne de caractères décrivant le point.

La classe java.awt.Polygon

Cette classe permet de manipuler des polygones, ensemble de npoints points dont les coordonnées sont
enregistrées dans les tableaux xpoints et ypoints.

Variables

public int npoints


public int xpoints [ ]
public int ypoints [ ]

Constructeurs

public Polygon ()
public Polygon (int xpoints [ ], int ypoints [ ], int npoints)

Méthodes

public void addPoint (int x, int y)

Ajoute le point de coordonnées (x,y) à la fin de l'ensemble des points.

public Rectangle getBoundingBox ()

Renvoie le rectangle englobant tous les points du polygone.

public boolean inside (int x, int y)

Renvoie true si le point de coordonnées (x,y) est à l'intérieur du polygone.

Exemple

AppletPaperBoardClient .

La classe java.awt.Rectangle

Cette classe permet de manipuler des rectangles, mémorisés sous forme d'un point origine de
coordonnées (x,y) représentant le coin supérieur gauche, de sa largeur width et de sa hauteur height.

Variables

public int x
public int y
public int width
public int height

Constructeurs

public Rectangle ()
public Rectangle (int x, int y, int width, int height)
public Rectangle (int width, int height)
public Rectangle (Point p, Dimension d)
public Rectangle (Point p)
public Rectangle (Dimension d)

Méthodes

public void reshape (int x, int y, int width, int height)

Permet de changer l'origine, la largeur et la hauteur d'un rectangle.


vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 22

public void move (int x, int y)

Déplace l'origine du rectangle en (x,y).

public void translate (int x, int y)

Effectue une translation (x,y) sur l'origine du rectangle.

public void resize (int width, int height)

Modifie la largeur et la hauteur d'un rectangle.

public boolean inside (int x, int y)

Renvoie true si le point de coordonnées (x,y) est à l'intérieur d'un rectangle.

public boolean intersects (Rectangle r)

Renvoie true si le rectangle r et le rectangle sur lequel la méthode est invoquée ont une intersection.

public Rectangle intersection (Rectangle r)


public Rectangle union Rectangle r)

Ces méthodes renvoie un rectangle résultat de l'intersection ou de l'union entre deux rectangles.

public void add (int newx, int newy)


public void add (Point pt)

Ces méthodes ajoutent un point à un rectangle qui est agrandit si nécessaire pour contenir le point passé
en paramètre.

public void add (Rectangle r)

Ajoute le rectangle r au rectangle sur lequel la méthode est invoquée. Le résultat est l'union des deux
rectangles.

public void grow (int horiz, int vert)

Agrandit un rectangle de horiz points à gauche et à droite, et de vert points en haut et en bas.

public boolean isEmpty ()

Renvoie true si la largeur ou la hauteur d'un rectangle est négative ou nulle.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
rectangle à un objet ou renvoyer une chaîne de caractères décrivant le rectangle.

La création de nouveaux composants

Dans les trois exemples précédents, les classes DrawItComponent, AfficheurTexte et ComposantNuancier
dérivent de la classe Panel. Bien que ces applets fonctionnent correctement, Panel est une classe de
container donc prévue pour contenir d'autres composants.
Pour créer de nouveaux composants, il est plutôt recommandé de dériver de nouvelles classes à partir
de la classe Canvas.

La classe java.awt.Canvas

Cette classe qui dérive de la classe Component, est la classe du package java.awt dont vous devez
dériver pour créer vos propres classes de composants.
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 23

Constructeur

public Canvas ()

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
instance de Canvas.

public void paint (Graphics gc)

Cette méthode outrepasse la méthode paint () de la classe Component, pour remplir le composant de la
couleur de fond.

L'applet suivante utilise une nouvelle classe de composant BoutonImage. Ce composant est un bouton
utilisant deux images de même taille, pour représenter l'état relâché et l'état enfoncé du bouton :

Le programme Java correspondant a été divisé en deux fichiers. Comme la classe BoutonImage (à copier
dans un fichier dénommé BoutonImage.java ) peut être appelée à être réutilisée, elle est rangée dans un
package tools :

// Les classes de ce fichier appartiennent au package tools


package tools;

import java.awt.*;
import java.net.*;

public class BoutonImage extends Canvas


{
private boolean boutonEnfonce = false;
private boolean boutonSourisEnfonce = false;

private Object argAction;


private Image imageRelache;
private Image imageEnfonce;

// Constructeur
public BoutonImage (Object argAction, URL urlImageRelache,
URL urlImageEnfonce)
{
try
{
this.argAction = argAction;
// Création des images enfoncé/relâché du bouton
imageRelache = getToolkit ().getImage (urlImageRelache);
imageEnfonce = getToolkit ().getImage (urlImageEnfonce);
// Création d'un MediaTracker pour charger les images
// afin que leur taille soit disponible pour le layout
MediaTracker imageTracker = new MediaTracker (this);
imageTracker.addImage (imageRelache, 0);
imageTracker.addImage (imageEnfonce, 0);
imageTracker.waitForAll ();
// En cas d'erreur, déclenchement d'une exception
if (imageTracker.isErrorAny ())
throw new IllegalArgumentException ("Images introuvables");
}
catch (InterruptedException e)
{ }
}

// Il est conseillé d'outrepasser les méthodes preferredSize ()


// et minimumSize () pour que les layouts utilisant ces méthodes
// puissent dimensionner ce composant correctement
public Dimension preferredSize ()
{
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 24

return new Dimension (imageRelache.getWidth (this),


imageRelache.getHeight (this));
}

public Dimension minimumSize ()


{
return preferredSize ();
}

// La méthode paint () dessine l'image suivant l'état du bouton


public void paint (Graphics gc)
{
if (boutonEnfonce)
gc.drawImage (imageEnfonce, 0, 0, this);
else
gc.drawImage (imageRelache, 0, 0, this);
}

// Les méthodes mouseDown () et mouseUp () modifient l'image du


// bouton à l'enfoncement/relâchement du bouton de la souris
public boolean mouseDown (Event event, int x, int y)
{
modifierEnfoncementBouton (true);
boutonSourisEnfonce = true;
return true;
}

public boolean mouseUp (Event event, int x, int y)


{
boolean boutonEnfonce = this.boutonEnfonce;
modifierEnfoncementBouton (false);
boutonSourisEnfonce = false;
// Si le bouton est resté enfoncé, envoi d'un événement d'action
if (boutonEnfonce)
postEvent (new Event (this, Event.ACTION_EVENT, argAction));
return true;
}

// Les méthodes mouseEnter () et mouseExit () modifient


// l'image du bouton si le bouton de la souris est enfoncé
public boolean mouseEnter (Event event, int x, int y)
{
if (boutonSourisEnfonce)
modifierEnfoncementBouton (true);
return true;
}

public boolean mouseExit (Event event, int x, int y)


{
if (boutonSourisEnfonce)
modifierEnfoncementBouton (false);
return true;
}

private void modifierEnfoncementBouton (boolean nouvelEtat)


{
// Si l'état enfoncé/relâché du bouton a changé
// mettre à jour le dessin du bouton
if (boutonEnfonce != nouvelEtat)
{
boutonEnfonce = nouvelEtat;
paint (getGraphics ());
}
}
}

L'autre fichier décrit l'applet (à copier dans un fichier dénommé BoutonsNavigation.java et invoqué à partir
d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.net.*;
// Importation de la classe BoutonImage du package tools
import tools.BoutonImage;

public class BoutonsNavigation extends Applet


{
vendredi 11 février 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 25

public void init ()


{
try
{
// Création et ajout des deux boutons à l'applet
add (new BoutonImage (new URL (getDocumentBase (), "awtlayouts10.html"),
new URL (getCodeBase (), "prevup.gif"),
new URL (getCodeBase (), "prevdown.gif")));
add (new BoutonImage (new URL (getDocumentBase (), "image10.html"),
new URL (getCodeBase (), "nextup.gif"),
new URL (getCodeBase (), "nextdown.gif")));
}
catch (MalformedURLException e)
{ }
}

// Les boutons de classe BoutonImage émettent des événements ACTION_EVENT


public boolean action (Event evt, Object arg)
{
// Chargement d'un document HTML suivant le bouton enfoncé
if (arg instanceof URL)
getAppletContext ().showDocument ((URL)arg);

return true;
}
}

En récupérant les valeurs de paramètres utilisant la balise PARAM, cette applet pourrait être généralisée
pour qu'elle configure les images et les actions des boutons, sans avoir à modifier son code source à
chaque utilisation différente dans un fichier HTML.
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 1

Les images

La génération d'images
Le chargement des images
La création d'images
Transformer des images avec un filtre
Comment ça marche ?
Gestion d'animations

La génération d'images

Les images, instances de la classe Image, peuvent provenir de deux sources différentes :

Chargées à partir d'un fichier grâce aux méthodes getImage () des classes Applet ou Toolkit. Ce fichier
pouvant être éventuellement téléchargé à partir d'une URL sur un réseau, son chargement peut prendre un
certain temps. C'est pourquoi la création de ce type d'image se fait en deux temps : getImage () permet de
créer une instance de la classe Image tandis que d'autres méthodes se chargent d'initialiser et de surveiller son
chargement, et d'attendre la fin de son chargement.
Créées de toute pièce grâce aux méthodes createImage () des classes Component et Toolkit. Ces images sont
souvent utilisées comme buffer ou comme bitmap affichés à l'écran qu'une fois leur dessin terminé.

Les méthodes drawImage () de la classe Graphics permettent d'afficher une image à un point donné en la
redimensionnant éventuellement, comme le montre l'applet suivante, qui crée une image d'un nuancier, et
l'affiche à 4 tailles différentes :

Voici le programme Java correspondant (à copier dans un fichier dénommé MultiImages.java et invoqué à partir
d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class MultiImages extends Applet


{
private Image image;

public void reshape (int x, int y, int width, int height)


{
// Effectuer le comportement par défaut du changement de taille
super.reshape (x, y, width, height);

// Création de l'image que quand le peer de l'applet existe


if (getPeer () != null)
{
Dimension taille = size ();
taille.width /= 3;
taille.height /= 3;
// Création d'une image de dimensions 3 fois plus petites que l'applet
image = createImage (taille.width, taille.height);
Graphics gc = image.getGraphics ();

// Remplissage d'une image avec un nuancier de rouge et vert


for (int i = 0; i < taille.height; i++)
for (int j = 0; j < taille.width; j++)
{
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 2

gc.setColor (new Color ((float)i / taille.height, // Rouge


(float)j / taille.width, // Vert
0)); // Bleu
gc.fillRect (i, j, 1, 1);
}
}
}

public void paint (Graphics gc)


{
if (image != null)
{
// Dessin de l'image à sa taille et agrandie
gc.drawImage (image, 0, 0, this);
gc.drawImage (image, image.getWidth (this), 0,
image.getWidth (this) * 2,
image.getHeight (this), this);
gc.drawImage (image, 0, image.getHeight (this),
image.getWidth (this),
image.getHeight (this) * 2, this);
gc.drawImage (image, image.getWidth (this), image.getHeight (this),
image.getWidth (this) * 2,
image.getHeight (this) * 2, this);
}
}
}

Soit un composant comp sur lequel vous invoquez la méthode createImage (int width, int height)
de la classe Component (comp peut être une applet ou un autre composant). comp.createImage () ne
crée une image que si le peer du composant comp existe. Le peer n'étant pas créé dans le constructeur
d'un composant (de classe dérivée de Applet, Canvas, ou autre), cette caractéristique vous amène à créer
une image pas forcément dans la méthode à laquelle vous pensiez au départ... Vous devez appeler
createImage () qu'au moment où ce peer existe, par exemple dans la méthode init () de la classe
Applet, dans la méthode paint () d'une classe dérivée de Canvas, ou en outrepassant la méthode
addNotify () de la manière suivante :

//...
Image image;
public void addNotify ()
{
super.addNotify ();
// Le peer du composant existe, on peut créer une image
image = createImage (largeur, hauteur);
}
//...

Souvenez-vous donc que si un appel à comp.createImage () vous renvoie null, demandez vous
d'abord si le peer du composant comp existe à ce moment.

La classe java.awt.Image

Cette classe abstract permet de manipuler les images en Java.

Variable

public final static Object UndefinedProperty

Constructeur

public Image ()

Méthodes

public abstract int getWidth (ImageObserver observer)


public abstract int getHeight (ImageObserver observer)

Ces méthodes renvoient la largeur ou la hauteur d'une image. La classe Component implémentant l'interface
ImageObserver, observer peut être égal à un composant. Si l'image n'est pas encore chargée, la valeur -1 est
renvoyée.
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 3

public abstract Graphics getGraphics ()

Renvoie une instance de la classe Graphics, grâce à laquelle vous pouvez dessiner dans l'image.

public abstract ImageProducer getSource ()

Renvoie une instance de la classe implémentant l'interface ImageProducer. Cet objet est notamment utilisé par
les classes réalisant un filtrage.

public abstract Object getProperty (String name, ImageObserver observer)


public abstract void flush ()

Exemples

Applets BoutonsNavigation , MultiImages , ImageSimple , ChargementImage , ImageTableau , ImageNoirEtBlanc ,


NegatifImage , AnimationFleche , ScrollText et Horloge .

Le chargement des images

Comme il est expliqué au début de ce chapitre, la méthode getImage () permet créer une instance d'une
image. Pour initialiser et surveiller le chargement d'une image et l'utiliser quand elle est partiellement ou
entièrement chargée, il existe plusieurs moyens :

Soit à l'appel de l'une des méthodes drawImage () de la classe Graphics avec en paramètre une
image img. Si img n'est encore pas chargée, la méthode drawImage () débute le chargement de
l'image de manière asynchrone et rend la main. Le dernier paramètre de drawImage () doit être une
instance d'une classe implémentant l'interface ImageObserver comme par exemple la classe
Component. Cette interface ne déclare qu'une seule méthode imageUpdate () et l'implémentation de
cette méthode dans la classe Component redessine le composant pour mettre à jour le dessin de
l'image au fur et à mesure de son chargement.
Donc, si vous donnez en dernier paramètre le composant dans lequel l'image est affichée, l'image
sera affichée automatiquement aussitôt qu'elle est disponible.
L'applet suivante utilise la méthode drawImage () pour charger et afficher une image :

Voici le programme Java correspondant (à copier dans un fichier dénommé ImageSimple.java et


invoqué à partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class ImageSimple extends Applet


{
private Image image;

public void init ()


{
// Création d'une image
image = getImage (getCodeBase (), "monval.jpg");
}

public void paint (Graphics gc)


{
if (image != null)
// Affichage de l'image (image chargée automatiquement)
gc.drawImage (image, 0, 0, this);
}
}

Soit à l'appel des méthodes prepareImage (Image img, ImageObserver observer) ou


vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 4

prepareImage (Image img, int width, int height, ImageObserver observer) des classe
Component et Toolkit. Ces méthodes permettent de débuter le chargement de l'image img par un
autre thread de manière asynchrone et rend la main. Les paramètres width et height permettent de
redimensionner l'image dès son chargement. Le dernier paramètre observer doit être une instance
d'une classe implémentant l'interface ImageObserver et permet de surveiller l'état du chargement de
l'image. La classe Component implémentant cette interface, vous pouvez utiliser un composant
comme paramètre.
L'applet ChargementImage est un exemple d'utilisation de la méthode prepareImage () et de
l'interface ImageObserver.
Soit en utilisant la classe MediaTracker, qui permet de surveiller et d'attendre la fin du chargement
d'une image. Cette classe qui utilise la méthode prepareImage () et l'interface ImageObserver,
simplifie la programmation du chargement d'une image.

De plus, les méthodes checkImage () des classes Component et Toolkit permettent de vérifier l'état du
chargement d'une image. Ces méthodes prennent en dernier paramètre une instance d'une classe
implémentant l'interface ImageObserver, dont la méthode imageUpdate () est appelée pour lui
communiquer l'état de l'image.

La classe java.awt.MediaTracker

Cette classe permet de gérer le chargement d'une ou plusieurs images. Elle est utilisée par les applets
BoutonsNavigation , AnimationFleche et Horloge pour charger les images dont elles ont besoin. Les
méthodes addImage () permettent de donner un ensemble d'image à charger et les méthodes waitForID
() et waitForAll () de lancer le chargement des images.

Variables

public final static int ABORTED


public final static int COMPLETE
public final static int ERRORED
public final static int LOADING

Les méthodes statusID () et statusAll () renvoie une combinaison de ces constantes pour indiquer
l'état du chargement des images (annulé, terminé, erreur ou en cours de chargement).

Constructeur

public MediaTracker (Component comp)

Construit une instance de MediaTracker. comp désigne un composant dans lequel sera visualisé les
images à charger.

Méthodes

public void addImage (Image image, int id)


public synchronized void addImage (Image image, int id,
int width, int height)

Ces méthodes permettent d'ajouter une image à charger. width et height permettent d'éventuellement
redimensionner l'image dès son chargement. id est un identifiant numérique permettant de rassembler les
images, pour les charger par groupe avec les méthodes ...ID () de cette classe.

public void waitForID (int id)


public synchronized boolean waitForID (int id, long ms)
public void waitForAll ()
public synchronized boolean waitForAll (long ms)

Ces méthodes permettent de lancer le chargement des images d'identifiant id ou de toutes les images et
met en attente le thread courant jusqu'à la fin de leur chargement ou pendant le laps de temps ms
millisecondes.

public int statusID (int id, boolean load)


public int statusAll (boolean load)
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 5

Ces méthodes renvoient l'état du chargement des images d'identifiant id ou de toutes les images. La
valeur renvoyée est une combinaison des constantes ABORTED, COMPLETE, ERRORED et LOADING. Si load est
égal à true, le chargement des images est démarré.

public boolean checkID (int id)


public synchronized boolean checkID (int id, boolean load)
public synchronized boolean checkAll ()
public boolean checkAll (boolean load)

Ces méthodes renvoient true si les images d'identifiant id ou toutes les images sont chargées
correctement (état égal à COMPLETE). Si load est égal à true (égal à false par défaut), le chargement des
images est démarré.

public synchronized boolean isErrorID (int id)


public synchronized boolean isErrorAny ()

Ces méthodes renvoient true si une erreur est survenue pendant le chargement d'une des images
d'identifiant id ou de n'importe quelle image (état égal à ERRORED).

public synchronized Object [ ] getErrorsID (int id)


public synchronized Object [ ] getErrorsAny ()

Ces méthodes renvoient un tableau contenant toutes les images éventuellement d'identifiant id, dont le
chargement a produit une erreur.

Exemples

Applets BoutonsNavigation , AnimationFleche et Horloge .

L'interface java.awt.image.ImageObserver

Cette interface est utilisée pour surveiller le chargement d'une image. Une classe implémentant cette
interface est requise par la méthode drawImage () de la classe Graphics, les méthodes prepareImage ()
et checkImage () des classes Component et Toolkit et les méthodes getWidth () et getHeight () de la
classe Image. Cette interface est notamment implémentée par la classe Component pour mettre à jour un
composant contenant une image au fur et à mesure que celle-ci est chargée.

Variables

Le paramètre infoflags de la méthode imageUpdate () utilise une combinaison des constantes suivantes
pour indiquer quelles caractéristiques d'une image sont connues au moment du chargement d'une image :

public final static int WIDTH


public final static int HEIGHT

La largeur ou la hauteur de l'image sont disponibles.

public final static int PROPERTIES

Les propriétés de l'image sont disponibles.

public final static int SOMEBITS


public final static int FRAMEBITS

Une partie de l'image est chargée.

public final static int ALLBITS

Le chargement de l'image est terminé et toutes ses caractéristiques sont disponibles.

public final static int ERROR

Une erreur est survenue pendant le chargement de l'image. Combiné avec ABORT.

public final static int ABORT


vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 6

Le chargement de l'image a été annulé.

Méthodes

public boolean imageUpdate (Image image, int infoflags,


int x, int y,
int width, int height)

Cette méthode est appelée au cours du chargement d'une image pour indiquer quelles en sont les
caractéristiques connues. infoflags est une combinaison des constantes WIDTH, HEIGHT, PROPERTIES,
SOMEBITS, FRAMEBITS, ALLBITS, ERROR et ABORT. x, y, width et height sont significatives suivant la valeur
de infoflags.
imageUpdate () doit renvoyer true si elle a besoin d'être encore appelée pour lui communiquer les
phases suivantes du chargement d'une image. Généralement, false est renvoyé en cas d'erreur.

L'applet suivante utilise l'interface ImageObserver pour attendre la fin du chargement d'une image et
l'afficher :

Voici le programme Java correspondant (à copier dans un fichier dénommé ChargementImage.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

public class ChargementImage extends Applet implements ImageObserver


{
private Image image;
private boolean chargementTermine = false;

public void init ()


{
// Création d'une image et lancement de son chargement
image = getImage (getCodeBase (), "brookbr.jpg");
prepareImage (image, this);
}

public void paint (Graphics gc)


{
// Si le chargement de l'image est terminé, affichage de l'image
// sinon affichage d'une chaîne de caractères d'attente
if (chargementTermine)
gc.drawImage (image, 0, 0, this);
else
gc.drawString ("Chargement en cours...", 10, size ().height - 10);
}

// Méthode appelée pour communiquer les étapes du chargement de l'image


public boolean imageUpdate (Image image, int infoFlags,
int x, int y, int width, int height)
{
// Si le chargement est terminé, redessin de l'applet
if ((infoFlags & ALLBITS) != 0)
{
chargementTermine = true;
repaint ();
}
return (infoFlags & (ALLBITS | ABORT)) == 0;
}
}
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 7

A la différence de l'applet ImageSimple , l'image de cette applet n'est affichée qu'une fois que l'image est
entièrement chargée.

La création d'images

Comme le montre l'applet MultiImages , une image peut être créée grâce à la méthode createImage (int
width, int height) de la classe Component. Cette méthode crée une image vierge dans laquelle vous
pouvez dessiner grâce aux méthodes de dessin de la classe Graphics.
Il existe une deuxième version de la méthode createImage () disponible dans les classes Component et
Toolkit : createImage (ImageProducer producer). Le paramètre producer doit être d'une classe qui
implémente l'interface ImageProducer. Le package java.awt.image fournit deux classes qui implémentent
cette interface :

La classe MemoryImageSource permet de créer une image initialisée avec un tableau décrivant la
couleur de chacun des points d'une image.
La classe FilteredImageSource permet de créer une image qui est le résultat de l'application d'un
filtre sur une image existante.

La classe java.awt.image.MemoryImageSource

Cette classe qui implémente l'interface ImageProducer permet de créer une image à partir d'un tableau
décrivant la couleur de chacun des points (ou pixels) d'une image.

Constructeurs

public MemoryImageSource (int width, int height,


int pix [ ], int offset, int scan)
public MemoryImageSource (int width, int height,
int pix [ ], int offset, int scan,
Hashtable props)
public MemoryImageSource (int width, int height,
ColorModel cm,
int pix [ ], int offset, int scan)
public MemoryImageSource (int width, int height,
ColorModel cm,
int pix [ ], int offset, int scan,
Hashtable props)
public MemoryImageSource (int width, int height,
ColorModel cm,
byte pix [ ], int offset, int scan)
public MemoryImageSource (int width, int height,
ColorModel cm,
byte pix [ ], int offset, int scan,
Hashtable props)

Ces constructeurs permettent de créer une image de largeur width et de hauteur height à partir du
tableau pix [ ] de type byte ou int. pix [ ] décrit la couleur de chacun des points de l'image ligne par
ligne. offset permet de donner le premier point du tableau à utiliser et scan le nombre de pixels par
ligne dans le tableau pix [ ] au cas où cette valeur serait différente de width. cm permet de spécifier un
modèle de couleur (par défaut égal au modèle RGB par défaut), et props décrit éventuellement les
propriétés associées à l'image.

Méthodes

public synchronized void addConsumer (ImageConsumer ic)


public synchronized boolean isConsumer (ImageConsumer ic)
public synchronized void removeConsumer (ImageConsumer ic)
public void startProduction (ImageConsumer ic)
public void requestTopDownLeftRightResend (ImageConsumer ic)

Implémentation des méthodes de l'interface ImageProducer.

L'applet suivante, comme l'applet MultiImages , crée un nuancier en rouge et vert mais cette fois-ci en
utilisant la classe MemoryImageSource :
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 8

Voici le programme Java correspondant (à copier dans un fichier dénommé ImageTableau.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

public class ImageTableau extends Applet


{
private Image image;

public void init ()


{
Dimension taille = size ();
int [ ] tableauPixels = new int [taille.width * taille.height];

// Remplissage du tableau avec un nuancier de rouge et vert


// les pixels sont décrits ligne par ligne par leur code RGB
for (int i = 0; i < taille.height; i++)
for (int j = 0; j < taille.width; j++)
tableauPixels [i * taille.width + j] =
255 << 24 // Transparence
| ((j * 255 / taille.width) << 16) // Rouge
| ((i * 255 / taille.height) << 8) // Vert
| 0; // Bleu

// Création d'une image à partir du tableau


image = createImage (new MemoryImageSource
(taille.width, taille.height,
tableauPixels, 0, taille.width));
}

public void paint (Graphics gc)


{
if (image != null)
gc.drawImage (image, 0, 0, this);
}
}

Remplir le tableau oblige ici à positionner les bits des composantes Rouge, Vert, Bleu d'une couleur. Quelle
est la principale différence entre cette applet et l'applet MultiImages ? A partir de mesures de temps réalisées
sur ces deux applets, la création d'une image de même dimension prend au moins 100 fois plus de temps par
l'applet MultiImages que sur l'applet ImageTableau ! Donc, si la création d'une image n'a pas besoin des
méthodes de dessin de la classe Graphics, n'hésitez pas à utiliser la classe MemoryImageSource.

Le modèle de couleur RGB par défaut implique que chaque couleur d'un point est codée sur un entier
32 bits dont les bits 16 à 23 représentent le Rouge, les bits 8 à 15 le Vert et les bits 0 à 7 le Bleu, ET
dont les bits 24 à 31 représentent le canal alpha. Cette dernière valeur correspond à la transparence (0
pour transparent à 255 pour opaque) : si vous oubliez de mettre une valeur pour ces bits dans le code
couleur, l'image sera du coup transparente et donc invisible !

Autre exemple

Applets ImageNoirEtBlanc .

La classe java.awt.image.ColorModel

Cette classe abstract est la super classe des modèles de couleurs. Un modèle de couleur décrit comment
retrouver la couleur d'un point (ou pixel) d'une image à partir de sa valeur entière. Le modèle de couleur RGB
par défaut est obtenu par la méthode static getRGBDefault (). Les classes DirectColorModel et
IndexColorModel qui dérivent de cette classe permettent d'utiliser des modèles de couleur différents, pour par
exemple décrire une image créée avec la classe MemoryImageSource.

Variable
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 9

protected int pixel_bits

Nombre de bits par pixel.

Constructeur

public ColorModel (int bits)

Méthodes

public static ColorModel getRGBdefault ()

Renvoie le modèle de couleur RGB par défaut utilisant 32 bits avec les bits 24 à 31 pour le canal alpha, les
bits 16 à 23 pour le Rouge, les bits 8 à 15 pour le Vert et les bits 0 à 7 pour le Bleu.

public int getPixelSize ()

Renvoie le nombre de bits utilisé par un modèle de couleur pour coder la couleur d'un pixel.

public abstract int getRed (int pixel)


public abstract int getGreen (int pixel)
public abstract int getBlue (int pixel)
public abstract int getAlpha (int pixel)

Ces méthodes doivent renvoyer les composantes Rouge, Vert, Bleu et Alpha (comprises entre 0 et 255) pour
une valeur pixel.

public int getRGB (int pixel)

La classe java.awt.image.DirectColorModel

Cette classe qui dérive de la classe ColorModel permet de définir un modèle de couleur différent du modèle
RGB par défaut, qui n'utilise pas forcément 32 bits pour coder la couleur d'un pixel. Ce modèle permet par
exemple de décrire comment coder une couleur sur 16 bits, utilisant les bits 11 à 15 pour le Rouge, les bits 6 à
10 pour le Vert et les bits 0 à 5 pour le Bleu (correspondant à la création d'une instance de la classe
DirectColorModel par l'instruction new DirectColorModel (16, 0xF800, 0x07C0, 0x003F)).

Constructeur

public DirectColorModel (int bits, int rmask, int gmask, int bmask)
public DirectColorModel (int bits, int rmask, int gmask,
int bmask, int amask)

Ces constructeurs permettent de créer un modèle de couleur sur nbits bits. rmask, gmask bmask et amask
décrivent les masques de bits utilisés pour coder les composantes Rouge, Vert, Bleu et Alpha. Les masques
doivent occupés les bits de poids le plus faible. Par défaut, les couleurs sont opaques.

Méthodes

public final int getRed (int pixel)


public final int getGreen (int pixel)
public final int getBlue (int pixel)
public final int getAlpha (int pixel)

Ces méthodes renvoient les composantes Rouge, Vert, Bleu ou Alpha (comprises entre 0 et 255) pour une
valeur de couleur pixel, utilisée avec ce modèle de couleur.

public final int getRGB (int pixel)

Renvoie la couleur RGB utilisant le modèle de couleur RGB par défaut pour une valeur pixel.

public final int getRedMask ()


public final int getGreenMask ()
public final int getBlueMask ()
public final int getAlphaMask ()
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 10

Renvoie masques de bits utilisés pour coder les composantes Rouge, Vert, Bleu ou Alpha de ce modèle de
couleur.

La classe java.awt.image.IndexColorModel

Cette classe qui dérive de la classe ColorModel permet de définir une palette de plusieurs couleurs. Chaque
couleur de cette palette est définie par ses composantes RGB (comprises entre 0 et 255) et Alpha
(transparence de 0 transparent à 255 opaque). Une image utilisant une palette de couleurs définit la couleur de
chacun de ses points par un entier égal à l'indice d'une couleur de cette palette (comme par exemple les
images au format GIF).

Constructeur

public IndexColorModel (int bits, int size,


byte r [ ], byte g [ ], byte b [ ])
public IndexColorModel (int bits, int size,
byte r [ ], byte g [ ],
byte b [ ], byte a [ ])
public IndexColorModel (int bits, int size,
byte r [ ], byte g [ ],
byte b [ ], int trans)

Ces constructeurs permettent de créer une palette de size couleurs. Les tableaux r [ ], g [ ], b [ ] et a [ ]
décrivent les composantes Rouge, Vert, Bleu et Alpha de chacune des couleurs de la palette. Par défaut,
chaque couleur est opaque. trans permet de spécifier l'indice de la couleur qui sera considérée comme
transparente (comme pour les images au format GIF). bits donne le nombre de bits utilisé par chaque pixel
(habituellement égal à log2 size arrondi à l'entier supérieur).

public IndexColorModel (int bits, int size,


byte cmap [ ], int start,
boolean hasalpha)
public IndexColorModel (int bits, int size,
byte cmap [ ], int start,
boolean hasalpha, int trans)

Ces constructeurs permettent de créer une palette de size couleurs. Le tableau cmap [ ] décrit dans l'ordre
les composantes Rouge, Vert, Bleu et Alpha (si hasalpha est égal à true) de chacune des couleurs de la
palette. start permet de spécifier l'indice du premier élément du tableau à utiliser. trans et bits sont utilisées
de la même manière que pour les constructeurs précédents.

Méthodes

public final int getMapSize ()

Renvoie la taille de la palette.

public final int getTransparentPixel ()

Renvoie l'indice de la couleur transparente ou -1 s'il n'est pas défini.

public final int getRed (int pixel)


public final int getGreen (int pixel)
public final int getBlue (int pixel)
public final int getAlpha (int pixel)

Ces méthodes renvoient les composantes Rouge, Vert, Bleu ou Alpha (comprises entre 0 et 255) à l'indice
pixel de la palette.

public final int getRGB (int pixel)

Renvoie la couleur RGB utilisant le modèle de couleur RGB par défaut à l'indice pixel de la palette.

public final void getReds (byte r [ ])


public final void getGreens (byte g [ ])
public final void getBlues (byte b [ ])
public final void getAlphas (byte a [ ])
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 11

Ces méthodes remplissent le tableau qui leur est passé en paramètre avec les composantes Rouge, Vert, Bleu
ou Alpha de chacune des couleurs de la palette.

L'applet suivante crée une image aléatoire en utilisant la classe MemoryImageSource, et une palette de couleurs
noir et blanc :

Voici le programme Java correspondant (à copier dans un fichier dénommé ImageNoirEtBlanc.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import java.util.Random;

public class ImageNoirEtBlanc extends Applet


{
private Image image;

public void init ()


{
byte [ ] noirEtBlanc = {0x00, 0x00, 0x00, // Noir
(byte)0xFF, (byte)0xFF, (byte)0xFF}; // Blanc
ColorModel paletteNoirEtBlanc =
new IndexColorModel (1, 2, noirEtBlanc, 0, false);

Dimension taille = size ();


byte [ ] tableauPixels = new byte [taille.width * taille.height];
Random generateur = new Random ();

// Remplissage aléatoire avec les valeurs 0 ou 1


for (int i = 0; i < tableauPixels.length; i++)
tableauPixels [i] = (byte)Math.abs (generateur.nextInt () % 2);

// Création d'une image à partir du tableau


image = createImage (new MemoryImageSource
(taille.width, taille.height,
paletteNoirEtBlanc,
tableauPixels, 0, taille.width));

public void paint (Graphics gc)


{
if (image != null)
gc.drawImage (image, 0, 0, this);
}
}

Une image créée avec la classe MemoryImageSource et une palette de couleurs de classe
IndexColorModel doit utiliser un tableau de type byte pour mémoriser la couleur des points de l'image
(ceci limite la palette à 256 couleurs).

La classe java.awt.image.PixelGrabber

Cette classe qui implémente l'interface ImageConsumer permet de récupérer les points d'une partie d'une image
dans un tableau. Le tableau est rempli avec la couleur de chacun de ces points en utilisant le modèle de
couleur RGB par défaut.

Constructeurs

public PixelGrabber (Image img, int x, int y,


int width, int height,
int pix [ ], int offset, int scansize)
public PixelGrabber (ImageProducer ip, int x, int y,
int width, int height,
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 12

int pix [ ], int offset, int scansize)

Ces constructeurs créent une instance de la classe PixelGrabber pour récupérer dans le tableau pix [ ] une
portion rectangulaire d'une image aux coordonnées (x,y), de largeur width et de hauteur height. Le tableau
est rempli à partir de l'indice offset, avec un nombre de scansize pixels par ligne.

Méthodes

public boolean grabPixels () throws InterruptedException


public synchronized boolean grabPixels (long ms)
throws InterruptedException

Ces méthodes démarre la récupération des points de l'image (éventuellement pendant ms millisecondes
maximum).

public synchronized int status ()

Renvoie le statut des points à récupérer. La valeur renvoyée est une combinaison des constantes déclarées
dans l'interface ImageObserver.

public void
setDimensions (int width, int height)
public void
setProperties (Hashtable props)
public void
setColorModel (ColorModel model)
public void
setHints (int hints)
public void
setPixels (int x, int y, int w, int h,
ColorModel model,
byte pixels [ ],
int off, int scansize)
public void setPixels (int x, int y, int w, int h,
ColorModel model,
int pixels [ ],
int off, int scansize)
public void imageComplete (int status)

Implémentation des méthodes de l'interface ImageConsumer.

Transformer des images avec un filtre

Java comporte le concept de filtres qui permettent de transformer une image en une autre. Ces filtres
dérivent de la classe ImageFilter, et permettent toute sorte de transformation. Le package
java.awt.image fournit deux classes de filtre dérivées de la classe ImageFilter, les classes
CropImageFilter qui permet d'extraire une partie d'une image, et RGBImageFilter qui permet de
transformer la couleur de chacun des points d'une image ; vous pouvez aussi imaginer toute sorte de
filtre.
Une image filtrée est créée grâce à la méthode createImage (ImageProducer producer) des classes
Component ou Toolkit, avec le paramètre producer égal à une une instance de la classe
FilteredImageSource.
Pour plus d'information sur le fonctionnement du filtrage d'images, voir Comment ça marche ?

La classe java.awt.image.FilteredImageSource

Cette classe qui implémente l'interface ImageProducer permet de créer une image filtrée. Le constructeur
de cette classe prend en paramètre une instance d'une classe implémentant l'interface ImageProducer
(obtenue par exemple grâce à la méthode getSource () de la classe Image) et une instance d'une classe
de filtre. Les applets NegatifImage , Compteur et AnimationFleche utilise cette classe pour créer des
images filtrées.

Constructeur

public FilteredImageSource (ImageProducer orig,


ImageFilter imgf)

Méthodes

public synchronized void addConsumer (ImageConsumer ic)


public synchronized boolean isConsumer (ImageConsumer ic)
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 13

public synchronized void removeConsumer (ImageConsumer ic)


public void startProduction (ImageConsumer ic)
public void requestTopDownLeftRightResend (ImageConsumer ic)

Implémentation des méthodes de l'interface ImageProducer.

La classe java.awt.image.ImageFilter

Cette classe qui implémente les interfaces ImageConsumer et Cloneable, est la super classe de toutes les
classes permettant de réaliser un filtrage. Cette classe n'a aucun effet sur l'image à filtrer (filtre nul). Le
package java.awt.image fournit les deux classes de filtre CropImageFilter et RGBImageFilter.

Variable

protected ImageConsumer consumer

Consommateur final d'images de l'image filtrée. Une fois modifiées, les données doivent être renvoyée à
ce consommateur.

Constructeur

public ImageFilter ()

Méthodes

public void
setDimensions (int width, int height)
public void
setProperties (Hashtable props)
public void
setColorModel (ColorModel model)
public void
setHints (int hints)
public void
setPixels (int x, int y, int w, int h,
ColorModel model,
byte pixels [ ],
int off, int scansize)
public void setPixels (int x, int y, int w, int h,
ColorModel model,
int pixels [ ],
int off, int scansize)
public void imageComplete (int status)

Implémentation des méthodes de l'interface ImageConsumer pour renvoyer l'image non modifiée à
consumer.

public ImageFilter getFilterInstance (ImageConsumer ic)


public void resendTopDownLeftRight (ImageProducer ip)
public Object clone ()

Exemple

Applet Compteur .

La classe java.awt.image.CropImageFilter

Cette classe qui dérive de la classe ImageFilter permet d'extraire une partie d'une image. Elle est
intéressante pour récupérer différentes images d'une image téléchargée. En effet, il est plus rapide de
charger un seul fichier et d'en extraire plusieurs images que de charger plusieurs images, car une seule
requête est nécessaire et la taille d'un fichier compressé comportant plusieurs images est plus petite que
la somme des tailles des fichiers compressés de ces images.

Constructeur

public CropImageFilter (int x, int y, int width, int height)

Construit un filtre permettant d'extraire une image aux coordonnées (x,y), de largeur width et de
hauteur height.

Méthodes
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 14

public void setProperties (Hashtable props)


public void setDimensions (int w, int h)
public void setPixels (int x, int y, int w, int h,
ColorModel model,
byte pixels [ ],
int off, int scansize)
public void setPixels (int x, int y, int w, int h,
ColorModel model,
int pixels [ ],
int off, int scansize)

Ces méthodes outrepassent celles de la classe ImageFilter pour réaliser les opérations du filtre.

Exemple

Applet AnimationFleche .

La classe java.awt.image.RGBImageFilter

Cette classe abstract qui dérive de la classe ImageFilter permet de créer des classes de filtres
modifiant la couleur des points d'une images. Il faut pour cela créer une classe dérivée de cette classe et
implémenter la méthode filterRGB (int x, int y, int rgb) pour qu'elle renvoie la nouvelle couleur
(modifiée ou non) du point de coordonnées (x,y). A la création de l'image filtrée avec la classe
FilteredImageSource, l'ensemble des points de l'image originale est énuméré à travers cette méthode
pour récupérer la couleur de chacun des points de la nouvelle image.

Variables

protected ColorModel origmodel


protected ColorModel newmodel

protected boolean canFilterIndexColorModel

Si le filtrage de la couleur ne dépend pas des coordonnées des points de l'image, il est conseillé de
mettre cette variable à true.

Constructeur

public RGBImageFilter ()

Méthodes

public abstract int filterRGB (int x, int y, int rgb)

Cette méthode doit être outrepassée par les classes dérivées pour renvoyer la couleur du point de
coordonnées (x,y) de l'image filtrée, sachant que la couleur de l'image originale est égale à rgb à ce
point.

public void filterRGBPixels (int x, int y, int w, int h,


int pixels [ ],
int off,
int scansize)

public void substituteColorModel (ColorModel oldcm,


ColorModel newcm)
public IndexColorModel filterIndexColorModel (IndexColorModel icm)

public void setColorModel (ColorModel model)


public void setPixels (int x, int y, int w, int h,
ColorModel model,
byte pixels [ ],
int off, int scansize)
public void setPixels (int x, int y, int w, int h,
ColorModel model,
int pixels [ ],
int off, int scansize)

Ces méthodes outrepassent celles de la classe ImageFilter pour réaliser les opérations du filtre.
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 15

Voici un exemple d'applet utilisant cette classe pour créer le négatif d'une image :

et le programme Java correspondant (à copier dans un fichier dénommé NegatifImage.java et invoqué à


partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

public class NegatifImage extends Applet


{
private Image image,
negatifImage;

public void init ()


{
// Création d'une image et de son négatif
image = getImage (getCodeBase (), "rockfel.jpg");
negatifImage = createImage (new FilteredImageSource
(image.getSource (),
new FiltreNegatif ()));
}

public void paint (Graphics gc)


{
if (image != null)
{
// Affichage des images
gc.drawImage (image, 0, 0, this);
gc.drawImage (negatifImage,
image.getWidth (this) + 10, 0, this);
}
}
}

// Classe FiltreNegatif transformant une couleur en son inverse


class FiltreNegatif extends RGBImageFilter
{
public FiltreNegatif ()
{
// La transformation des couleurs ne dépend pas
// des coordonnées des points de l'image
canFilterIndexColorModel = true;
}

public int filterRGB (int x, int y, int rgb)


{
int alpha = rgb & 0xFF000000;
// Transformation des composantes RGB en leur inverse
int rougeInverse = (rgb & 0xFF0000) ^ 0xFF0000;
int vertInverse = (rgb & 0x00FF00) ^ 0x00FF00;
int bleuInverse = (rgb & 0x0000FF) ^ 0x0000FF;
return alpha | rougeInverse | vertInverse | bleuInverse;
}
}

Comment ça marche ?

Les classes décrites précédemment implémentent soit l'interface ImageProducer (classes


MemoryImageSource et FilteredImageSource), soit l'interface ImageConsumer (classes PixelGrabber,
ImageFilter et les classes qui en dérivent).
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 16

Bien qu'il soit entièrement possible d'utiliser ces classes sans connaître ces interfaces, vous vous
demandez peut-être à quoi servent les interfaces ImageProducer et ImageConsumer et quel modèle elles
respectent.
Tout d'abord, ces deux interfaces ne vont pas l'une sans l'autre. C'est un peu comme pour vous,
consommateur d'images (ImageConsumer ), et votre magnétoscope, producteur d'images (ImageProducer ) :
vous mettez en marche le magnétoscope pour voir un film. Vous pouvez être plusieurs à voir un même
film et le magnétoscope n'a d'intérêt que si les images qu'ils diffusent sont vues. Souvenez-vous de cette
analogie, elle vous aidera à mieux comprendre comment fonctionne le modèle de gestion des images du
package java.awt.image.

Généralement en Java, un consommateur d'images est le système graphique de votre ordinateur qui
attend qu'on lui transmette les pixels à afficher à l'écran. Un producteur d'images est capable de créer
une image à partir d'un fichier GIF ou JPEG (la méthode getImage () de la classe Toolkit renvoie une
instance de la classe Image dont le producteur peut être obtenu grâce à la méthode getSource ()) ou à
partir d'une zone mémoire (via la classe MemoryImageSource).
Quand le consommateur d'images a besoin d'afficher une image, le producteur d'images est démarré en
appelant la méthode startProduction () qu'implémente le producteur. Ce dernier renvoie alors au
consommateur tous les renseignements (taille, modèle de couleur, couleur de chacun des pixels) qui lui
permettront de construire l'image, en appelant successivement les méthodes setDimensions (),
setColorModel (), setPixels () qu'implémente le consommateur.

figure 17. Génération d'une image

Quand toutes les données d'une image ont été transmises au consommateur ou en cas d'erreur, le
producteur appelle la méthode imageComplete () qu'implémente le consommateur.
Le producteur peut éventuellement délivrer l'image par morceaux en appelant plusieurs fois la méthode
setPixels (), ce qui permet au consommateur d'afficher l'image au fur et à mesure qu'elle est
disponible. Par exemple, c'est ce qui se produit à l'affichage d'une image provenant d'un fichier
téléchargé sur Internet : comme les données de l'image sont délivrées relativement lentement, on voit
l'image qui se dessine petit à petit comme dans l'exemple d'applet ImageSimple .
Le producteur peut être aussi capable de générer plusieurs images pour créer un animation. Dans ce cas,
il appelle imageComplete () à chaque fois qu'une image de l'animation (frame en anglais) a été
entièrement décrite.
Comme il est possible que plusieurs personnes regardent un même film, un producteur d'images peut
avoir plusieurs consommateurs qui lui demandent de leur envoyer les données d'une image.

En appliquant ce modèle de manière plus générale, il est possible d'imaginer toute sorte de classes de
producteur ou de consommateur d'images, du moment qu'ils implémentent les interfaces ImageProducer
ou ImageConsumer. Il est possible de créer par exemple une classe de consommateur d'images dont le but
est d'écrire une image dans un fichier respectant tel ou tel format : c'est ce schéma qu'utilise la
bibliothèque fournit par Acme, pour générer des sorties GIF ou JPEG.
La classe PixelGrabber est aussi une classe implémentant l'interface ImageConsumer : elle permet
d'interroger la valeur des pixels d'une image. En fait, elle stocke dans un tableau les valeurs transmises
par le producteur à l'appel de la méthode setPixels ().

Le filtrage d'images utilise aussi ce système, en intercalant des classes de producteur


(FilteredImageSource) et de consommateur (dérivant de la classe ImageFilter qui implémente
l'interface ImageConsumer) entre le producteur original et le consommateur final.
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 17

figure 18. Filtrage d'une image

Pour créer une image filtrée imageFiltree de classe FilteredImageSource, vous devez passer en
paramètre au constructeur de cette classe un producteur d'image et un filtre dont la classe FiltreImage
dérive de ImageFilter.
Quand le consommateur d'images va appeler la méthode startProduction () sur imageFiltree, l'objet
imageFiltree va créer une instance spéciale de filtre filtreImageIC qui mémorise le consommateur
final puis imageFiltree va à son tour appeler la méthode startProduction () sur le producteur original
en lui passant en paramètre le nouveau consommateur d'images filtreImageIC.
Quand le producteur original va produire l'image en appelant successivement les méthodes
setDimensions (), setPixels (),... ce sera donc sur le consommateur filtreImageIC. filtreImageIC va
rappeler ces méthodes sur le consommateur final avec des valeurs modifiées en fonction du filtre voulu.
Comme c'est filtreImageIC qui transmet toutes les données de l'image au consommateur final, il peut
créer tous les effets possibles, comme changer la taille ou les couleurs de l'image, voir même créer une
animation sur une image qui était statique à l'origine !

Voici un exemple d'applet qui affiche l'image d'un nombre aléatoire et utilise la classe de filtre
ImageFilterCounter dérivant de la classe ImageFilter. Cette classe permet de fabriquer l'image d'un
nombre donné à partir d'une image décrivant les 10 chiffres de 0 à 9, dans 10 zones rectangulaires de
taille égale. Globalement, quand le producteur appelle une des deux méthodes setPixels (), cette
classe mémorise les pixels qu'on lui transmet, puis une fois qu'elle a une image complète, elle retransmet
au consommateur final les images de chacun des chiffres du nombre à afficher.
Cette applet prend en paramètre le nom du fichier d'image contenant tous les chiffres. Ceci permet de
rendre le compteur sous différents aspects, comme le montrent les deux exemples suivants, l'un utilisant
une image JPEG, l'autre un GIF animé (l'animation d'un GIF n'est gérée qu'à partir de Java 1.1) :

Voici le programme Java correspondant (à copier dans un fichier dénommé Compteur.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

public class Compteur extends Applet


{
private Image image;

public void start ()


{
// Récupération de l'image originale dont le fichier
// est indiqué dans le paramètre de l'applet "image"
Image imageOriginale = getImage (getCodeBase (), getParameter ("image"));
// Création d'une image filtrée avec un nombre aléatoire
image = createImage (new FilteredImageSource
(imageOriginale.getSource (),
new ImageFilterCounter ((int)(Math.random () * 100000),
}

public void paint (Graphics gc)


vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 18

{
if (image != null)
// Affichage de l'image
gc.drawImage (image, 0, 0, this);
}
}

// Classe de filtre créant l'image d'un nombre à partir d'une


// image comportant tous les chiffres de 0 à 9
class ImageFilterCounter extends ImageFilter
{
private int valeur;
private int nbChiffres;

private int largeurImageOriginale;


private int hauteurImageOriginale;

private byte [] bytePixels;


private int [] intPixels;

private ColorModel model;

public ImageFilterCounter (int valeur,


int nbChiffres)
{
this.valeur = valeur;
this.nbChiffres = nbChiffres;
}

// Implémentation des méthodes de ImageConsumer


public void setDimensions (int width, int height)
{
largeurImageOriginale = width;
hauteurImageOriginale = height;

// Renvoie la dimension de l'image au consommateur final


consumer.setDimensions (nbChiffres * largeurImageOriginale / 10, height);
}

public void setHints (int hints)


{
// Positionnemennt de RANDOMPIXELORDER uniquement pour dire que
// les pixels dont renvoyés dans un ordre aléatoire
consumer.setHints ( ( hints
| RANDOMPIXELORDER)
& ~(TOPDOWNLEFTRIGHT | COMPLETESCANLINES));
}

public void setColorModel (ColorModel model)


{
this.model = model;
consumer.setColorModel (model);
}

public void setPixels (int x, int y, int width, int height,


ColorModel model, byte pixels [], int offset, int scansize)
{
if (bytePixels == null)
bytePixels = new byte [largeurImageOriginale * hauteurImageOriginale];
copyPixels (x, y, width, height, pixels, offset, scansize, bytePixels);
}

public void setPixels (int x, int y, int width, int height,


ColorModel model, int pixels[], int offset, int scansize)
{
if (intPixels == null)
intPixels = new int [largeurImageOriginale * hauteurImageOriginale];
copyPixels (x, y, width, height, pixels, offset, scansize, intPixels);
}

// Recopie la portion d'image chargee dans le tableau destPixels


private void copyPixels (int x, int y, int width, int height,
Object pixels, int offset, int scansize,
Object destPixels)
{
for (int i = 0; i < height; i++)
System.arraycopy (pixels, offset + (scansize * i),
destPixels, x + (y + i) * largeurImageOriginale, width);
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 19

public void imageComplete (int status)


{
if ( status == IMAGEERROR
|| status == IMAGEABORTED)
consumer.imageComplete(status);
else
{
int largeurChiffre = largeurImageOriginale / 10;
// Renvoie vers le consommateur final les chiffres du compteur un à un
for (int puissanceDix = nbChiffres - 1; puissanceDix >= 0; puissanceDix--)
{
// Recherche du chiffre à afficher
int chiffre = (valeur / (int)Math.pow (10, puissanceDix)) % 10;

if (bytePixels != null)
consumer.setPixels ((nbChiffres - puissanceDix - 1) * largeurChiffre, 0,
largeurChiffre, hauteurImageOriginale, model, bytePixels,
chiffre * largeurChiffre, largeurImageOriginale);
else
consumer.setPixels ((nbChiffres - puissanceDix - 1) * largeurChiffre, 0,
largeurChiffre, hauteurImageOriginale, model, intPixels,
chiffre * largeurChiffre, largeurImageOriginale);
}
consumer.imageComplete (status);
}
}
}

L'interface java.awt.image.ImageProducer

Cette interface est implémentée par les classes qui sont capables de produire des images. A l'appel de la
méthode startProduction (), la classe qui implémente cette interface doit commencer à produire une
image vers un consommateur, qui lui doit implémenter l'interface ImageConsumer. Ceci doit se traduire
par l'appel des méthodes de l'interface ImageConsumer pour transmettre au consommateur les
informations décrivant l'image.
Un producteur est capable de produire des images pour un ou plusieurs consommateurs. Les méthodes
addConsumer (), isConsumer () et removeConsumer () doivent être implémentées pour gérer cet
ensemble de consommateurs.

Méthodes

public synchronized void addConsumer (ImageConsumer ic)


public synchronized void removeConsumer (ImageConsumer ic)

Ces méthodes doivent ajouter ou retirer le consommateur d'images ic, de l'ensemble des consommateurs
enregistrés par ce producteur.

public synchronized boolean isConsumer (ImageConsumer ic)

Doit renvoyer true si ic appartient à l'ensemble des consommateurs d'images enregistrés par ce
producteur.

public void startProduction (ImageConsumer ic)

Cette méthode méthode doit enregistrer ic comme consommateur d'images, et commencer la production
de(s) image(s) en appelant les différentes méthodes de l'interface ImageConsumer sur chacun des
consommateurs d'images enregistrés.

public void requestTopDownLeftRightResend (ImageConsumer ic)

Cette méthode doit essayer de renvoyer les données de l'image vers le consommateur d'images ic avec
les pixels transmis de haut en bas et de gauche à droite, pour que le traitement des pixels par ce
consommateur soit de meilleure qualité. Par conséquent, le producteur doit appeler la méthode setHints
() de l'interface ImageConsumer avec comme paramètre TOPDOWNLEFTRIGHT.

L'interface java.awt.image.ImageConsumer
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 20

Cette interface est implémentée par les classes qui ont besoin des données d'une image. Un producteur
d'image, dont la classe doit implémenter l'interface ImageProducer, invoque chacune des différentes
méthodes de cette interface pour transmettre au consommateur d'images tous les renseignements
décrivant une image.

Variables

public final static int RANDOMPIXELORDER


public final static int TOPDOWNLEFTRIGHT
public final static int COMPLETESCANLINES

La méthode setHints () est appelée avec comme paramètre l'une de ces trois constantes combinée
éventuellement avec l'une des deux suivantes, pour transmettre au consommateur dans quel ordre seront
transmis les pixels de l'image pendant les appels successifs à la méthode setPixels (). Le producteur
peut envoyer ces pixels dans un ordre aléatoire, de haut en bas et de gauche à droite, ou par ligne entière
mais dans un ordre indéterminé.

public final static int SINGLEPASS

La méthode setHints () peut recevoir en paramètre cette constante, pour signifier au consommateur
que l'image sera générée par le producteur en une seule passe.

public final static int SINGLEFRAME

La méthode setHints () peut recevoir en paramètre cette constante, pour signifier au consommateur
qu'une seule image sera générée par le producteur. Dans le cas contraire, un ensemble d'images peuvent
être transmises au consommateur pour fabriquer une animation et chaque fois qu'une image est complète
imageComplete () est appelée avec SINGLEFRAMEDONE en paramètre ou SINGLEFRAMEDONE quand
l'animation est terminée.

public final static int IMAGEERROR


public final static int IMAGEABORTED
public final static int STATICIMAGEDONE
public final static int SINGLEFRAMEDONE

La méthode imageComplete () est appelée avec comme paramètre l'une de ces quatre constantes, pour
indiquer au consommateur si la génération de l'image a rencontré une erreur, si elle a été interrompue, si
l'image final est terminée ou si la génération d'une image d'un ensemble en comportant plusieurs est
complète.

Méthodes

public void setDimensions (int width, int height)

Cette méthode est appelée par le producteur d'images pour transmettre au consommateur la largeur
width et la hauteur height de l'image produite.

public void setProperties (Hashtable props)

Cette méthode est appelée par le producteur d'images pour transmettre au consommateur les propriétés
props de l'image.

public void setColorModel (ColorModel model)

Cette méthode est appelée par le producteur d'images pour transmettre au consommateur le modèle de
couleurs le plus courant utilisé pour décrire l'image.

public void setHints (int hints)

Cette méthode est appelée par le producteur d'images pour transmettre au consommateur les propriétés
hints de l'image. hints est une combinaison des constantes RANDOMPIXELORDER, TOPDOWNLEFTRIGHT ou
COMPLETESCANLINES, SINGLEPASS et SINGLEFRAME et permet au consommateur de préparer et d'optimiser
son environnement en fonction de la manière dont sera générée l'image.
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 21

public void setPixels (int x, int y, int width, int height,


ColorModel model,
byte pixels [ ],
int off, int scansize)
public void setPixels (int x, int y, int width, int height,
ColorModel model,
int pixels [ ],
int off, int scansize)

L'une de ces deux méthodes est appelée par le producteur d'images pour transmettre au consommateur
les valeurs des pixels de la portion d'image de taille width et height au point (x,y). La première méthode
reçoit les pixels dans un tableau de type byte, l'autre dans un tableau de type int. Ces pixels utilisent le
modèle de couleur model. Les données du tableau pixels sont à prendre en compte à partir de l'indice
offset, et comprennent un nombre de scansize pixels par ligne.

public void imageComplete (int status)

Cette méthode appelée par le producteur d'images doit être implémentée pour prendre en compte le
statut status de la génération d'images (égal à IMAGEERROR, IMAGEABORTED, STATICIMAGEDONE ou
SINGLEFRAMEDONE).

Exemple

Applet Compteur .

Gestion d'animations

L'utilisation des threads et des images permet de réaliser rapidement des animations en Java. Comme le
montrent les trois exemples suivant, le principe de programmation d'une animation est presque toujours
le même : Vous créez un thread dont la méthode run () utilise une boucle qui à chaque tour affiche une
nouvelle image puis arrête le thread courant pendant un laps de temps avec la méthode sleep () de la
classe Thread.
Bien que comme au cinéma, une animation sera en apparence bien fluide à 25 images par seconde
(équivalent à un laps de temps entre chaque image de 40 millesecondes), évitez un laps de temps de
rafraîchissement aussi court, car les machines ont souvent du mal à suivre.

Si comme dans certains des exemples qui suivent, vous utilisez la méthode repaint () pour mettre à
jour l'image d'une animation, le redessin du fond du composant effectué par la méthode update () de
la classe Component est inutile si vous devez transférer une image à l'écran occupant toute la surface du
composant.
N'oubliez pas alors d'outrepasser la méthode update () pour qu'elle appelle directement la méthode
paint () sans redessiner le fond de l'image. Ceci évitera un clignotement désagréable.

Enchaînement d'images téléchargées

Cette première applet utilise des images contenues dans un fichier téléchargé. Elles sont extraites de l'image
chargée grâce à l'utilisation de la classe de filtre CropImageFilter, puis un thread provoque leur affichage l'une
après l'autre à intervalle régulier pour donner l'effet animé :

Voici le programme Java correspondant (à copier dans un fichier dénommé AnimationFleche.java et


invoqué à partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

public class AnimationFleche extends Applet implements Runnable


{
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 22

private Thread threadAnimation = null;


private Image imagesAnimation [ ] = new Image [8];
private int imageCourante = 0;

public void init ()


{
try
{
// Création de l'image globale mémorisant 8 dessins de flèches
Image multiImages = getImage (getCodeBase (), "fleches.gif");
// Création d'un MediaTracker pour récupérer les 8 images
MediaTracker imageTracker = new MediaTracker (this);
for (int i = 0; i < imagesAnimation.length; i++)
{
// Chacune des 8 images est extraite de l'image principale
imagesAnimation [i]
= createImage (new FilteredImageSource
(multiImages.getSource (),
new CropImageFilter (i * 50, 0,
50, 50)));
imageTracker.addImage (imagesAnimation [i], 0);
}

imageTracker.waitForID (0);
// En cas d'erreur, déclenchement d'une exception
if (imageTracker.isErrorAny ())
throw new IllegalArgumentException ("Images non chargees");
}
catch (Exception e)
{ }
}

public void start ()


{
// Création et démarrage d'un thread d'animation
threadAnimation = new Thread (this);
threadAnimation.start ();
}

public void stop ()


{
threadAnimation.stop ();
}

public void run ()


{
while (threadAnimation.isAlive ())
try
{
// Redessin de l'applet et passage à l'image suivante
repaint ();
imageCourante = ++imageCourante % 8;
// Attente de 70 ms avant de passer à l'image suivante
Thread.sleep (70);
}
catch (InterruptedException exception)
{ }
}

// Méthode outrepassée pour qu'elle dessine directement l'image


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

public void paint (Graphics gc)


{
// Dessin de l'image courante
gc.drawImage (imagesAnimation [imageCourante], 0, 0, this);
}
}

Utilisation du double buffering

Cette applet montre l'intérêt d'utiliser le système du double buffering pour gérer l'animation d'une image
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 23

générée par un programme.


Le principe en est simple : au lieu de dessiner directement à l'écran un dessin qui évolue à chaque laps
de temps, vous utilisez une image dans laquelle vous dessinez puis que vous transférez à l'écran. Ceci
évite l'effet de clignotement d'une animation démontré par l'applet suivante, qui affiche un texte défilant
horizontalement :

Voici le programme Java correspondant (à copier dans un fichier dénommé ScrollText.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;

public class ScrollText extends Applet implements Runnable


{
private String texte;
private Thread threadAnimation;
private Dimension tailleApplet;
private int positionTexte;
private FontMetrics metrics;
private int largeurTexte;

public void start ()


{
// Mise en blanc du fond de l'applet
setBackground (Color.white);
tailleApplet = size ();
// Récupération du texte à afficher
texte = getParameter ("Texte");
positionTexte = tailleApplet.width;
// Création et démarrage du thread d'animation
threadAnimation = new Thread (this);
threadAnimation.start();
}

public void stop ()


{
threadAnimation.stop ();
}

public void run ()


{
try
{
while (threadAnimation.isAlive ())
{
// Redessin de l'applet et calcul d'une nouvelle position
repaint ();
if (positionTexte > -largeurTexte)
positionTexte -= tailleApplet.height / 2;
else
positionTexte = tailleApplet.width;
// Arrête le compteur pendant 2/10 de secondes (200 ms)
Thread.sleep (200);
}
}
catch (InterruptedException e)
{ }
}

public void paint (Graphics gc)


{
gc.setColor (Color.black);
// Création d'une police de caractères et récupération de sa taille
gc.setFont (new Font ("Helvetica", Font.BOLD, tailleApplet.height - 4));
if (metrics == null)
{
metrics = gc.getFontMetrics ();
largeurTexte = metrics.stringWidth (texte);
}

// Utilisation d'un rectangle de clipping


vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 24

// pour créer une bordure au bord de l'applet


gc.clipRect (2, 0, tailleApplet.width - 4, tailleApplet.height);
// Dessin du texte
gc.drawString (texte, positionTexte + 2,
tailleApplet.height - metrics.getDescent () - 2);
}
}

Si vous consultez le source de ce fichier HTML , vous pourrez voir que le paramètre Texte de cette
applet permet de modifier le texte affichée :

<APPLET CODE="ScrollText" CODEBASE="../classes"


ALT="ScrollText" WIDTH=250 HEIGHT=40 ALIGN=middle>
<PARAM NAME="Texte" VALUE="Texte d&eacute;filant horizontalement...">
</APPLET>

Maintenant, voici la même applet modifiée pour éviter l'effet de clignotement :

La modification apportée porte sur la manière de programmer la méthode paint () de l'applet ScrollText
:

// ...

public class ScrollText extends Applet implements Runnable


{
// start (), stop () et run () : même code que précédemment

private Image imageTexte;

public void paint (Graphics gc)


{
// Création d'une image de la taille de l'applet
if (imageTexte == null)
imageTexte = createImage (tailleApplet.width, tailleApplet.height);
Graphics gcImage = imageTexte.getGraphics ();

// Même dessin mais cette fois-ci dans l'image


gcImage.setColor (Color.white);
gcImage.fillRect (0, 0, tailleApplet.width, tailleApplet.height);

gcImage.setColor (Color.black);
gcImage.setFont (new Font ("Helvetica", Font.BOLD, tailleApplet.height - 4));
if (metrics == null)
{
metrics = gcImage.getFontMetrics ();
largeurTexte = metrics.stringWidth (texte);
}
gcImage.clipRect (2, 0, tailleApplet.width - 4, tailleApplet.height);
gcImage.drawString (texte, positionTexte + 2,
tailleApplet.height - metrics.getDescent () - 2);

// Dessin de l'image à l'écran


gc.drawImage (imageTexte, 0, 0, this);
}

// Méthode outrepassée pour éviter de dessiner le fond


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

Horloge avec image de fond

Cette applet est aussi un exemple d'utilisation du système du double buffering et montre la possibilité de
dessiner par dessus une image chargée à partir d'un fichier. Au début du programme, le fond de l'horloge
est téléchargé, puis toutes les secondes une nouvelle image est mise à jour en copiant ce fond dans le
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 25

double buffer puis en dessinant les aiguilles par dessus à leur nouvelle position :

Voici le programme Java correspondant (à copier dans un fichier dénommé Horloge.java et invoqué à
partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import java.util.Date;

public class Horloge extends Applet implements Runnable


{
private Thread threadHorloge;
private Image fondHorloge;

public void init ()


{
try
{
// Chargement du fond de l'horloge avec un MediaTracker
fondHorloge = getImage (getCodeBase (), "starclock.gif");
MediaTracker imageTracker = new MediaTracker (this);
imageTracker.addImage (fondHorloge, 0);
imageTracker.waitForID (0);

if (imageTracker.isErrorAny ())
fondHorloge = null;
}
catch (Exception e)
{ }
}

public void start ()


{
// Lancement d'un thread pour mettre à jour l'horloge
threadHorloge = new Thread (this);
threadHorloge.start ();
}

public void stop ()


{
threadHorloge.stop ();
}

public void run ()


{
try
{
while (threadHorloge.isAlive ())
{
// Récupération du temps courant pour calculer
// le durée d'arrêt du thread en fonction du temps
// d'exécution de la méthode afficher ()
long tempsCourant = System.currentTimeMillis ();
afficher (new Date (tempsCourant));
long dureeArret = 1000 - ( System.currentTimeMillis ()
- tempsCourant);
if (dureeArret > 0)
Thread.sleep (dureeArret);
}
}
catch (InterruptedException exception)
{ }
}

private Image horloge;

private void afficher (Date tempsCourant)


vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 26

{
// Création d'une image aux dimensions de l'applet
Dimension taille = size ();
if (horloge == null)
horloge = createImage (taille.width, taille.height);

// Récupération des heures, minutes et secondes


int heures = tempsCourant.getHours ();
int minutes = tempsCourant.getMinutes ();
int secondes = tempsCourant.getSeconds ();

// Pour chaque aiguille, conversion du temps en radian


// et récupération du polygone utilisant cet angle
double angleHeure = (180 - heures * 60 - minutes) * Math.PI / 360;
Polygon pointsHeure
= calculerAiguille (angleHeure,
taille.width / 4.,
taille.height / 4.);
double angleMinute = (900 - minutes * 60 - secondes) * Math.PI / 1800;
Polygon pointsMinute
= calculerAiguille (angleMinute,
3. * taille.width / 8.,
3. * taille.height / 8.);

double angleSeconde = (15 - secondes) * Math.PI / 30;


Polygon pointsSeconde
= calculerAiguille (angleSeconde,
2. * taille.width / 5.,
2. * taille.height / 5.);

Graphics gcImage = horloge.getGraphics ();


if (fondHorloge != null)
// Dessin de l'image de fond d'horloge avec redimensionnement
gcImage.drawImage (fondHorloge, 0, 0,
taille.width, taille.height, this);
else
{
// Si l'image de fond d'horloge est absente
// remplissage avec une couleur et dessin d'un cercle
gcImage.setColor (new Color (0x638494));
gcImage.fillRect (0, 0, taille.width, taille.height);
gcImage.setColor (Color.white);
gcImage.drawOval (0, 0, taille.width - 1, taille.height - 1);
}
// Translation au centre de l'horloge
gcImage.translate (taille.width / 2, taille.height / 2);
// Dessin des aiguilles (les secondes utilisent une ligne)
gcImage.setColor (Color.white);
gcImage.drawPolygon (pointsHeure);
gcImage.drawPolygon (pointsMinute);
gcImage.drawLine (pointsSeconde.xpoints [0],
pointsSeconde.ypoints [0],
pointsSeconde.xpoints [2],
pointsSeconde.ypoints [2]);
// Mise à jour à l'écran
repaint ();
}

// Dessin d'une aiguille à 3 heures (0 rad)


private int [] xAiguille = {-15, 33, 100, 33, -15};
private int [] yAiguille = { 0, 10, 0, -10, 0};
private Polygon pointsAiguille =
new Polygon (xAiguille, yAiguille, xAiguille.length);

private Polygon calculerAiguille (double angle,


double width,
double height)
{
Polygon points = new Polygon ();
// Calcul de la rotation de chaque point de l'aiguille
for (int i = 0; i < pointsAiguille.npoints; i++)
points.addPoint ((int)( ( pointsAiguille.xpoints [i]
* Math.cos (angle)
+ pointsAiguille.ypoints [i]
* Math.sin (angle))
* width / 100),
(int)( ( pointsAiguille.xpoints [i]
* -Math.sin (angle)
vendredi 11 février 2000 Du C/C++ à Java : Les images Page: 27

+ pointsAiguille.ypoints [i]
* Math.cos (angle))
* height / 100));
return points;
}

public void update (Graphics gc)


{
paint (gc);
}

public void paint (Graphics gc)


{
// Dessin de l'image à l'écran
if (horloge != null)
gc.drawImage (horloge, 0, 0, this);
}
}

Cette applet pourrait être améliorée en lui permettant de récupérer en paramètre l'image de fond, le choix
d'afficher les secondes ou non, l'ajout d'effets sonores toutes les secondes ou toutes les heures en
utilisant l'interface AudioClip,...
vendredi 11 février 2000 Du C/C++ à Java : Plus loin avec Java... Page: 1

Plus loin avec Java...

Les évolutions du langage


Conclusion

Les évolutions du langage

Le langage Java évolue rapidement : après les premières versions 1.0 de test sorties fin 1995, la version
1.1 est apparue au printemps 1997 et la version 2 (anciennement 1.2) en décembre 1998 (disponible pour
Windows, Solaris et Linux chez Javasoft).
Ce paragraphe vous présente les principales évolutions apportées à Java au cours de ses versions : ces
développements ont surtout enrichis la bibliothèque de classes Java mais certains portent aussi sur le
noyau du langage lui-même.

Classes internes (inner classes ) : ce type de classes a accès à toutes les variables de la classe
dans laquelle elles sont définies.
Les fichiers de classes, d'images, de sons,... dont peut avoir besoin un programme Java,
peuvent être rassemblés dans un seul fichier au format JAR (Java ARchvive). Ceci permet
de charger une applet en seule requête HTTP et de réduire la taille des fichiers car ce
format reprend le format de compression ZIP. Le chargement des applets est ainsi
accéléré.
Un nouveau système de gestion des événements utilisant un modèle de délégation des
événements.
JavaBeansTM : ce nouveau package permet de créer des classes de composants réutilisables
et que l'on peut facilement assembler ensemble dans les environnements de
développement rapide (RAD), comme le permettent d'autres produits comme ActiveX de
Microsoft.
Internationalisation : gestion de ressources pour permettre de gérer les différentes versions
d'un programme suivant la langue de l'utilisateur.
La possibilité d'enregistrer les objets (sérialisation) et de les recréer à partir d'une
sauvegarde.
Remote Method Invocation : ces nouveaux packages permettent d'invoquer des méthodes à
distance sur des objets fonctionnant sur d'autres Machines Virtuelles Java et
éventuellement même sur un autre ordinateur d'un réseau. Ceci permet de mettre en
pratique la programmation objet distribué.
JDBCTM (Java DataBase Connectivity) : ce package permet d'accéder à des bases de
données via des requêtes SQL.
...
JFC (Java Foundation Classes) : cette ensemble de classes vient enrichir de manière
significative la gestion de l'interface graphique actuelle AWT. Il se décompose en
différents types de fonctionnalités :
Swing : ces nouvelles classes définissent un nouvel ensemble de composants
reprenant les composants classiques (bouton, liste,...) enrichi de composants plus
complexes (arbre, tableau,...).
Java 2D : ces nouvelles classes du package java.awt permettent d'effectuer des
opérations plus riches de dessins : définitions et compositions de formes,
transformations, antialiasing, gestion plus riche des polices de caractères...
Drag and drop (glisser-déposer pour les puristes) : gestion du transfert de données
entre applications Java et applications natives du système.
Collections de données : de nouvelles classes sont ajoutées au package java.util,
permettant de gérer des listes chaînées, des ensembles,... et d'effectuer des opérations de
tris, de recherche,...
Intégration de CORBA (Common Object Request Broker Architecture) dans Java.
Améliorations diverses des classes existantes et des performances.
...
vendredi 11 février 2000 Du C/C++ à Java : Plus loin avec Java... Page: 2

A ces versions de base, s'ajoutent des bibliothèques d'extensions développées par Javasoft, qui
fonctionnent soit avec Java 1.1, soit avec Java 2 :

Swing (compatible Java 1.1) : cette bibliothèque intégrée dans Java 2, est aussi utilisable avec Java
1.1.
Servlets (compatible Java 1.1) : les servlets permettent de réaliser des programmes CGI (Common
Gateway Interface ) en Java sur un serveur Internet. Ce type de programme permet par exemple de
programmer un compteur de page ou un moteur de recherche.
JavaMail (compatible Java 1.1) : cette bibliothèque permet de gérer une boite email (envoi/lecture
de message sur un serveur de mail).
Java 3D (compatible Java 2) : cette bibliothèque permet de programmer de réaliser des scènes
virtuelles en 3D, et permet de gérer les éclairages, les textures, les animations d'objets 3D.
...

Ce manuel présente la version 2 du noyau du langage Java et la version 1.0 de la bibliothèque Java (qui
permet de faire déjà beaucoup de choses), et sera appelé à présenter plus dans le détail les évolutions
décrites précédemment.

Conclusion

Un des principaux atouts de Java est sa portabilité résumée dans la phrase Write once, run anywhere TM
(Ecrivez une fois, faites tourner n'importe où). A l'usage, vous verrez que certaines des premières
versions des navigateurs ne permettent pas toujours de faire tourner vos applets de la manière attendue.
Les principaux problèmes surviennent surtout pour les méthodes des interfaces du package java.applet
qui sont justement implémentées par chaque navigateur... Si vous voulez utiliser ces fonctionnalités,
testez si possible vos applets sur différents navigateurs pour vérifier leur bon fonctionnement.

Certaines personnes reprocheront aussi au système d'interface graphique AWT de Java de n'être que le
plus petit dénominateur commun entre toutes les fonctionnalités fournies par les différents systèmes
existants (MacOS, Windows, X11/Motif,...). Bien que ceci soit plutôt vrai, le package java.awt a le
mérite d'exister et de satisfaire les besoins de la plupart des applets et applications.
Mais la nouvelle bibliothèque Swing qui est utilisable avec Java 1.1 et/ou Java 2 a considérablement
enrichi les possibilités de gestion de l'interface graphique tout en offrant un modèle de conception très
ouvert.

Personnellement, je trouve que Java est un langage très agréable et se maîtrisant très rapidement. Pour
vous donner un ordre d'idée, j'ai appris le langage en une cinquantaine d'heures (en lisant deux
ouvrages), et j'ai réalisé ensuite en une quarantaine d'heures une première version du démineur Java Mine
(très proche de la version finale fournie sur le site http://www.eteks.com).

A ce jour, je n'ai trouvé que peu de personnes critiques sur ce langage (et encore que sur des points de
détails à mon avis négligeables tels que les assertions, l'absence de template, l'utilisation de paramètres
constants,...). C'est peut-être bien là que réside la véritable explication de son succès présent et futur :
après tout un langage n'est qu'un outil du développeur, et si celui-ci est satisfait par son utilisation, l'outil
en question a de bonnes chances de succès...

J'espère que ces pages auront pu rassasier vos esprits curieux, et vous auront convaincu.

Pour terminer, je ne ferai pas de citation (je suis plutôt mauvais dans ce domaine) mais je tiens à
remercier toutes les personnes de mon entourage qui m'ont soutenu dans ce travail de longue haleine, ne
serait-ce que par leur curiosité... et particulièrement Diem My, mon petit Thomas et Sophie la petite
dernière.

Si vous avez encore un peu de temps à consacrer à ce manuel, MERCI de donner votre avis grâce au
formulaire accompagnant le site http://www.eteks.com.
vendredi 11 février 2000 Historique du site Page: 1

Historique

11 Février 2000
Version 1.1.1 :
Corrections diverses de fautes de frappe et d'erreurs de texte.
07 Juillet 1999
Version 1.1 :
Description complète des ajouts au noyau du langage Java dans Java 1.1 (classes internes,...). Ces descriptions
sont faites essentiellement dans un nouveau chapitre, mais aussi dans les autres chapitres et notamment ceux
sur la création des classes et les tableaux.
Description du fonctionnement des classes ImageProducer et ImageConsumer.
Ajout de l'applet Compteur , utilisant un filtre d'image.
Revue de la plupart du texte.
Corrections de fautes de frappe et d'erreurs de texte.
10 Février 1999
Version 1.0.1 :
Pour permettre d'ajouter des fonctionnalités de Java 1.1 et Java 2, l'architecture du cours a changé.
Après avoir pris en compte l'avis des internautes qui ont répondu au sondage du mois de janvier 99, la solution
retenue est celle-ci (75% des personnes qui ont répondu) :
Le manuel Java 1.0 reste tel quel, et une nouvelle branche Java 2 commencera "en parallèle" à partir de la
description de la bibliothèque (chapitre sur la bibliothèque Java 1.0 et suivants). La seule partie commune
restera la description du noyau du langage. Quand cela s'avérera intéressant, des liens entre les parties Java 1.0
et Java 2 seront ajoutés.
Pour faciliter l'évolution vers cette architecture, tous les chapitrexx.html ont été renommés pour ne plus être
lié à un numéro de chapitre (désolé pour ceux qui avaient créé un lien vers ces chapitres), le package
java.lang a son propre chapitre, les conventions d'écriture et le portage de programmes C/C++ ont été
rassemblés dans le chapitre qui conclut la description du noyau du langage.
Au passage, toutes les ancres utilisées par les liens du site http://www.eteks.com (ce qui suit le symbole #
dans un tag <A HREF="file#anchor">), ont été vérifiées et éventuellement corrigées.
Corrections de fautes de frappe.
17 Décembre 1998
Version 1.0 :
Début décembre, JavaTeks est devenu eTeks et est hébergé sur son propre serveur http://www.eteks.com.
Tout Java 1.0 (langage et classes) est décrit. Description complète du package java.net.
Ajout des programmes client serveur d'Echo et du Paper board Internet .
Ajout d'une description détaillée de la procédure de compilation de l'applet HelloWorld , pour les débutants en
programmation.
Corrections de fautes de frappe et d'erreurs de texte.
31 Octobre 1998
Version 0.9.4 :
Ajout des applets CalculetteSimple et MessageBoxApplet .
Description partielle du package java.net (Accès via une URL).
Corrections de fautes de frappe et d'erreurs de texte (merci à Arcmap).
24 Septembre 1998
Version 0.9.3 :
Nouveau look "bleu métal" du site : Nouveaux boutons de navigation, mise en place de frames avec un menu
pour améliorer la navigation sur le site et alléger la page d'accueil.
Ajout d'une FAQ, d'un livre d'or et d'une liste de liens utiles plus complète.
Description complète du package java.io.
Description plus détaillée des classes du package java.lang.
Ajout d'un paragraphe "exemples" à chacune des classes quand c'est possible. Chacun de ces paragraphes
contient des liens vers les applets ou les applications du manuel où la classe est utilisée.
Découpage en deux du fichier HTML sur l'accès aux fichiers et au réseau (io10.html ), décalage des pages qui
suivent.
vendredi 11 février 2000 Historique du site Page: 2

Corrections de fautes de frappe et d'erreurs de texte.


31 Mai 1998
Version 0.9.2 :
Ajout d'une table des matières complète.
Explication plus détaillée des constructeurs, des méthodes outrepassées, des classes abstract, avec un nouvel
exemple d'application.
Description partielle du package java.io (Principes généraux et manipulation des fichiers).
Ajout d'un paragraphe sur le remplacement de l'héritage multiple du C++ en Java.
Modification de l'applet AfficheurDeCalcul .
Ajout de l'applet d'animation Horloge .
Corrections de fautes de frappe et d'erreurs de texte.
15 Mai 1998
Version 0.9.1 :
Ajout de l'applet ImageNoirEtBlanc .
Ajout des captures d'écrans des applets pour permettre de visualiser une image à la place de chaque applet si
le navigateur n'utilise pas Java.
Corrections de fautes de frappe et d'erreurs de texte.
08 Mai 1998
Ajout de balises <META ...> pour les moteurs de recherche et d'un formulaire de réponse sur le site.
06 Mai 1998
Version 0.9 : Première version disponible sur Internet.
Après un an de travail et de longues nuits souvent blanches, voilà la version 0.9 de ce manuel : 0.9 comme
90% du boulot réalisé. Reste à décrire plus en détail les packages java.io et java.net.

You might also like