CJava
CJava
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.
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.
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 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 1
Le langage Java
La bibliothèque Java 1.0
Les extensions Java
Copyrights
Avant propos
Le langage Java
Démarrer en Java
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
La classe Object
La classe Class
Les tableaux
Les chaînes de caractères
La classe String
La classe StringBuffer
La classe System
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
Les threads
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 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
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
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
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
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 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 é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
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
Démarrer en Java 3D
Prérequis
Téléchargement
Installation
Architecture
Un premier exemple
Applet Applet3D
Principes 3D
Construction d'un univers 3D
Repère 3D
Transformation 3D
Applet CubeFaces
Arbre d'une scène 3D
Applet MultiCubes
Optimisations Java 3D
Compilation
Capacité d'un noeud
Les classes de bases
La classe javax.media.j3d.SceneGraphObject
La classe javax.media.j3d.Node
La classe javax.media.j3d.Leaf
La classe javax.media.j3d.Group
La classe javax.media.j3d.BranchGroup
La classe javax.media.j3d.TransformGroup
La classe javax.media.j3d.Transform3D
Les classes algébriques
La classe javax.vecmath.Tuple3f
La classe javax.vecmath.Point3f
La classe javax.vecmath.Vector3f
La classe javax.vecmath.Color3f
Objets 3D
La classe javax.media.j3d.GeometryArray
La classe com.sun.j3d.utils.geometry.GeometryInfo
La classe com.sun.j3d.utils.geometry.NormalGenerator
Construction d'une surface autour d'un axe central
Applet AxisShapeDemo
La classe javax.media.j3d.Text3D
Applet HelloWorld3D
La classe javax.media.j3d.Font3D
La classe javax.media.j3d.FontExtrusion
Les fonds d'écran
La classe javax.media.j3d.Background
La classe javax.media.j3d.Bounds
La classe javax.media.j3d.BoundingBox
La classe javax.media.j3d.BoundingSphere
La classe javax.media.j3d.BoundingLeaf
L'importation de scènes 3D
Applet ObjectFileDemo
L'interface com.sun.j3d.loaders.Loader
L'interface com.sun.j3d.loaders.Scene
Eclairage 3D
Activation
Les différentes sources lumineuses
Effets des sources lumineuses
Applet LightEffect
Couleur des facettes éclairées
Eclairage et texture
Applet LitPlane
Combinaison des sources lumineuses
Applet LitApplet3D
Les classes d'éclairage
La classe javax.media.j3d.Light
La classe javax.media.j3d.AmbientLight
La classe javax.media.j3d.DirectionalLight
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 10
La classe javax.media.j3d.PointLight
La classe javax.media.j3d.SpotLight
Animation 3D
Interaction
Comportement et stimulus
Applet MouseApplet3D
La classe javax.media.j3d.Behavior
La classe javax.media.j3d.WakeupCondition
La classe javax.media.j3d.WakeupCriterion
La classe com.sun.j3d.utils.behaviors.mouse.MouseBehavior
La classe com.sun.j3d.utils.behaviors.mouse.MouseRotate
La classe com.sun.j3d.utils.behaviors.mouse.MouseTranslate
La classe com.sun.j3d.utils.behaviors.mouse.MouseZoom
La classe com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior
Animation
Comportement d'animation et opérateur alpha
Applet TextTranslation
La classe javax.media.j3d.Interpolator
La classe javax.media.j3d.Alpha
Applet AlphaTest
La classe javax.media.j3d.PositionInterpolator
La classe javax.media.j3d.RotationInterpolator
Applet Clock3D
La classe javax.media.j3d.ScaleInterpolator
La classe javax.media.j3d.ColorInterpolator
La classe javax.media.j3d.TransparencyInterpolator
Un exemple complet : Du soleil à la lune
Applet SunEarthMoonMotion
Plus loin avec Java 3D...
Solutions alternatives
Nota : Les liens hypertexte des exemples d'applets ou d'applications de la table des matières
précédente désignent le programme source de ces exemples (quand celui-ci est disponible).
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
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 11
Filtrage en négatif
Compteur
Image animée
Défilement de texte
Horloge des étoiles
Applets/applications Java 3D
Applet3D
CubeFaces
MultiCubes
SimpleObjects
Pyramid
AxisShapeDemo
HelloWorld3D
ObjectFileDemo
Clown
SphereConstruction
WaterGlass
SimpleTexturedObjects
LightEffect
LitPlane
LitApplet3D
MouseApplet3D
TextTranslation
AlphaTest
Clock3D
SunEarthMoonMotion
Interpréteur de fonctions
Démineur Java
Historique du manuel
Avant propos
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 http://www.eteks.com.
Niveau : Initiés/Expérimentés
Des développements futurs traiteront plus en détail des autres ajouts effectués dans la
bibliothèque Java 1.1 et Java 2.
vendredi 13 octobre 2000 Du C/C++ a Java : Avant propos Page: 2
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 13 octobre 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 !"
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 d'exemples.
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 :
vendredi 13 octobre 2000 Du C/C++ à Java : Démarrer en Java Page: 2
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).
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 :
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.
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/.
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...
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 !
Principe de fonctionnement
vendredi 13 octobre 2000 Du C/C++ à Java : Démarrer en Java Page: 3
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 :
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".
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.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Démarrer en Java Page: 4
.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 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.
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.
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 .
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.
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 ().
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 :
appletviewer crée pour chaque balise APPLET, une fenêtre de taille (WIDTH,HEIGHT), où
vendredi 13 octobre 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.
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 !...
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;
<HTML>
<HEAD>
<TITLE> Un programme simple </TITLE>
</HEAD>
<BODY>
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 :
HelloWorld.java, jusqu'à ce qu'il n'y ait plus texte "Hello world !", comme le montre
d'erreur. l'applet ci-dessus.
Tapez finalement appletviewer C'est tout.
HelloWorld.html. Normalement une fenêtre Pour faire d'autres programmes, c'est toujours
s'ouvre affichant le texte "Hello world !", la même procédure : reprenez un programme
comme le montre l'applet ci-dessus. Java et créez un fichier HTML appelant
C'est tout. l'applet grâce à la balise <APPLET ...>,
Pour faire d'autres programmes, c'est compilez-le et exécutez-le avec Applet
toujours la même procédure : Reprenez un Viewer ou Applet Runner .
programme Java et créez un fichier HTML
appelant l'applet grâce à la balise <APPLET
...>, compilez-le avec javac et exécutez-le
avec appletviewer suivi du nom du fichier
HTML.
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 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 1
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 :
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 champs 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 comporte un ensemble de champs 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 :
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 champs sans méthodes ; elle peut aussi n'avoir que des
méthodes sans déclarer de champ.
vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 3
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, tous les
champs et méthodes hérités sont accessibles directement comme s'ils avaient été déclarés 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à 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 (champs, paramètres ou variables locales) 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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 4
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;
};
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 champs 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 liens définis dans le tableau indiquent les endroits où sont utilisés le mot-clé pour la première
fois.
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 liens définis dans le tableau désignent les endroits où il est traité de la disparition de ces
mots-clés.
Types de base
VALEUR PAR
TYPE DESCRIPTION
DEFAUT
byte Entier signé occupant 8 bits (valeurs de -128 à 127) 0
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 :
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).
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.
class NouvelleClasse2
{
// Corps de NouvelleClasse2
}
interface NouvelleInterface
{
// Corps de NouvelleInterface
}
// ...
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 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 7
class Classe1
{
/* Bloc inutilisé
int x = 1; // x sert à ...
*/
// ...
}
Les packages
import
Les classes fournies avec le Java Development Kit ou par d'autres sources sont rangées dans des
packages (ou paquets si vous préférez), comparables à des groupes rassemblant les classes par
thème. Dans un fichier .java , vous devez indiquer à quels packages appartiennent les classes que
vous utilisez. La clause import permet de spécifier ces packages pour chacune des classes ou pour
chaque groupe de classes. Ces clauses se placent en début de fichier avant la déclaration de la
première classe ou interface du fichier :
import est suivi soit directement du nom d'une classe, soit du nom d'un package, suivi lui-même du
nom d'une 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 connaît pas
encore, il cherchera notamment dans les packages suivis d'un astérisque si Classe1 existe.
La classe nomClasse peut correspondre soit à un fichier source nomClasse.java, soit à un fichier
compilé nomClasse.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 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
vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 8
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.
Quand vous voulez utiliser dans un fichier UneClasse.java des classes définies dans des
fichiers situés dans le même package que celui de 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 : La clause import java.lang.*; est
implicite à chaque compilation.
Quand vous écrivez le nom d'une classe en la précédant de son package complet à chaque
utilisation de celle-ci, par exemple en écrivant java.util.Date pour la classe Date du package
java.util.
Pour utiliser une classe nomClasse d'un package nomPackage, vous avez donc le choix entre ces trois
options :
En rassemblant les classes par groupes, les packages permettent d'organiser l'ensemble des classes
et d'éviter d'éventuels conflits sur les noms des classes. En effet, si deux classes appartenant à deux
packages distincts portent le même nom, il est toujours possible de les utiliser ensemble dans un
même fichier .java , en les différenciant avec leur nom du package grâce à la troisième option .
Par exemple, la bibliothèque de Java 1.0 déclare la classe List dans le package java.awt, qui
représente un composant graphique de liste. Dans Java 2, une autre classe List a été ajoutée mais
elle appartient au package java.util. Cette classe permet de traiter un ensemble d'objets organisé
sous forme de liste, et il est logique qu'elle porte ce nom. Si jamais vous avez besoin de ces deux
classes dans un même fichier .java , il faut les utiliser sous la forme java.awt.List et
java.util.List, ce qui permet de les différencier.
Comme les différents fournisseurs mettent à disposition leurs classes sous leurs propres packages,
vous pouvez ainsi utiliser n'importe laquelle de leurs classes et créer des classes utilisant des noms
existants dans vos propres packages, sans risque de conflits.
Les clauses import permettant d'énumérer la liste des packages auxquels appartiennent les classes
utilisées dans un fichier .java , évitent de répéter le nom du package d'une classe à chaque
utilisation de celle-ci. Mais il est tout à fait possible de ne spécifier aucun import et d'écrire
chaque classe avec son package.
vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 9
Définir un package
import permet d'importer n'importe quelle classe d'une bibliothèque, mais vous pouvez aussi créer
votre propre bibliothèque, pour y rassembler par exemple un groupe 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 nomPackage;
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;.
La figure symbolisant les contrôles d'accès à des classes représente aussi un exemple simple
d'utilisation des packages.
La plupart des exemples fournis dans ce manuel n'utilisent pas de package pour simplifier les
programmes.
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 1
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, champs, méthodes, paramètres,
variables locales,...) 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
// 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 champs, 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 champs, 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 champs communs à 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).
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 :
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 champs 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 ()
}
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 4
class ClasseAyantBesoinDeLaCouleurDominante
{
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
}
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.
Syntaxe
class Classe1
{
// Déclaration de champs
TypeChamp champ1;
TypeChamp champ2, champ3;
ModifieurDeChamp TypeChamp champ4;
TypeChamp champ5 = valeurOuExpression ; // Initialisation d'un champ
// ...
}
TypeChamp est soit un type de base, soit le nom d'une classe, soit le nom d'une interface. Dans les
deux derniers cas, le champ est une référence sur un objet.
Les tableaux sont cités ici en exemple de champ et sont traités dans la partie sur les tableaux.
Le contrôle d'accès à un champ est soit public, soit protected, soit private, soit par défaut, si
aucun de ces modifieurs n'est précisé, amical (friendly en anglais) : le champ 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 un champ. Comme vous pouvez le voir, l'utilisation d'un champ
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 (.).
L'initialisation d'un champ se comporte exactement comme l'affectation. Si le champ est static,
alors l'initialisation est effectuée au chargement de la classe.
Les champs non initialisés prennent obligatoirement une valeur par défaut (voir le tableau sur les
types de base). Si ce champ est une référence à un objet, la valeur par défaut est null.
Une classe Classe1 peut déclarer un champ 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 champs de classe ainsi que
les champs d'instance.
Les champs d'instance et de classe sont tous initialisés à une valeur par défaut. En C++, il est
obligatoire d'initialiser les champs de classe à part ; en Java, soit ces champs prennent une valeur
par défaut, soit ils sont initialisés à leur déclaration, soit ils sont initialisés dans un bloc
d'initialisation static.
L'accès aux champs 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 champs static, vous le faites
directement à la déclaration du champ, comme par exemple static int var = 2;.
Le contrôle d'accès aux champs (et aux méthodes) se fait pour chacune d'eux individuellement, et
pas en bloc comme en C++ (avec par exemple public :).
Les champs (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 champs (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.
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 7
Le contrôle d'accès par défaut de Java est très pratique car il donne accès à tous les champs 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 champs dont vous avez
besoin.
Donc, prenez l'habitude de préciser dès le départ les contrôles d'accès des champs 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 champs static.
Ils sont exécutés dans l'ordre de déclaration et peuvent ne manipuler que les champs static déclarés
avant le bloc.
class Classe1
{
// Déclaration de champs static
static int champ1 = 10;
static int champ2;
static
{ // Bloc static
champ2 = champ1 * 2;
}
// ...
}
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 champs static de ces classes sont uniques pour une Machine Virtuelle, et
partagés entre les différentes applets. Donc, attention aux effets de bord ! Si vous modifiez la
valeur d'un champ static dans une applet, il sera modifié pour toutes les applets amenées à
utiliser ce champ.
Ceci est à opposer au C, où les champs 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'un champ comptant le nombre d'instances créées
static int nombreInstances = 0;
// Déclaration d'un champ d'instance
int champ1;
{ // Bloc d'instance
champ1 = 10;
nombreInstances++;
}
// ...
}
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 ()
}
// ...
}
soit un type (de base, une classe ou une interface) : dans ce cas, la méthode doit utiliser
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.
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 champs.
Dans la plupart des cas, il est conseillé de ne rendre public que les méthodes et les constantes
(champs final static), dont a besoin l'utilisateur d'une classe. Les autres champs sont déclarés
private voir friendly ou protected et sont rendus 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 champs et de vérifier les conditions requises pour modifier
ka valeur d'un champ.
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)
{
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);
}
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 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 à un champ et à une méthode (à éviter
pour ne pas nuire à la lisibilité du programme).
Contrairement au C/C++, dans une classe Classe1, vous pouvez utiliser tous les champs et les
méthodes de Classe1 dans le corps de ses méthodes qu'ils soient déclarés 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
champs 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 champs 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.
L'appel aux méthodes static se fait grâce à l'opérateur point (.) et pas l'opérateur ::, comme en
C++.
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 11
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 un
champ 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).
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 ()
}
}
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 champ d'une classe peut être initialisé à une valeur par défaut à sa déclaration. Mais si vous
voulez initialiser certains de ces champs 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.
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 champs 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
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 12
{
// Déclaration du constructeur sans paramètre remplaçant le constructeur par défaut
public Classe1 ()
{
// 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 :
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
le champ var1 est initialisé 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 la valeur d'un
champ d'un objet après sa création ?
Un constructeur permet d'initialiser certains champs 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 tel ou tel champ 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 tous les champs d'un objet
seront correctement initialisés 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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 13
Le corps d'un constructeur peut éventuellement commencé par une des deux instructions suivantes :
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 var;
Classe1 (int val)
{
var = val;
}
}
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 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 14
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.
Un nouvel objet de classe Classe1 est créé, l'espace mémoire nécessaire pour les champs d'instance
est alloué, ces champs sont ensuite initialisés 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 champs 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 déclenchée en cas de mémoire insuffisante pour allouer
l'espace mémoire nécessaire au nouvel objet.
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 15
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 champs var1 et var2
public Classe1 (int valeur1,
int valeur2)
{
var1 = valeur1;
var2 = valeur2;
}
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; // Champ protected accessible
// par les classes dérivées
// Constructeur
Compte (int nouveauNumero, float sommeDeposee)
{
// Mise à jour des champs de la classe
numero = nouveauNumero;
soldeInitial = sommeDeposee;
}
int numeroCompte ()
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 16
{
return numero;
}
float calculerSolde ()
{
return soldeInitial;
}
}
// Constructeur
CompteDepot (int nouveauNumero)
{
// Appel du constructeur de la super classe
super (nouveauNumero, 0);
}
// Constructeur (tauxInteret en %)
CompteEpargne (int nouveauNumero, float depot, float tauxInteret)
{
super (nouveauNumero, depot);
tauxInteretPourcentage = tauxInteret;
}
// 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);
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 champs d'instance qui sont allouées à la création d'une nouvelle instance des classes
Compte, CompteDepot ou CompteEpargne, un champ caché qui représente la classe du nouvel objet lui
est ajouté. A l'appel compte.calculerSolde (), consulte ce champ 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 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 18
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 champs, comme le montre l'exemple suivant :
class Classe1
{
final static int cste1 = 0;
}
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
champs)... 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;
}
}
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)
}
}
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 un champ var1 qui
est une référence de classe classeAbstract. var1 peut être égal à 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
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 19
méthodes et donner leur 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 :
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
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 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 20
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) :
qu'elle implique que la notion de destructeur (méthode appelée à la destruction d'un objet en C++,
très souvent 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 :
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; }
}
{
ListeChainee eltPrecedent = liste;
liste = liste->suivant;
free (eltPrecedent);
}
}
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, le champ 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 locale 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 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 1
La classe Object
La classe Class
Les tableaux
Les chaînes de caractères
La classe String
La classe StringBuffer
La classe System
Ce chapitre présente les principales classes du package java.lang et comment utiliser les tableaux
en Java.
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 sont signalées par des liens et seront détaillées ultérieurement.
Constructeur
public Object ()
Méthodes
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.
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 champs.
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.
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 champs. 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
tous les champs de l'original.
Si un des champs recopiés 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 () :
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
().
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.
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 représente chacune des classes (et des interfaces) chargées par la Machine
Virtuelle Java, en faisant correspondre à chaque classe une instance de classe Class.
Méthodes
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 .
Renvoie le chargeur de classe utilisé pour charger une classe ou null si c'est le chargeur par défaut.
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).
Renvoie true ou false suivant que l'objet sur lequel cette méthode est appelée est une interface ou
une classe.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 4
!=.
A partir de Java 1.1, cette classe a été très enrichie et comporte des méthodes qui permettent
d'interroger tous les champs, 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 au champ length de ce
tableau (table.length) pour déterminer le nombre d'éléments mémorisés dans celui-ci.
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];
import java.lang.reflect.*;
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
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 5
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 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;
}
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
:
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 6
C char
D double
F float
I int
J long
L classe ou interface
S short
Z boolean
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 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}};
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 7
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 un champ 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.
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 :
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) :
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 8
Constructeurs
Construit une nouvelle chaîne initialisée avec la suite de caractères données dans le tableau value.
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
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 ()
Ces méthodes permettent de récupérer dans le tableau dst les caractères de la chaîne, de l'indice
srcBegin jusqu'à srcEnd (non compris).
Cette méthode outrepasse la méthode hashCode () de la classe Object, pour renvoyer un code
différent si deux chaînes de caractères sont différentes.
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.
Comme equals () mais la comparaison est faite sans tenir compte des majuscules/minuscules des
chaînes comparées.
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,
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 9
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).
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.
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).
Renvoie une chaîne où tous les caractères oldChar sont convertis en newChar.
Renvoie une chaîne où tous les caractères majuscules sont convertis en minuscules, et inversement.
Renvoie une chaîne où tous espaces en tête et en queue d'une chaîne de caractères sont supprimés.
Ces méthodes permettent de créer des chaînes de caractères à partir de tableaux de caractères.
Renvoie une chaîne représentant la valeur de obj sous forme de chaîne de caractères. En fait,
équivalent à obj.toString ().
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).
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 ()
Méthodes
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.
Permet de s'assurer, que la chaîne à une capacité d'au moins minimumCapacity caractères
supplémentaires.
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 newLength.
Renvoie le caractère mémorisé à l'indice index (valeur comprise entre 0 et length () - 1).
Copie à l'indice dstBegin du tableau dst les caractères compris entre les indices srcBegin et srcEnd.
Ajoute en fin de chaîne les caractères du tableau str, compris entre beginIndex et beginIndex +
length - 1.
Insère à l'indice offset d'une chaîne un objet, une chaîne ou un tableau de caractères.
Insère à l'indice offset d'une chaîne la valeur d'un type de base convertie en chaîne.
Méthode de la classe Object, outrepassée pour qu'elle renvoie la chaîne de caractères mémorisée.
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 champs et méthodes très utiles.
Tous les champs et les méthodes de cette classe sont static.
Les champs 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 ).
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 12
Champs
Méthodes
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.
Permet volontairement d'appeler la méthode finalize () des objets qui ne sont plus référencés.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 13
commande java .
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 :
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 ;
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 qu'un champ de Classe1. Dans ce cas, le champ variable1 de
Classe1 est caché par la variable locale variable1. Si vous voulez faire référence au champ de la
classe dans le bloc, vous pouvez utiliser l'expression Classe1.variable1, si variable1 est un champ
de classe (static), ou l'expression this.variable1 si variable1 est un champ d'instance.
L'utilisation de this est développée plus loin dans ce chapitre.
Toute variable locale 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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 2
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.
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'un champ 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 (expressionBooleenne )
instructionOuBlocSiVrai
if (expressionBooleenne )
instructionOuBlocSiVrai
else
instructionOuBlocSiFaux
switch (expressionEntiere )
{
case expressionConstante1 :
instructionOuBlocCas1
break; // ou return; ou rien pour passer au case suivant
vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 3
case expressionConstante2 :
instructionOuBlocCas2
break; // ou return; ou rien pour passer au case suivant
// ...
default :
instructionOuBlocParDefaut
break; // ou return;
}
Si les expressions expressionConstantei qui suivent chaque case ne sont pas constantes, vous
devez utiliser l'instruction if.
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 (expressionBooleenne )
instructionOuBloc
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 .
expressionInit est exécuté puis tant que expressionBooleenne est true, l'instruction ou le bloc
d'instruction instructionOuBloc puis expressionIncrementation sont exécutés.
{
expressionInit ;
while (expressionBooleenne )
{
instructionOuBloc ;
expressionIncrementation ;
vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 4
}
}
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 expressionBooleenne .
class Classe1
{
int [ ] factorielles (int max)
{
// Création d'un tableau de max éléments
int [ ] tableau = new int [max];
return tableau;
}
}
Comme pour if, l'expression déterminant la poursuite d'une boucle while ou for doit être du type
boolean.
for permet 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 :
Les expression suivantes peuvent être utilisées comme expression ou comme instruction, si elles
sont suivies d'un point virgule (;) :
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
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 champs ou variables locales final initialisées avec des expressions constantes.
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 13 octobre 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);
} }
}
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 champs et méthodes d'instance d'un objet. Ceci peut
être utile pour distinguer un champ 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;
En fait, à chaque fois que vous utilisez une méthode ou un champ 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 13 octobre 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 champs 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
&, ^, | 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 UneClasse
{
void methode ()
{
Classe0 objet1 = new Classe1 ();
Classe0 objet2 = new Classe0 ();
Object objet3 = new Classe0 ();
Object objet4 = null;
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.
OPERATEUR DESCRIPTION
Les liens définis dans le tableau désignent les endroits où il est traité de l'absence de ces opérateurs.
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.
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.
Ces conversions ne sont autorisées par le compilateur que si le nombre à convertir est converti
vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 10
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 champs 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
{ }
class UneClasse
{
void uneMethode ()
{
// Création d'un objet de chaque classe
ClasseX objetClasseX = new ClasseX ();
ClasseY objetClasseY = new ClasseY ();
ClasseZ objetClasseZ = new ClasseZ ();
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
{ }
class ClasseX
{ }
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
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 [ ]
La figure suivante résume les conversions les plus usuelles que vous serez amené à utiliser :
Les casts s'opèrent sur les références et non sur les objets. Dans les deux lignes suivantes :
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 13 octobre 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 champs de Classe1
// en ceux 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.
Les exceptions
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
try
BlocInstructionsTry
catch (ClasseException exceptionInterceptee)
BlocInstructions
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 qu'il faut toujours 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
vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 3
{
// ...
void methode1 () throws ClasseException
{
// ...
// En cas d'erreur, déclenchement d'une exception
throw new ClasseException ();
// ...
}
}
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 ?
}
}
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 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 4
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) :
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) :
Autres exemples
vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 6
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 champs, 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 ();
}
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 :
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 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 8
// ...
throw new Exception ();
}
}
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 Classe2
{
void methode (Classe1 objet1)
{
Classe1 objet2 = (Classe1)objet1.clone ();
// ...
}
}
La classe java.lang.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
Imprime sur la sortie standard ou sur un stream, l'exception et la trace de l'exception dans la pile.
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 ().
java.lang.ArithmeticException : Une exception est survenue sur une opération arithmétique, comme une
division d'un entier par zéro.
java.lang.ArrayStoreException : Tentative de stocker dans un tableau un élément qui n'est pas du type
des éléments du tableau ou castable dans ce type.
java.lang.ClassCastException : Tentative de cast d'un objet dans un type incorrecte.
java.lang.IllegalArgumentException : Une méthode a été appelée avec un mauvais argument ou
invoquée sur un mauvais objet. Les classes suivantes dérivent de cette classe d'exception :
java.lang.IllegalThreadStateException : Un thread était dans un état inadéquat pour l'opération
requise.
java.lang.NumberFormatException : Tentative de convertir dans un type numérique une chaîne de
caractères mal formattée.
java.lang.IllegalMonitorStateException : Le thread courant a tenté d'attendre ou de prévenir d'autres
threads, sur un objet non verrouillé par ce thread. Cette exception est déclenchée par les méthodes wait
() et notify () de la classe Object (voir aussi la synchronisation des threads).
java.lang.IndexOutOfBoundsException : Un indice (sur un tableau, une chaîne) ou un intervalle défini par
deux indices ont dépassé les limites inférieures ou supérieures. Les classes suivantes dérivent de cette
classe d'exception :
java.lang.ArrayIndexOutOfBoundsException pour les tableaux (indice négatif ou supérieur ou égal à
la taille du tableau).
java.lang.StringIndexOutOfBoundsException pour les chaînes de caractères.
java.lang.NegativeArraySizeException : Tentative de créer un tableau ou une chaîne avec une taille
négative.
java.lang.NullPointerException : Tentative d'utiliser une référence null alors qu'une référence sur un
objet valide était attendue.
java.lang.SecurityException : Tentative de violation de sécurité.
Le package java.util définit les exceptions suivantes signalant des opérations interdites :
java.util.EmptyStackException : Tentative d'accéder à un élément dans une pile vide (classe
vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 10
java.util.Stack).
java.util.NoSuchElementException : Cette exception est déclenchée par les implémentations de la
méthode nextElement () de l'interface java.util.Enumeration quand il n'y a plus d'éléments à énumérer.
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.
java.lang.LinkageError : Cette classe est la super classe des classes d'erreurs déclenchées quand le lien
vers une classe est impossible (classe ou méthode inexistante, fichier .class corrompu,...). Ces erreurs
surviennent la plupart du temps quand la Machine Virtuelle Java continue à utiliser un ancien fichier
.class d'une classe qui a changé. Dans ce cas, vérifiez les trois points suivants et recompilez la classe si
nécessaire :
1. Les dates du fichier .class et du fichier source .java .
2. Le répertoire de provenance du fichier .class en consultant le CLASSPATH utilisé.
3. L'unicité du fichier .class : peut-être avez-vous changé votre arborescence de développement en
laissant d'anciens fichiers .class utilisés par inadvertance ?
Thread.
java.lang.VirtualMachineError : Cette classe est la super classe des classes d'erreurs déclenchées quand
la Machine Virtuelle Java n'est plus en mesure de fonctionner.
java.lang.InternalError : Cette erreur est déclenchée suite à un état incohérent de la Machine
Virtuelle.
java.lang.OutOfMemoryError : La Machine Virtuelle n'a pas assez de mémoire pour instancier un
nouvel objet même après que le Garbage Collector soit intervenu.
java.lang.StackOverflowError : Un débordement de pile est survenu, par exemple suite à un appel
récursif dans une méthode sans condition d'arrêt.
java.lang.UnknownError : Cette erreur n'est pas utilisée.
java.awt.AWTError : Une erreur est survenue pendant l'utilisation d'AWT (le système de gestion de
l'interface utilisateur). Cette erreur est utilisée en cas d'erreur d'initialisation du toolkit AWT (pas de
DISPLAY sous X11 par exemple) ou de l'impression.
java.lang.ClassNotFoundException : Une classe ou une interface d'un certain nom n'a pas été trouvée.
Cette exception peut être déclenchée par les méthodes forName () de la classe Class, et les méthodes
findSystemClass () et loadClass () de la classe ClassLoader.
java.lang.CloneNotSupportedException : La méthode clone () de la classe Object a été appelée sur un
objet dont la classe n'implémente pas l'interface Cloneable.
java.lang.IllegalAccessException : Tentative de charger une classe dont le nom est correct, mais qui
n'est pas public, ou qui se trouve dans un package différent ou dont le constructeur par défaut n'est pas
accessible (s'il est private par exemple). Cette exception peut être déclenchée par la méthode forName ()
de la classe Class.
java.lang.InstantiationException : La classe spécifiée en paramètre de newInstance () est abstract, un
tableau ou une interface, ceci interdisant la création d'un nouvel objet.
java.lang.InterruptedException : Le thread courant était en attente et un autre thread a 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).
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 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 12
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 1
Les threads
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.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 2
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.
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 !
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 :
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 ().
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) :
import java.awt.*;
import java.applet.Applet;
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 () 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 !...
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 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 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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 6
import java.applet.Applet;
import java.awt.*;
this.applet = applet;
}
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...
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é.
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 8
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.
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 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 :
notifyAll ();
}
// ...
}
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.
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.
notifyAll ();
}
}
catch (InterruptedException e) { }
}
// ...
}
Ici, en modifiant le champ courbe avec null ou 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 le champ 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.
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 java.lang.Thread
Champs
Ces 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 ()
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éé.
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.
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 :
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).
run () est la méthode où sont décrites les instructions que doit exécuter un thread. Elle est appelée
une 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.
Arrête le thread sur lequel stop () est invoqué en déclenchant l'exception exception.
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 ().
Permet de suspendre le thread courant pour laisser la main à d'autres threads en attente d'exécution.
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.
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é.
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 12
Modifie ou renvoie la priorité d'un thread. newPriority doit avoir une valeur comprise entre
MIN_PRIORITY et MAX_PRIORITY.
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.
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.
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 ()).
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.
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.
Exemples
Application PaperBoardServer .
Applets Chrono , AfficheurDeCalcul , ObservateurCalcul , PaperBoardClient ,
AnimationFleche , ScrollText et Horloge .
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 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 ).
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 champs :
class ClasseExterne
{
// Déclararation d'une classe interne
ModifieurClasseInterne class ClasseInterne
{
// Corps de ClasseInterne :
// Déclaration des champs, des méthodes, des constructeurs,...
}
// Autres déclarations
}
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 champs 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 champs static, une classe interne static ou ses instances ne dépend
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 2
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 champs 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 ClasseExterne.
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.
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 champs 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
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 3
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 :
public class AfficheurDeCalcul extends Applet public class AfficheurDeCalcul extends Applet
{ {
private Thread calculateur; private Thread calculateur;
private Thread afficheur; private Thread afficheur;
// ... // ...
// 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
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é.
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 :
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 champs 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.*;
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.
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};
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 6
void methode2 ()
{
// 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.
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.
Les variables locales et les paramètres d'une méthode ou d'un constructeur peuvent être déclarés
final.
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, tous les champs 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 est impossible de
modifier param1 mais l'objet désigné par param1 lui peut être modifié.
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 1
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'un champ 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 champs 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 champs 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.
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 .
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 2
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.
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 à transposez toutes vos variables globales et vos
fonctions en champs 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 champs 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 .
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'un champ de
ce type, soit vous remplacez le nom de ce type par le type de base Java correspondant partout où il est
utilisé.
#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 champs 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.
#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 booléennes (champs static final boolean) permettent de réaliser un effet
comparable comme dans l'exemple suivant :
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 3
class Classe1
// Définit un symbole DEBUG {
#define DEBUG static final boolean DEBUG = true;
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.
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.
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 instance 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 :
#define TYPE_VALEUR 0
#define TYPE_OPERATION 1
expr [2].expr.valeur = 2;
expr [3].expr.valeur = 3;
expr [4].expr.valeur = 4;
// Constructeur
Nombre (double nombre)
{
this.nombre = nombre;
}
// Constructeur
Operation (char operateur, Calculable expr1, Calculable expr2)
{
this.operateur = operateur;
this.expr1 = expr1;
this.expr2 = expr2;
}
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 :
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 un
champ value de type double et passer une instance de DoubleValue à fonction2 (). Ainsi, si vous
modifiez la valeur du champ value dans fonction2 (), l'appelant de fonction2 () aura accès à ce
champ modifié. 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 13 octobre 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;
}
/* ... */ // ...
} }
}
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 un champ 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 13 octobre 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 !).
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.
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) :
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 13 octobre 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);
}
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 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 10
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;
}
}
}
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 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 11
ClasseDerivee *objet3 = new ClasseDerivee (); ClasseDerivee objet3 = new ClasseDerivee ();
objet3->methode1 (); objet3.methode1 ();
objet3->methode2 (); objet3.methode2 ();
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 ()).
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 13 octobre 2000 Du C/C++ à Java : La bibliothèque Java Page: 1
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.
package permettent de programmer une applet Java et d'intégrer une applet dans un navigateur.
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
vendredi 13 octobre 2000 Du C/C++ à Java : La bibliothèque Java Page: 3
Classe java.net.ContentHandler
Interface java.net.ContentHandlerFactory
Interface java.io.DataInput
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
vendredi 13 octobre 2000 Du C/C++ à Java : La bibliothèque Java Page: 4
Interface java.util.Observer
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
Classe java.io.FileNotFoundException
vendredi 13 octobre 2000 Du C/C++ à Java : La bibliothèque Java Page: 5
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
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.
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 à partir de la machine locale ou sur 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
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 2
Méthodes
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.
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 champs de la classe c.
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
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 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.
Champs
TRUE et FALSE sont deux constantes qui correspondent aux valeurs true et false du type boolean.
Constructeurs
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
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.
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.
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.
Champs
Ces constantes sont les bases de conversion minimum (= 2) et maximum (= 36) des nombres
entiers.
Constructeur
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 4
Méthodes
Ces méthodes renvoient true si le caractère ch est une minuscule, une majuscule ou une lettre de
titre.
Ces méthodes renvoient true si le caractère ch est défini dans l'ensemble des caractères Unicode.
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.
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.
Renvoie true si le caractère ch est un espace c'est-à-dire un des caractères ' ', '\t', '\f', '\n' ou
'\r'.
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é.
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
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.
Champs
Constructeurs
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
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.
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).
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.
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
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.
Champs
Constructeurs
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
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.
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).
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.
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.
Champs
Ces constantes permettent d'obtenir les valeurs correspondantes à l'infini positif, négatif ou
représentant une valeur non significative.
Constructeurs
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 8
Ces méthodes renvoie true si le nombre mémorisé est infini ou si c'est une valeur non significative.
Ces méthodes renvoie true si v est une valeur infinie ou si c'est une valeur non significative.
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.
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.
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.
Champs
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).
Constructeurs
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 9
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
Ces méthodes renvoie true si le nombre mémorisé est infini ou si c'est une valeur non significative.
Ces méthodes renvoie true si v est une valeur infinie ou si c'est une valeur non significative.
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.
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.
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.
Cette classe final rassemble l'ensemble des méthodes de calcul mathématique Java. Toutes ses
méthodes sont static.
Champs
Méthodes
Renvoie l'arc sinus (sin-1) du nombre a, exprimé en radian et compris entre -PI/2 et PI/2.
Renvoie l'arc cosinus (cos-1) du nombre a, exprimé en radian et compris entre 0 et PI.
Renvoie l'arc tangente (tan-1) du nombre a, exprimé en radian et compris entre -PI/2 et PI/2.
Renvoie l'arc tangente (tan-1) de b/a, exprimé en radian et compris entre -PI et PI.
Ces méthodes renvoient la valeur arrondie au plus proche entier du nombre décimal a.
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 11
Renvoie un nombre aléatoire compris entre 0. inclus et 1. exclu. Voir aussi la classe
java.util.Random.
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
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
Méthodes
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
Ces méthodes ont le même effet que celles que de la classe java.lang.System.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 13
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
Runtime. Ces méthodes permettent de contrôler l'état d'un process.
Constructeurs
public Process ()
Méthodes
Ces méthodes renvoient les flux de sortie, d'entrée et d'erreur d'un 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 entre autre, l'accès au système
de fichiers local.
Comme l'implémentation de toutes les méthodes check... () de cette classe déclenche une
exception de classe SecurityException, toute classe qui dérive de la classe SecurityManager doit
outrepasser les méthodes pour autoriser tel ou tel action de la Machine Virtuelle Java.
Champs
Constructeurs
Méthodes
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 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");
Méthodes
Cette méthode doit renvoyer true s'il reste encore un ou plusieurs éléments à énumérer.
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 ().
Champs
Constructeurs
public Vector ()
public Vector (int initialCapacity)
public Vector (int initialCapacity, int capacityIncrement)
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 3
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
Ajoute l'élément obj en fin de vecteur. Si le tableau du vecteur est trop petit, il est automatiquement
agrandi.
Insère l'élément obj à l'indice index. Si le tableau du vecteur est trop petit, il est automatiquement
agrandi.
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.
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.
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.
Réduit la capacité du tableau du vecteur aux nombres d'éléments mémorisés par le vecteur.
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.
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.
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.
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.
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
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 5
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 instanciée (comme la classe Hashtable) :
Constructeur
public Dictionary ()
Méthodes
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.
Cette méthode doit renvoyer l'élément associé à la clé key, ou null si la clé key n'existe pas.
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.
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.
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
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 6
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.
Méthodes
Ces méthodes outrepassent celles de la classe Dictionary. Pour plus d'informations, consultez la
classe Dictionary.
Ces méthodes renvoient true si respectivement un élément ou une clé existe dans la table.
Renvoie un clone de la table de hash. Cette méthode outrepasse la méthode clone () de la classe
Object.
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.
Champ
Constructeurs
public Properties ()
public Properties (Properties defaults)
Méthodes
Ces méthodes renvoient la propriété de nom property, ou defaultValue si cette propriété n'est pas
définie.
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 7
Ecrit sur le flux de données out la liste des propriétés et de leur valeur.
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
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 8
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
Méthodes
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.
Ces méthodes renvoient true si une date est inférieure ou supérieure à la date when.
Renvoie une chaîne de caractères décrivant la date courante respectant la notation locale.
Renvoie une chaîne de caractères décrivant la date courante respectant la notation GMT.
Exemples
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)
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
Exemples
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
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
Ces deux méthodes renvoient vraies si la chaîne str a encore des sous-chaînes à énumérer.
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.
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
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
Ces méthodes permettent d'ajouter, de supprimer ou de compter les observateurs d'un objet.
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é.
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 :
import java.awt.*;
import java.applet.Applet;
import java.util.*;
Thread.sleep (200);
}
}
catch (InterruptedException e) { }
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 12
}
}
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 1
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.
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.
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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 2
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.
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.
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.
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
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
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.
Champs
La chaîne ou le caractère utilisé comme séparateur de plusieurs chemins d'accès (égal généralement
à ;).
Constructeurs
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.
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
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.
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.
Ces méthodes renvoient true, si le chemin d'accès est accessible en lecture ou en écriture.
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.
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.
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
répertoires autorisés par la méthode accept () de la classe de l'objet filter implémentant l'interface
FilenameFilter.
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 champs 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.
Champs
Ces champs représentent l'entrée standard, la sortie standard ou la sortie standard des erreurs. Les
champs de même nom de la classe System sont construits à partir de ces champs.
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
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.
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.
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 :
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.*;
// 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 le champ static in de la classe System, qui est
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 7
de classe InputStream, soit par le champ static in de la classe FileDescriptor. Ce dernièr champ
étant lui-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).
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 :
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.*;
fluxFichier.close ();
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 8
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant l'accès au fichier
System.out.println (e);
}
}
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
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.
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.
Saute n octets plus loin dans la lecture du flux de données. Renvoie le nombre d'octets effectivement
sautés.
Renvoie le nombre d'octets que les méthodes read () peuvent actuellement lire sans bloquer le thread
courant.
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.
Repositionne la position courante dans le flux de données sur la dernière marque positionnée par la
méthode mark (). Si aucune marque n'a été positionnée ou si la marque est invalide, une exception
IOException est déclenchée.
Renvoie true si le flux de données autorise l'utilisation des méthodes mark () et reset ().
Exemples
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
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.
Construit une instance de classe FileInputStream à partir d'un descripteur de fichier fdObj.
Méthodes
Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à la lecture d'un fichier.
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
La classe java.io.StringBufferInputStream
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 10
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.
Champs
Constructeur
Méthodes
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.
Champs
Constructeurs
Construit une instance de classe ByteArrayInputStream à partir du tableau d'octets buf, où length octets
seront lus à partir de l'indice offset.
Méthodes
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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 11
flux de données.
Constructeurs
public PipedInputStream ()
public PipedInputStream (PipedOutputStream src) throws IOException
Méthodes
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
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.
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
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.
Champ
protected InputStream in
Le flux de données sur lequel la lecture est faite. Utilisez ce champ dans vos classes dérivées pour lire
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 12
Constructeur
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
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 :
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);
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 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é.
Champs
Constructeurs
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
Exemples
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
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 14
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.
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.
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.
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
Construit une instance de la classe DataInputStream dont la lecture sera effectuée sur le flux de données
in.
Méthodes
Exemple
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
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
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.
Champ
Constructeur
Méthodes
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.
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.
Champs
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.
Ce champ 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.
Ces champs mémorisent le dernier nombre (si ttype = TT_NUMBER) ou le dernier mot lu (si ttype =
TT_WORD).
Constructeur
Construit une instance de la classe StreamTokenizer avec comme flux de données d'entrée in.
Méthodes
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, le champ ttype est égal à TT_WORD et le
champ sval contient le mot lu.
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.
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.
Permet d'ajouter le caractère de code ch ou tous les caractères dont le code est compris entre low et high à
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 17
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, le champ ttype a pour valeur ce caractère.
Permet d'ajouter le caractère de code ch à l'ensemble des caractères utilisés pour entourer une chaîne de
caractères constante. Quand une chaîne de caractères constante est lue, le champ ttype est égal au
délimiteur et le champ sval contient la chaîne lue sans les délimiteurs.
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, le champ ttype est égal à
TT_NUMBER et le champ nval contient le nombre lu.
Permet de spécifier que les caractères de fin de ligne sont significatifs ou non. Si flag est égal à true, le
champ 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.
Permet de spécifier si les commentaires Java de fin de ligne // ... sont significatifs ou non.
Permet de spécifier si tous les mots lus sont convertis en minuscules ou non.
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 du champ ttype.
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 .
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 à 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 :
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 champs static out et err de
la classe System, qui sont de classe PrintStream, soit par les champs static out et err de la classe
FileDescriptor. Ces dernièrs champs étant eux-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.
import java.io.*;
import java.util.Vector;
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
Ecrit length octets du tableau tab sur le flux de données, à partir de l'indice offset.
Ecrit physiquement sur le flux de données quand une zone tampon (buffer ) est utilisée.
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 20
Exemple
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
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éé.
Construit une instance de classe FileInputStream à partir d'un descripteur de fichier fdObj.
Méthodes
Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture dans un fichier.
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
Méthodes
Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture sur un pipeline.
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.
Champs
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
Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture dans un tableau
d'octets.
Renvoie un tableau d'octets copie des octets écrits dans le tableau interne.
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.
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.
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).
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 22
Champ
Constructeur
Méthodes
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é.
Champs
Constructeurs
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
Exemple
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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 23
Cette méthode doit permettre d'écrire length octets du tableau tab, à partir de l'indice offset.
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.
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).
Cette méthode doit permettre d'écrire la chaîne de caractères s, comme une suite de caractères Unicode.
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.
Champ
Constructeur
Construit une instance de la classe DataOutputStream dont l'écriture sera effectuée sur le flux de données
out.
Méthodes
Implémentation des méthodes de l'interface DataOutput. La méthode write (byte [ ] tab) est
implémentée par la classe FilterOutputStream.
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 le champ 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. Si une erreur survient pendant
l'écriture sur le flux de données, un champ 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 le champ System.out sans être obligé d'utiliser un
bloc try ... catch.
Constructeurs
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
Appelle la méthode flush () et renvoie true si une erreur est survenue pendant un appel aux autres
méthodes de cette classe.
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 ().
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.
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
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
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
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.
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.
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.
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.
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 standard définit comment appeler un
programme et lui transmettre ses paramètres.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 2
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 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.
Construit une instance de classe URL à partir de la chaîne de caractères spec représentant une URL.
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
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.
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.
Renvoie une chaîne de caractères correspondant à l'URL mémorisée par un objet de classe URL.
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.
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 3
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.*;
Autre exemple
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,...).
Champs
Ces champs protected sont utilisés par les classes dérivées de la classe URLConnection.
Constructeur
Méthodes
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.
Renvoie la taille du fichier avec lequel la connexion est faite ou 0 si cette taille n'est pas connue.
Renvoie le type du contenu et son codage ou null si ces renseignements ne sont pas connus
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.
Ces méthodes renvoient les renseignements correspondant aux clés contenues dans l'entête d'un
fichier
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.
Renvoie la valeur associée au mot-clé key par lequel une requête est reconnue.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 5
Les classes dérivées de la classe URLConnection doivent implémenter cette méthode pour réaliser la
connexion avec une URL.
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 :
import java.applet.Applet;
import java.awt.Graphics;
import java.net.*;
import java.io.*;
// Fermeture de la connexion
fluxFichier.close ();
}
catch (Exception e)
{
texteLu = "Probleme...";
}
}
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
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
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 7
public URLStreamHandler ()
Méthodes
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.
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
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
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 8
Principe
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
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
vendredi 13 octobre 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
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 10
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é.
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.
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 de 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
throws UnknownHostException
Renvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine host.
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").
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).
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
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
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 12
port port. Si stream (égal à true par défaut) est égale à false, un socket de datagrammes est créé.
Ces constructeurs fonctionnent comme les deux premiers, mais prennent comme premier paramètre
une instance de la classe InetAddress.
Méthodes
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.
Ces méthodes renvoient l'adresse Internet et le port du socket distant auquel le socket est connecté.
Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le socket.
Exemples
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
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
Attend la demande de connexion d'un programme client, et renvoie un socket une fois la connexion
établie.
Ces méthodes renvoient l'adresse Internet et le port local sur lequel le socket du serveur est
connecté.
Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le
serveur.
Exemples
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.*;
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);
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 14
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 ) :
import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.*;
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) :
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 15
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 :
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.*;
if (lancerServeur () == null)
System.exit (-1);
}
return serveur;
}
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');
}
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.*;
fluxSortie = null;
}
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)
{ }
}
if (polylineCourante != null)
drawPolyline (gc, polylineCourante);
}
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 pour les sockets côté client comme côté serveur. Elle comporte des champs 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.
Champs
protected FileDescriptor fd
protected InetAddress address
protected int port
protected int localport
Constructeur
public SocketImpl ()
Méthodes
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
Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract SocketImpl.
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
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.
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
Renvoie l'adresse Internet de la machine dont provient ou auquel est destiné ce datagramme.
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
Construit une instance de la classe DatagramSocket. Le port utilisé pour recevoir ou envoyer des
datagrammes est le premier disponible sur la machine locale.
Construit une instance de la classe DatagramSocket. Le port port sur la machine locale est utilisé
pour recevoir ou envoyer des datagrammes.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 22
Méthodes
Renvoie le port de la machine locale sur lequel l'envoi ou la réception de datagrammes s'effectue.
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.
Ferme le socket.
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 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 :
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 2
Les applets sont des applications un peu spéciales et fonctionnent différemment des applications
Java. En voici les principales caractéristiques :
La classe d'une applet doit obligatoirement dériver de la classe Applet, être public et avoir un
constructeur sans paramètre public (éventuellement celui fourni par défaut).
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.
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.
La classe java.applet.Applet
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 3
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.
Constructeur
public Applet ()
Méthodes
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.
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.
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.
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.
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,...) .
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"}.
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).
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 4
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.
Renvoie l'instance de la classe AppletContext, représentant le contexte dans lequel l'applet tourne
(fourni par le navigateur ou par appletviewer ).
Renvoie true si une applet est active. Une applet devient active à l'appel de sa méthode start ().
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).
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.
Ces méthodes permettent d'obtenir ou de jouer directement le fichier son à l'URL url ou le fichier
son name relatif à url.
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'interface java.awt.AppletContext
Méthodes
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).
showDocument () est utilisé typiquement pour charger un document quand un utilisateur clique sur
un bouton défini dans une applet.
Exemples
L'interface java.applet.AppletStub
L'interface AppletStub est utilisée pour intégrer une applet dans un navigateur ou dans
appletviewer .
Méthodes
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 :
import java.applet.Applet;
import java.awt.*;
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);
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 :
L'applet PlayApplet n'a pas qu'un intérêt didactique : elle permet de contrôler sans les modifier, des
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 7
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 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 (µ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
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.
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 !):
import java.applet.*;
import java.awt.*;
// 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);
}
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.
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 :
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 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 1
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 :
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 2
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 3
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.
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
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.
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 champs,
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é.
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.
Vous pouvez rendre utilisable ou inutilisable un composant grâce à ces méthodes. Un composant
inutilisable (disabled ) est généralement grisé.
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.
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).
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 5
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.
Si un composant est invalide (son image à l'écran ne correspond pas aux valeurs de ses champs),
cette méthode appelle layout (). Elle est outrepassée dans la classe Container pour mettre à jour
tous les composants d'un container invalide.
Marque un composant et ses parents comme étant invalide. Ainsi, un appel à validate () pourra
mettre à jour ces composants invalides.
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é.
Permet de récupérer les renseignements concernant une police de caractère (hauteur, largeur,...).
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.
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.
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.
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.
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.
Ces méthodes sont utilisées pour la gestion événementielle des composants (voir le chapitre sur la
gestion de l'interface utilisateur).
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.
Donne le focus au composant suivant parmi ceux de la liste des composants d'un container.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 7
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.
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).
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
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
Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un
bouton.
Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
bouton.
Exemples
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,
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 8
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.
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
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.
Ces méthodes permettent d'interroger ou de modifier le label d'une boite à cocher ou d'un bouton
radio.
Ces méthodes permettent d'interroger ou de modifier l'état (coché ou non) d'une boite à cocher ou
d'un bouton radio.
Ces méthodes permettent d'interroger ou de modifier le groupe auquel appartient un bouton radio.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 9
Ces méthodes permettent d'interroger ou de modifier le bouton radio qui est actuellement coché.
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
Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
instance de la classe Choice.
Permet d'ajouter en fin de liste un nouveau choix égal à la chaîne de caractères item.
Ces méthodes renvoient soit la chaîne de caractères soit l'indice du choix courant.
Ces méthodes permettent de désigner le choix courant du composant, soit en donnant son indice
index, soit en donnant sa valeur str.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 10
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.
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
Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
liste.
Permet d'ajouter en fin de liste un nouveau choix égal à la chaîne de caractères item.
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).
Supprime le choix à l'indice index, ou tous les choix compris entre les indices start et end.
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).
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 11
Ces méthodes renvoient soit un tableau des chaînes de caractères sélectionnées soit un tableau de
leur indice.
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 ().
Méthodes de la classe Component, outrepassées pour renvoyer la taille préférée ou la taille minimum
d'une liste.
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.*;
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.
Champs
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
l'alignement du texte dans le composant (égal à LEFT, CENTER ou RIGHT).
Méthodes
Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un
label.
Ces méthodes permettent d'interroger ou de modifier l'alignement du texte dans un label (égal à
LEFT, CENTER ou RIGHT).
Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
label.
Exemples
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
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é.
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.
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 14
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.
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.
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).
import java.awt.*;
import java.applet.*;
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;
}
Autre exemple
Applet ListePolices .
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
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.
Permet d'insérer la chaîne de caractères str à la position pos dans le texte de la zone de saisie.
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.
Ces méthodes permettent d'interroger le nombre de colonnes ou le nombre de lignes occupées par la
zone de saisie.
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.
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
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.
Champs
Constructeurs
public Scrollbar ()
public Scrollbar (int orientation)
public Scrollbar (int orientation, int value, int visible,
int minimum, int maximum )
Méthodes
Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un
ascenseur.
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").
Modifie la valeur courante, la taille logique de la portion visible, le minimum et le maximum d'un
ascenseur.
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.*;
Sous le bouton, une liste affichant l'ensemble des appels aux méthodes a été ajoutée.
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 le
champ private peer de la classe Component. L'ensemble des interfaces Peer sont déclarées dans le
package java.awt.peer.
Le champ 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 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 :
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 19
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
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
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.
Synchronise l'affichage graphique ; cette méthode peut être utile pour réaliser des animations.
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)
Exemples
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 :
import java.applet.Applet;
import java.awt.*;
import java.util.Date;
Les relations entre les composants de l'applet précédente décrivent l'arbre suivant :
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 un champ 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
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 3
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.
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
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).
Ces méthodes renvoient le nième composant ou tous les composants d'un container.
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 4
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.
Méthode de la classe Component, outrepassée pour disposer les composants d'un container.
Méthode de la classe Component, outrepassée pour mettre à jour tous les composants d'un container
invalide.
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à.
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.
Cette méthode qui outrepasse celle de la classe Component est utilisée pour la gestion événementielle
(voir le chapitre suivant).
Cette méthode outrepasse la méthode addNotify () de la classe Component pour appeler la méthode
addNotify () sur chacun des composants du container.
Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
container.
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é
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 5
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
Cette méthode outrepasse la méthode addNotify () de la classe Container pour créer le peer d'un
panel puis appeler la méthode addNotify () de la classe Container.
Exemples
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
Ce constructeur construit une fenêtre invisible dont le parent parent doit être de classe Frame ou ses
dérivées.
Méthodes
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.
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.
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.
Détruit une fenêtre. Vous devez appeler cette méthode pour que la fenêtre à l'écran soit
effectivement détruite.
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 6
Ces méthodes ramène une fenêtre devant ou derrière toutes les autres.
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.
Renvoie le warning d'une fenêtre. Ce type de message est affiché dans les fenêtres créées dans un
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.
Champs
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)
Méthodes
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.
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.
Ces méthodes permettent d'interroger ou de modifier la barre de menu utilisée pour une fenêtre.
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.
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 champs.
Méthode de la classe Container, outrepassée pour renvoyer une chaîne de caractères décrivant la
fenêtre.
Exemples
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 :
// Démarrage de l'applet
applet.init ();
fenetreApplet.validate ();
}
}
La classe java.awt.Dialog
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 8
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 avec la méthode dispose () vos boites de dialogues modales une fois son
traitement terminé.
Constructeurs
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
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.
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 :
import java.applet.Applet;
import java.awt.*;
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 modale 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 la
Machine Virtuelle Java. Comme cette classe donne accès au système de fichiers, certains
navigateurs interdisent tout simplement de s'en servir.
Champs
Ces constantes sont utilisées pour choisir le mode de saisie du fichier de la boite de dialogue
(Ouvrir ou Enregistrer ).
Constructeurs
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
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.
Ces méthodes permettent d'interroger le répertoire et le fichier saisis dans la boite de dialogue. Si
l'utilisateur a annulé la saisie, ces méthodes renvoient null.
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 ).
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.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 11
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.
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. 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
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.
Méthode appelée quand le composant component est enlevé d'un container par la méthode remove
(component) de la classe Container.
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 12
Doit renvoyer les dimensions préférées du container parent auquel ont été ajoutés des composants.
Doit renvoyer les dimensions minimum du container parent auquel ont été ajoutés des composants.
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
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 ().
Champs
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
Exemples
La classe java.awt.BorderLayout
Cette classe qui implémente l'interface LayoutManager, est un type de layout qui permet de gérer la
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 13
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).
Constructeurs
public BorderLayout ()
public BorderLayout (int horizontalgap, int verticalgap)
Méthodes
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) :
import java.applet.Applet;
import java.awt.*;
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
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
Ces constructeurs permettent de créer un layout GridLayout avec une grille de rows lignes par cols
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
Exemples
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 nombreux champs et
méthodes protected, vous permettant de modifier le comportement de cette classe dans une classe
dérivée.
Champs
Constructeur
public GridBagLayout ()
Méthodes
Positionne les contraintes sur un composant comp géré par un layout GridBagLayout.
Renvoie une copie des contraintes positionnées sur un composant géré par un layout GridBagLayout.
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 Champ permet de saisir un nom de champ pour chacun des composants que vous ajoutez
; ce nom est celui utilisé pour intituler chacune des champs 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 13 octobre 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.
Champs
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.
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 ().
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.
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 champs de contraintes les valeurs par défaut suivantes :
Méthode
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 13 octobre 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)
Méthodes
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.
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.
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).
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, puisque leur classe n'hérite pas de la classe Frame.
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 20
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) :
import java.applet.Applet;
import java.awt.*;
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
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
Ces méthodes permettent d'obtenir le container auquel est rattaché un composant de menu et le peer
d'un composant de menu.
Ces méthodes permettent d'interroger ou de modifier la police de caractère utilisée pour l'affichage
d'un composant de menu.
Cette méthode est appelée pour supprimer le peer d'un composant de menu.
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).
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
Ces méthodes permettent de créer et de détruire le peer d'une barre de menu (removeNotify ()
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 22
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.
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
Méthodes
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é.
Méthode de la classe MenuComponent, outrepassée pour renvoyer une chaîne de caractères décrivant
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 23
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
Méthodes
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).
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
Méthodes
vendredi 13 octobre 2000 Du C/C++ à Java : Les containers et la disposition des composants Page: 24
Ces méthodes permettent d'interroger ou de modifier l'état coché ou non de l'élément de menu.
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 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 1
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 :
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 qu'il faut bien
comprendre pour 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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 2
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.
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) :
// Constructeur
public BoutonMiseAJour (String label,
Label heure)
{
super (label);
this.heure = heure;
}
Nota : La liste de tous les événements reconnus en Java est donnée dans la classe Event décrite
ci-après.
vendredi 13 octobre 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)
import java.applet.Applet;
import java.awt.*;
import java.util.Date;
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 ().
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 5
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 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 champs de la classe Event sont public,
et vous pouvez consulter directement les caractéristiques d'un événement par ces champs.
Champs
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.
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.
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.
Pour les événements MOUSE_DOWN, représente le nombre de clics consécutifs (égal à 2 pour un double
clic,...).
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.
Désigne l'événement suivant quand les événements sont mis dans une liste chaînée.
Ces constantes représentent la combinaison de l'état enfoncé ou non des touches Shift (Majuscule),
Control et Alt.
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.
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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 7
Ces constructeurs permettent de créer un événement. Les paramètres correspondent aux champs de
la classe Event.
Méthodes
Ces méthodes permettent de savoir si lors d'un événement les touches Shift, Control et Meta sont
enfoncées (true) ou non.
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
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
Crée une copie d'un contexte graphique, en effectuant en plus une translation (x,y) sur le repère
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 8
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.
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.
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.
Ces méthodes permettent d'interroger ou de modifier la couleur courante utilisée pour dessiner.
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.
Ces méthodes permettent d'interroger ou de modifier la police de caractère courante utilisée pour
dessiner les textes.
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 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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 9
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.
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.
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.
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.
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)
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 la méthode clipRect (). Voir aussi le
chapitre sur les images.
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.
Détruit le contexte graphique (appel de dispose ()). Cette méthode outrepasse celle de la classe
Object.
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 :
import java.applet.Applet;
import java.awt.*;
import java.util.Random;
// 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;
}
Autres exemples
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,...) :
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).
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 13
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 .
Champs
Ces constantes permettent de spécifier le style de la police de caractères à sa création (normal, gras
ou italique). Il est possible de combiner les styles avec l'opérateur | (par exemple, Font.BOLD |
Font.ITALIC).
Ces champs protected permettent d'accéder aux caractéristiques d'une police de caractères, dans les
classes dérivées de Font.
Constructeur
Construit une police de caractères de nom name, de style style et de taille size.
Méthodes
Ces méthodes permettent d'interroger le nom, le style et la taille d'une police de caractères`.
Ces méthodes renvoient true si une police de caractères a un style normal, gras ou italique.
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.
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.
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
La classe java.awt.FontMetrics
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 14
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).
Champ
Constructeur
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
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.
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.
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.
Renvoie un tableau décrivant la taille des 256 premiers caractères d'une police.
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 :
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 15
import java.awt.*;
import java.applet.*;
Autres exemples
La couleur
La classe java.awt.Color
Cette classe permet de manipuler une couleur. Elle déclare un ensemble de constantes décrivant les
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 17
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.
Champs
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
Construit une couleur à partir de ses composantes entières rouge r, vert g et bleu b comprises entre
0 et 255.
Construit une couleur à partir de l'entier rgb combinaison des composantes rouge (bits 16 à 23), vert
(bits 8 à 15) et bleu (bits 0 à 7).
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
Ces méthodes renvoient les composantes rouge, vert ou bleu d'une couleur comprises entre 0 et 255.
Renvoie un entier combinaison des composantes rouge (bits 16 à 23), vert (bits 8 à 15) et bleu (bits 0
à 7) d'une couleur.
Ces méthodes renvoient une couleur plus claire ou plus foncée d'une couleur.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 18
Crée une couleur à partir de sa teinte hue, sa saturation saturation et sa luminosité brightness
comprises entre 0.0 et 1.0.
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 :
import java.awt.*;
import java.applet.*;
return true;
}
}
// Classe ComposantNuancier
class ComposantNuancier extends Panel
{
private static final int TAILLE = 40;
private Rectangle [ ] carreaux = new Rectangle [TAILLE * TAILLE];
private Color [ ] couleurCarreaux = new Color [TAILLE * TAILLE];
Autres exemples
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 20
Cette classe permet de manipuler les dimensions d'un objet (largeur et hauteur) grâce à ses deux
champs public width et height.
Champs
Constructeurs
public Dimension ()
public Dimension (Dimension d)
public Dimension (int width, int height)
Méthode
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
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.
Champs
Ces champs 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
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
Champs
public int x
public int y
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 21
Constructeur
Méthodes
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.
Champs
Constructeurs
public Polygon ()
public Polygon (int xpoints [ ], int ypoints [ ], int npoints)
Méthodes
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.
Champs
public int x
public int y
public int width
public int height
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 22
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
Renvoie true si le rectangle r et le rectangle sur lequel la méthode est invoquée ont une
intersection.
Ces méthodes renvoie un rectangle résultat de l'intersection ou de l'union entre deux rectangles.
Ces méthodes ajoutent un point à un rectangle qui est agrandit si nécessaire pour contenir le point
passé en paramètre.
Ajoute le rectangle r au rectangle sur lequel la méthode est invoquée. Le résultat est l'union des
deux rectangles.
Agrandit un rectangle de horiz points à gauche et à droite, et de vert points en haut et en bas.
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.
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 23
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.
Constructeur
public Canvas ()
Méthodes
Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
instance de Canvas.
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 :
import java.awt.*;
import java.net.*;
// 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
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion de l'interface utilisateur Page: 24
if (boutonEnfonce != nouvelEtat)
{
boutonEnfonce = nouvelEtat;
paint (getGraphics ());
}
}
}
import java.applet.Applet;
import java.awt.*;
import java.net.*;
// Importation de la classe BoutonImage du package tools
import tools.BoutonImage;
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 13 octobre 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 :
import java.applet.Applet;
import java.awt.*;
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
Champ
Constructeur
public Image ()
Méthodes
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.
Renvoie une instance de la classe Graphics, grâce à laquelle vous pouvez dessiner dans l'image.
Renvoie une instance de la classe implémentant l'interface ImageProducer. Cet objet est notamment utilisé
par les classes réalisant un filtrage.
Exemples
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 :
import java.applet.Applet;
import java.awt.*;
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.
Champs
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
Construit une instance de MediaTracker. comp désigne un composant dans lequel sera visualisé les
images à charger.
Méthodes
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.
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.
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é.
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é.
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).
Ces méthodes renvoient un tableau contenant toutes les images éventuellement d'identifiant id,
dont le chargement a produit une erreur.
Exemples
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.
Champs
Une erreur est survenue pendant le chargement de l'image. Combiné avec ABORT.
Méthodes
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 :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
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
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
L'applet suivante, comme l'applet MultiImages , crée un nuancier en rouge et vert mais cette fois-ci
en utilisant la classe MemoryImageSource :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
Remplir le tableau oblige ici à positionner les bits des composantes Rouge, Vert, Bleu d'une couleur.
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 9
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.
Champ
Constructeur
Méthodes
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.
Renvoie le nombre de bits utilisé par un modèle de couleur pour coder la couleur d'un pixel.
Ces méthodes doivent renvoyer les composantes Rouge, Vert, Bleu et Alpha (comprises entre 0 et 255)
pour une valeur 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,
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 10
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
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.
Renvoie la couleur RGB utilisant le modèle de couleur RGB par défaut pour une valeur pixel.
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
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).
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
Ces méthodes renvoient les composantes Rouge, Vert, Bleu ou Alpha (comprises entre 0 et 255) à l'indice
pixel de la palette.
Renvoie la couleur RGB utilisant le modèle de couleur RGB par défaut à l'indice pixel de la palette.
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 :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import java.util.Random;
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
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
Ces méthodes démarre la récupération des points de l'image (éventuellement pendant ms millisecondes
maximum).
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 [ ],
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 13
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
Méthodes
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.
Champ
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
Implémentation des méthodes de l'interface ImageConsumer pour renvoyer l'image non modifiée à
consumer.
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
Construit un filtre permettant d'extraire une image aux coordonnées (x,y), de largeur width et de
hauteur height.
Méthodes
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 15
cette méthode pour récupérer la couleur de chacun des points de la nouvelle image.
Champs
Si le filtrage de la couleur ne dépend pas des coordonnées des points de l'image, il est conseillé de
mettre ce champ à true.
Constructeur
public RGBImageFilter ()
Méthodes
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.
Ces méthodes outrepassent celles de la classe ImageFilter pour réaliser les opérations du filtre.
Voici un exemple d'applet utilisant cette classe pour créer le négatif d'une image :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
negatifImage;
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).
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 17
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.
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 ().
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
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 18
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) :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
if (bytePixels != null)
consumer.setPixels ((nbChiffres - puissanceDix - 1) * largeurChiffre, 0,
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 20
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
Ces méthodes doivent ajouter ou retirer le consommateur d'images ic, de l'ensemble des
consommateurs enregistrés par ce producteur.
Doit renvoyer true si ic appartient à l'ensemble des consommateurs d'images enregistrés par ce
producteur.
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
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.
Champs
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 qui suivent, 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é.
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
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.
Cette méthode est appelée par le producteur d'images pour transmettre au consommateur les
propriétés props de l'image.
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.
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.
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).
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 22
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.
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.
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é :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
imageTracker.waitForID (0);
// En cas d'erreur, déclenchement d'une exception
if (imageTracker.isErrorAny ())
throw new IllegalArgumentException ("Images non chargees");
}
catch (Exception e)
{ }
}
Cette applet montre l'intérêt d'utiliser le système du double buffering pour gérer l'animation d'une
image 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 :
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 24
import java.applet.Applet;
import java.awt.*;
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 :
// ...
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);
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 double buffer puis en dessinant les aiguilles par dessus à leur nouvelle
vendredi 13 octobre 2000 Du C/C++ à Java : Les images Page: 26
position :
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import java.util.Date;
if (imageTracker.isErrorAny ())
fondHorloge = null;
}
catch (Exception e)
{ }
}
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 13 octobre 2000 Du C/C++ à Java : Plus loin avec Java... Page: 1
Classes internes (inner classes ) : ce type de classes a accès à toutes les champs 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...
vendredi 13 octobre 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), suivie de la description de la bibliothèque
d'extension Java 3D. Il sera appelé à présenter plus dans le détail les autres é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 ce 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.
vendredi 13 octobre 2000 Du C/C++ à Java : Plus loin avec Java... Page: 3
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 disponible sur le site http://www.eteks.com.
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 1
Java 3D
Démarrer en Java 3D
Un premier exemple
Principes 3D
Les classes de bases
Java 3D est une bibliothèque de classes d'extension Java destinée à créer des scènes 3D en réalité
virtuelle (avec utilisation de formes complexes, d'éclairages, de textures, d'animations, de sons,...).
Ce chapitre décrit les notions de base utiles pour programmer avec cette bibliothèque.
Démarrer en Java 3D
Prérequis
Ce manuel s'adresse aux personnes connaissant la programmation en Java et désirant s'initier à Java
3D. Si vous ne connaissez pas Java, commencez par apprendre ce langage en utilisant par exemple
le manuel Du C/C++ à Java ou un autre de la liste des liens utiles.
Des connaissances en programmation 3D ne sont pas obligatoires mais des notions en géométrie 3D
(un point dans l'espace défini par ses coordonnées (x,y,z), ça vous dit quelque chose ?) faciliteront
votre apprentissage.
Tous les exemples de ce manuel sont compatibles avec la version 1.1 de bibliothèque Java 3D.
Téléchargement
Java 3D est disponible sur le site de Sun http://www.javasoft.com/. En suivant le lien products ,
vous cherchez le lien Java 3D qui vous amène sur la page de téléchargement de Java 3D. La version
complète de cette bibliothèque est actuellement disponible sous Windows, Solaris, SGI, HP-UX et
Linux. La version Windows est disponible sous OpenGL ou DirectX.
Comme Java 3D est une extension, vous devez avoir déjà installé Java 2 pour pouvoir l'utiliser.
Pour les personnes développant sur d'autres systèmes, il existe aussi la bibliothèque JFree-D
développée par Jean-Christophe Taveau qui implémente en partie Java 3D et qui fonctionne sous
Java 1.1. JFree-D fonctionne avec l'implémentation Java d'OpenGL GL4Java. Les exemples donnés
dans ce manuel (sauf ceux générant du texte) fonctionnent avec la version actuelle de JFree-D.
Sur son site, Sun fournit aussi des documentations en anglais. Voici la liste des plus intéressantes :
Installation
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 2
Il est conseillé d'installer Java 3D dans le même répertoire que le JDK, pour simplifier
l'arborescence de votre installation.
Java 3D installe essentiellement :
Les exemples fournis avec Java 3D fonctionnent généralement sous forme d'application ou
d'applet. Comme Java 3D nécessite Java 2 pour fonctionner, vous pourrez utiliser les versions
actuelles de Netscape Navigator et d'Internet Explorer pour visualiser les applets qu'avec le plug-in
Java. Le plus simple reste d'ouvrir une fenêtre de commandes et d'utiliser les commandes java
pour lancer les applications et appletviewer pour lancer les applets.
Architecture
La réalité virtuelle est grande consommatrice de calculs. Pour accélérer son travail, Java 3D utilise
le plus possible les optimisations 3D offertes par le système sur lequel la Machine Virtuelle Java
tourne :
Quand vous utilisez une version OpenGL de Java 3D, certaines fonctionnalités de l'API font
appel à des méthodes native de la DLL Java 3D/OpenGL . Ces méthodes elles, utilisent des
fonctions de la DLL OpenGL du système.
La DLL OpenGL du système utilise l'accélérateur graphique disponible sur la carte
graphique. La puissance de traitement de Java 3D dépend donc de la puissance de votre
microprocesseur mais aussi de celle de votre carte graphique. Vérifiez que le driver de votre
carte est à jour pour profiter à plein de sa capacité de traitement.
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 3
Un premier exemple
Voici un premier exemple utilisant Java 3D, permettant de mettre en œuvre les principes généraux
de cette bibliothèque.
Cette classe est programmée pour fonctionner en tant qu'applet ou comme application.
Recopiez-la dans un fichier Applet3D.java , que vous compilez avec l'instruction javac
Applet3D.java pour ensuite l'exécuter avec java (ou JBindery sous MacOS), grâce à l'instruction
java Applet3D. Cet exemple affiche un cube avec 6 faces de couleur différente. Comme aucune
rotation n'est effectuée sur ce cube, le résultat n'est pas très impressionnant puisque vous n'en voyez
qu'une seule face ! Mais cet exemple va nous servir comme base pour la suite du manuel...
import java.applet.Applet;
import java.awt.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.applet.MainFrame;
La classe MainFrame utilisée dans la méthode main () de tous les exemples de ce manuel est une
classe utilitaire qui permet d'afficher l'applet passée en paramètre à son constructeur dans une
fenêtre de dimensions données. Cette classe permet aussi d'interpréter les arguments de la ligne de
commande comme des paramètres de l'applet. Par exemple, le code HTML qui utilise la classe
Applet3D, suivant :
Principes 3D
Construction d'un univers 3D
L'exemple précédent utilise tous les éléments nécessaires pour programmer avec Java 3D. De quoi
a-t-on besoin ?
Une instance de Canvas3D : Ce composant AWT qui dérive de la classe java.awt.Canvas, est la
zone de l'écran qui permet de visualiser une scène 3D. Cette zone peut être vue comme la
pellicule d'un appareil photo : elle ne permet pas de capturer tous les objets autour de
l'appareil mais juste d'avoir une vue plus ou moins large sur un ensemble d'objets en 3
dimensions.
Une scène 3D à visualiser : cette scène est un ensemble d'une ou plusieurs formes (shape )
géométriques simples (parallélépipède, sphère, cylindre ou cône) ou complexes (ensemble de
points reliés entre eux, texte 3D). Sur chacune de ces formes 3D, on peut effectuer des
transformations (translation, rotation, homothétie) permettant de les positionner dans l'espace
et de les animer.
L'ensemble de ces formes et de ces transformations est représenté par un graphe de type arbre
dont la racine est une instance de BranchGroup.
Une instance de SimpleUniverse : Cet objet permet de relier l'instance de Canvas3D avec la
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 5
scène 3D à visualiser. Cet objet modélise l'espace dans lequel on positionne l'appareil photo.
A cet espace sont rattachés une instance de Canvas3D et une scène 3D. La classe
SimpleUniverse est une classe utilitaire qui permet de créer des instances par défaut des classes
VirtualUniverse, Locale, ViewPlatform, View, PhysicalBody et PhysicalEnvironment,
nécessaires à Java 3D.
Dans la classe Applet3D, la création de l'arbre représentant la scène à visualiser est isolée dans la
méthode createSceneTree (). Tous les exemples qui suivent dans ce manuel outrepassent cette
méthode pour montrer les différents types de scènes qu'il est possible de créer en Java 3D.
Repère 3D
Une scène 3D est composée d'un ensemble de formes 3D. Chacune de ces formes est construit par
un assemblage de triangles ou de quadrilatères plans. Ces formes simples sont décrites grâce aux
coordonnées (x,y,z) de leurs sommets. Pour s'orienter correctement dans la scène 3D à laquelle
vous allez ajouter des formes 3D, il faut donc connaître comment est positionné le repère 3D
sous-jacent à tout espace 3D.
Quand vous utilisez la classe SimpleUniverse, le centre du repère 3D est dans l'axe du milieu du
composant Canvas3D. L'axe x va vers la droite, l'axe y vers le haut. Pour que le repère soit direct,
l'axe z est orienté vers l'observateur. Sur la figure, les flèches circulaires indiquent le sens positif de
rotation autour de chaque axe.
La méthode setNominalViewingTransform () utilisée dans la classe Applet3D permet de positionner
l'instance de Canvas3D à une certaine distance sur l'axe z entre l'observateur et l'origine du repère. La
largeur du composant Canvas3D permet alors de voir entièrement un cube d'une unité de côté et dans
le plan z=0 les points de coordonnées (-1,0,0) et (1,0,0).
Ce paramètrage est celui utilisé par défaut par la classe SimpleUniverse mais il est tout à fait
possible de le changer pour avoir une ou plusieurs vues différentes sur une scène 3D.
En 3D, l'axe y est orienté vers le haut et non vers le bas comme généralement en dessin 2D sur
ordinateur.
Transformation 3D
Les transformations sont utilisées en 3D pour positionner et animer une forme 3D dans l'espace.
Java 3D utilise la classe Transform3D pour décrire une opération de translation, de rotation ou
d'homothétie (changement d'échelle, scale en anglais). Cette opération est associée à une instance
de la classe TransformGroup et ajoutée à l'arbre de la scène pour l'appliquer sur une forme.
Voici l'applet CubeFaces dérivant de la classe Applet3D : Cet exemple effectue sur un cube deux
rotations de PI / 6 radian (30°) autour des axes x et y. Ces deux rotations permettent à l'observateur
de découvrir les couleurs de 3 des faces du cube.
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 6
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.applet.MainFrame;
return root;
}
L'ordre dans lequel sont effectués les transformations a une importance. Si, dans la méthode
createSceneTree () de la classe CubeFaces, vous enchaînez différemment les rotations sur les axes
x et y, vous n'obtiendrez pas le même résultat :
// Construction de la branche de l'arbre de la scène
// Rotation autour de l'axe y puis de l'axe x
rotationYAxisGroup.addChild (cube);
rotationXAxisGroup.addChild (rotationYAxisGroup);
root.addChild (rotationXAxisGroup);
Un arbre représentant une scène 3D est constitué de nœuds (node ) qui sont soit des feuilles (leaf ),
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 7
soit des groupes (group ). Les nœuds reliés entre eux ont une relation parent-enfant (parent-child ).
Tous les nœuds ont un parent sauf la racine (root ) et peuvent avoir un ou plusieurs enfants sauf les
feuilles.
Voici l'applet MultiCubes créant une scène 3D représentée par un cercle de 12 cubes centré autour
de l'axe z. Une rotation de 45° est appliquée ensuite autour de l'axe x, pour mieux voir cet
ensemble.
import javax.media.j3d.*;
import javax.vecmath.Vector3f;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.applet.MainFrame;
return cubeCircle;
}
Les douze cubes subissent tous le même type de transformation : une translation de 0.7 unité
le long de l'axe x puis une rotation de i*PI/6 radian autour de l'axe z, i étant compris entre 0
et 11.
Chaque feuille qui correspond à un cube est donc liée à deux groupes de transformations
: ce sous-ensemble forme une branche rattachée au groupe , racine de l'arbre qui rassemble
les douze cubes. Cet ensemble forme un arbre cohérent représentant le cercle des douze cubes.
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 9
La création un groupe intermédiaire permet de manipuler le cercle de cubes par sa racine. Mais
tout groupe peut avoir plusieurs enfants et la programmation de l'arbre suivant aurait donné le
même résultat :
Figure 6.
Arbre simplifié de la scène MultiCubes
Un arbre est un graphe ayant des propriétés particulières : A partir de sa racine, il est possible
d'énumérer toutes les feuilles et il existe un chemin unique reliant la racine à chacune des
feuilles.
En Java 3D, une feuille représentant une forme géométrique est un assemblage de triangles
ou de quadrilatères décrits par les coordonnées (x,y,z) de leurs sommets.
Le chemin qui relie la racine à une feuille est un ensemble de groupes. Java 3D part de chaque
feuille puis remonte le chemin jusqu'à la racine de l'arbre, en appliquant aux coordonnées des
sommets l'opération correspondant à chacun des groupes de transformations rencontrés. Les
transformations sont donc appliquées dans l'ordre, d'une feuille vers la racine .
L'arbre d'une scène 3D peut contenir des groupes de transformations et des formes 3D, mais aussi
d'autres types d'éléments utilisés par Java 3D : des nœuds représentant des lumières, des fonds
d'écran, des comportements (animation et réaction aux événements), des sources de sons,...
Les classes représentant un groupe dérivent des classes javax.media.j3d.Node et
javax.media.j3d.Group.
Les classes représentant une feuille dérivent des classes javax.media.j3d.Node et
javax.media.j3d.Leaf.
java.lang.Object
javax.media.j3d.SceneGraphObject
javax.media.j3d.Node
javax.media.j3d.Group
javax.media.j3d.BranchGroup
javax.media.j3d.OrderedGroup
javax.media.j3d.SharedGroup
javax.media.j3d.Switch
javax.media.j3d.TransformGroup
javax.media.j3d.Leaf
javax.media.j3d.Background
javax.media.j3d.Behavior
javax.media.j3d.BoundingLeaf
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 10
javax.media.j3d.Clip
javax.media.j3d.Fog
javax.media.j3d.Light
javax.media.j3d.Link
javax.media.j3d.Morph
javax.media.j3d.Shape3D
javax.media.j3d.Sound
javax.media.j3d.Soundscape
javax.media.j3d.ViewPlatform
javax.media.j3d.NodeComponent
...
Globalement, une scène 3D est documentée par la représentation graphique de son arbre. Il est
vivement conseillé de dessiner un tel arbre quand vous créez vos scènes 3D : ceci permet d'effectuer
mentalement l'assemblage des différentes formes d'une scène 3D, et ce type de documentation est
bien plus lisible que des centaines de lignes de programme.
Si vous tentez d'affecter deux parents à un nœud, Java 3D déclenchera une exception
MultipleParentException.
Si vous créez des branches inutiles, Java 3D ne signalera aucune erreur à l'exécution et le
résultat ne correspondra pas à vos attentes : Ce type d'erreur survient quelques fois à la suite
d'un copier/coller trop rapide d'une instruction addChild () pour ajouter un groupe de
modification à une branche existante.
Si la modification du parent a été oubliée, vous pourrez obtenir par exemple ceci :
root n'a pas été changé en transform2. Ainsi l'instruction root.addChild (transform2);
crée une nouvelle branche inutile puisque la transformation transform2 est seule sur
cette branche, et n'est pas appliquée à transform1.
Optimisations Java 3D
Java 3D utilise principalement deux optimisations pour améliorer les performances des calculs
d'affichage 3D : La compilation des nœuds d'une scène 3D et la capacité d'un nœud.
Compilation
La compilation de chacun des nœuds d'une scène 3D permet d'obtenir une représentation interne
que Java 3D manipule de manière optimum.
La méthode compile () de la classe BranchGroup permet de compiler tous les nœuds d'un arbre à
partir de sa racine et la méthode isCompiled () de la classe SceneGraphObject de savoir si un nœud
a été compilé.
La classe Applet3D peut être optimisée en ajoutant l'instruction scene.compile (); dans la méthode
init ().
Il n'est pas possible d'interroger ou de modifier une propriété d'un nœud compilé ou vivant (nœud
utilisé dans une scène attachée à un univers), si ce nœud n'en a pas la capacité (capability ).
Cette capacité peut être donnée à un nœud non compilé et pas encore vivant en appelant la méthode
setCapability () de la classe SceneGraphObject dont héritent toutes les classes des objets
manipulés dans une scène 3D. Les classes dérivées de SceneGraphObject définissent un ensemble de
constantes de capacité ALLOW_..._READ, ALLOW_..._WRITE ou ENABLE_.... Ces constantes sont passées
en paramètre à la méthode setCapability () pour autoriser la lecture ou la modification de la
propriété correspondante d'un nœud quand il sera compilé ou vivant.
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 11
Par exemple, pour autoriser la modification d'un groupe de transformation vivant, il faut appeler
setCapability (TransformGroup.ALLOW_TRANSFORM_WRITE) sur celui-ci : cette capacité permet
d'appeler la méthode setTransform () pour changer la transformation et animer une forme 3D à
l'écran. Si le groupe de transformation n'a pas la capacité ALLOW_TRANSFORM_WRITE, une exception de
classe CapabilityNotSetException sera déclenchée à l'appel de setTransform ().
Quand la racine de l'arbre d'une scène 3D est rattaché à un univers avec la méthode addBranchGraph
(), Java 3D vérifie les différentes capacités de chacun des nœuds en utilisant la méthode
getCapability () et optimise la représentation interne des objets 3D.
Dans ce manuel, les capacités associées aux méthodes des classes dérivées de SceneGraphObject
sont indiquées entre parenthèses après chacune des méthodes auxquelles elles s'appliquent.
La classe javax.media.j3d.SceneGraphObject
Cette classe abstract est la super classe des classes Node et NodeComponent, elles-mêmes les super
classes des classes utilisées pour construire l'arbre d'une scène 3D et pour décrire la construction
géométrique et l'apparence d'une forme 3D.
La classe SceneGraphObject intègre les principes utilisés par Java 3D pour améliorer les
performances des calculs d'affichage 3D : La compilation des nœuds d'une scène 3D et la capacité
d'un nœud.
Principales méthodes
Ces méthodes renvoient true si un nœud a été compilé ou s'il est vivant (s'il appartient à une scène
3D rattachée à un univers).
Ces méthodes permettent d'interroger, d'activer ou de désactiver la capacité capability d'un nœud,
en passant en paramètre une des constantes ALLOW_..._READ, ALLOW_..._WRITE ou ENABLE_...
définies dans les classes dérivées. Ces constantes correspondent aux méthodes de ces classes qu'il
est possible d'appeler quand un nœud est compilé ou vivant. Par exemple, il faut appeler
setCapability (Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE) sur une instance de la classe
Appearance pour pouvoir appeler setColoringAttributes () et modifier la couleur d'une forme déjà
affichée.
Les méthodes setCapability () et clearCapability () doivent être appelées avant de compiler ou
afficher un nœud sinon elles déclenchent une exception RestrictedAccessException.
Un nouveau nœud n'a aucune capacité à sa création. N'activez sur un nœud que les capacités dont
vous aurez effectivement besoin une fois que ce nœud sera vivant.
Ces méthodes permettent d'interroger ou de modifier l'objet userData associé à ce nœud. Libre à
vous de l'utiliser ou non pour stocker les informations supplémentaires que vous voulez associer à
ce nœud.
La classe javax.media.j3d.Node
Cette classe abstract dérive de la classe SceneGraphObject et représente un nœud utilisé dans l'arbre
d'une scène 3D. C'est la super classe des classes Leaf et Group, et regroupe les capacités en rapport
avec le calcul des limites (bounds ), l'interception par la souris et la collision avec les autres formes
3D.
Chaque nœud ne peut être utilisé qu'une seule fois dans un arbre. En cas de besoin, il est possible
d'utiliser les méthodes cloneNode () ou cloneTree () de cette classe pour copier facilement un
nœud ou un arbre dont la racine est ce nœud.
Principales méthodes
Renvoie le parent de ce nœud ou null s'il n'en a pas. Si le nœud est compilé ou vivant une
exception de classe RestrictedAccessException est déclenchée.
Renvoie une copie de l'arbre dont arbre dont la racine est ce nœud.
Renvoie une copie de ce nœud. Cette méthode peut être éventuellement être appelée par la méthode
cloneTree (). Toutes les classes dérivées SubClassNode qui ne sont pas abstract doivent
implémenter cette méthode comme suit :
Ces méthodes permettent d'interroger ou de modifier la sensibilité d'un nœud et tous ses enfants à la
collision avec d'autres formes (true par défaut).
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 13
Ces méthodes permettent d'interroger ou de modifier la sensibilité d'un nœud et tous ces enfants
pour pouvoir l'intercepter avec la souris ou un autre périphérique (true par défaut).
La classe javax.media.j3d.Leaf
Cette classe abstract dérive des classes SceneGraphObject, Node et représente une feuille dans
l'arbre d'une scène 3D. C'est la super classe des classes représentant les formes 3D, les fonds
d'écran, les comportements, les sources de lumière, les sources sonores.
Elle ne définit qu'un constructeur par défaut public.
La classe javax.media.j3d.Group
Cette classe dérive des classes SceneGraphObject, Node et représente un groupe dans l'arbre d'une
scène 3D. C'est la super classe des classes utilisées comme groupe et comme transformation.
Ces capacités permettent d'autoriser l'interrogation et la modification de la liste des enfants dont ce
groupe est le parent.
Ces capacités permettent d'autoriser l'interrogation et la modification des limites utilisées pour la
collision de l'objet 3D représenté par ce groupe.
Principales méthodes
Ces méthodes permettent d'ajouter, d'insérer ou de modifier l'enfant child à l'indice index de ce
groupe.
Ces méthodes permettent d'interroger sur ce groupe l'enfant à l'indice index, la liste de tous les
enfants sous forme d'énumération ou le nombre d'enfants.
Ces méthodes permettent d'interroger ou de modifier les limites utilisées pour la collision de l'objet
3D représenté par ce groupe.
Exemples
La classe javax.media.j3d.BranchGroup
Cette classe dérive des classes SceneGraphObject, Node, Group et représente le groupe utilisé
comme racine de l'arbre d'une scène 3D. Un groupe de cette classe peut être utilisé plusieurs fois
dans une scène 3D mais seule une instance de cette classe peut être rattachée à une instance des
classes SimpleUniverse ou Locale utilisées dans un univers 3D, avec la méthode addBranchGraph ().
Principales méthodes
Exemples
La classe javax.media.j3d.TransformGroup
Cette classe dérive des classes SceneGraphObject, Node, Group et représente un groupe de
transformation utilisé pour appliquer une transformation géométrique à tous les enfants de ce
groupe. La description géométrique de la transformation est mémorisée dans une instance de la
classe Transform3D.
Constructeurs
public TransformGroup ()
public TransformGroup (Transform3D transform)
Principales méthodes
Exemples
La classe javax.media.j3d.Transform3D
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 15
Principaux constructeurs
public Transform3D ()
public Transform3D (Transform3D transform)
Ces constructeurs créent une instance de Transform3D initialisée avec une transformation identité ou
par recopie de transform.
Principales méthodes
Modifie cette transformation en une transformation identité (qui n'a aucun effet).
Modifie cette transformation en une translation utilisant les valeurs sur les trois axes x, y, z de
transl.
Ces méthodes permettent de modifier cette transformation en une rotation d'angle angle radians
autour de l'axe x, y ou z. Le sens positif de rotation est donné par la figure de l'orientation d'un
repère 3D.
Ces méthodes permettent de modifier cette transformation soit en une homothétie de facteur scale
sur les trois axes x, y, z (agrandissement si scale > 1 ou réduction si scale < 1), soit en une
homothétie utilisant les facteurs sur les trois axes x, y, z de scales.
Les transformations sont toujours relatives au centre du repère. N'oubliez pas ceci surtout pour les
rotations ou les homothéties appliquées après une translation !
Les méthodes rotX (), rotY (), rotZ (), setScale () et setTranslation () appelées sur une
même instance de la classe Transform3D ne sont pas cumulatives et chaque appel à l'une des
méthodes rotX (), rotY (), rotZ () annule la transformation précédente. Par sécurité, il vaut
mieux créer deux instances différentes des classes Transform3D et TransformGroup pour cumuler
deux transformations différentes ou utiliser la méthode mul ().
Exemples
Certaines des classes fournies avec Java 3D sont utiles pour représenter les vecteurs, les
coordonnées d'un point ou les matrices. Comme ces classes ont un intérêt pour le calcul algébrique
plus vaste que le seul domaine de la 3D, elles appartiennent à un package indépendant
javax.vecmath.
Comme le montre la hiérarchie de ce package ci-dessous, la plupart de ces classes manipulent des
coordonnées ayant 2, 3 ou 4 valeurs de type float ou double, voir de type byte ou int pour
certaines classes. Le nombre de valeurs stockées et leur type est indiqué dans le nom de la classe :
par exemple, une instance de classe Point4f stocke 4 valeurs de type float et une instance classe
Vector3d stocke 3 valeurs de type double.
java.lang.Object
javax.vecmath.AxisAngle4d
javax.vecmath.AxisAngle4f
javax.vecmath.GMatrix
javax.vecmath.GVector
javax.vecmath.Matrix3d
javax.vecmath.Matrix3f
javax.vecmath.Matrix4d
javax.vecmath.Matrix4f
javax.vecmath.Tuple2d
javax.vecmath.Point2d
javax.vecmath.Vector2d
javax.vecmath.Tuple2f
javax.vecmath.Point2f
javax.vecmath.TexCoord2f
javax.vecmath.Vector2f
javax.vecmath.Tuple3b
javax.vecmath.Color3b
javax.vecmath.Tuple3d
javax.vecmath.Point3d
javax.vecmath.Vector3d
javax.vecmath.Tuple3f
javax.vecmath.Color3f
javax.vecmath.Point3f
javax.vecmath.TexCoord3f
javax.vecmath.Vector3f
javax.vecmath.Tuple3i
javax.vecmath.Point3i
javax.vecmath.Tuple4b
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 17
javax.vecmath.Color4b
javax.vecmath.Tuple4d
javax.vecmath.Point4d
javax.vecmath.Quat4d
javax.vecmath.Vector4d
javax.vecmath.Tuple4f
javax.vecmath.Color4f
javax.vecmath.Point4f
javax.vecmath.Quat4f
javax.vecmath.Vector4f
javax.vecmath.Tuple4i
javax.vecmath.Point4i
Les classes Point3f, Vector3f et Color3f représentant un point et un vecteur, ainsi que leur super
classe Tuple3f utilisant le type float sont décrites ci-dessous brièvement. La classe Tuple3d et ses
classes dérivées Point3d et Vector3d utilisées par certaines méthodes Java 3D décrites dans ce
manuel déclarent les mêmes champs et méthodes.
La classe javax.vecmath.Tuple3f
Cette classe abstract qui implémente l'interface java.io.Serializable est la super classe des
classes Point3f, Vector3f, Color3f et TexCoord3f. Elle mémorise 3 coordonnées dans les champs
public x, y, z de type float et fournit un ensemble de méthodes pour effectuer des opérations
mathématiques de base (addition, soustraction, valeur opposée, encadrement,...).
Champs
public float x
public float y
public float z
La classe javax.vecmath.Point3f
Cette classe dérive de la classe Tuple3f et représente les coordonnées (x, y, z) d'un point 3D.
Principaux constructeurs
public Point3f ()
public Point3f (float x, float y, float z)
public Point3f (float [ ] p1)
public Point3f (Point3f point)
Principales méthodes
Ces méthodes renvoient la distance ou la distance au carré entre ce point et le point p1.
Exemples
La classe javax.vecmath.Vector3f
Cette classe dérive de la classe Tuple3f et représente les coordonnées (x, y, z) d'un vecteur 3D.
Constructeurs
vendredi 13 octobre 2000 Du C/C++ à Java : Java 3D Page: 18
public Vector3f ()
public Vector3f (float x, float y, float z)
public Vector3f (float [ ] v1)
public Vector3f (Vector3f vector)
Principales méthodes
Normalise ce vecteur, pour que ce vecteur soit de même direction avec une norme égale à 1.
Exemples
La classe javax.vecmath.Color3f
Cette classe dérive de la classe Tuple3f et représente les composantes RGB Rouge, Vert, Bleu d'une
couleur exprimées entre 0.f et 1.f (correspondant au champs x, y, z).
Constructeurs
public Color3f ()
public Color3f (float r, float g, float b)
public Color3f (float [ ] c1)
public Color3f (Color3f color)
Ces constructeurs créent une couleur initialisée à partir des paramètres r, g, b, c1 ou color. Le
constructeur par défaut initialise à 0 les champs x, y et z de la couleur.
Exemples
Objets 3D
Suite à la présentation des notions utiles en Java 3D, ce chapitre décrit comment créer des objets 3D
par l'assemblage de formes de base, la description de formes géométriques, l'application d'un effet
3D à un texte ou l'importation de scènes existantes.
Les objets 3D affichés avec Java 3D sont manipulés soit sous forme de feuilles ajoutées à l'arbre
d'une scène 3D, soit sous forme de groupes contenant des feuilles . Les feuilles sont des
formes 3D de classe Shape3D. Cette classe décrit la construction géométrique de l'objet et ses
différents attributs d'apparence.
Pour simplifier la construction de scènes 3D, le package com.sun.j3d.utils.geometry fournit les
classes utilitaires suivantes :
Les classes Box, Sphere, Cylinder et Cone représentant les objets de formes parallélépipède,
sphère, cylindre ou cône.
La classe ColorCube représentant un cube avec 6 faces de couleur différente.
La classe Text2D permettant d'afficher un texte sans épaisseur (le texte 3D est construit
géométriquement avec la classe javax.media.j3d.Text3D).
Voici l'applet SimpleObjects mettant en scène les quatre objets de forme simple et de classes Box,
Sphere, Cylinder et Cone sur un fond d'écran blanc :
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.applet.MainFrame;
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 2
// Création d'un fond blanc pour apercevoir les objets qui sont noirs par défaut
Background background = new Background (1, 1, 1);
background.setApplicationBounds (new BoundingBox ());
return root;
}
La classe Clown est aussi un exemple plus complexe d'assemblage de formes simples représentant un
clown.
La classe javax.media.j3d.Shape3D
Cette classe dérive des classes SceneGraphObject, Node, Leaf et représente une feuille utilisée
comme forme 3D dans l'arbre d'une scène 3D. A une forme 3D sont associés une construction
géométrique et des attributs d'apparence appliqués à la forme 3D.
A partir de la version 1.2 de Java 3D, il est possible d'associer plusieurs constructions géométriques
à une même forme 3D si elles sont équivalentes (il n'est pas possible de mélanger une description
de texte 3D avec celle d'une forme géométrique).
Ces capacités permettent d'autoriser l'interrogation et la modification des limites utilisées pour la
collision de cette forme 3D.
Constructeurs
public Shape3D ()
public Shape3D (Geometry geometry)
public Shape3D (Geometry geometry, Appearance appearance)
Principales méthodes
Ces méthodes compatibles permettent d'interroger sur cette forme 3D la construction géométrique
d'indice index, la liste de toutes les constructions géométriques sous forme d'énumération ou son
nombre (compatible à partir de la version 1.2 de Java 3D).
Retire la construction géométrique de cette forme 3D (compatible à partir de la version 1.2 de Java
3D).
Ces méthodes permettent d'interroger ou de modifier les attributs d'apparence utilisées par cette
forme 3D.
Ces méthodes permettent d'interroger ou de modifier les limites utilisées pour la collision de cette
forme 3D.
Exemples
La classe com.sun.j3d.utils.geometry.ColorCube
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Shape3D et représente un cube centré
sur l'origine du repère dont les 6 faces sont de couleur différente. Elle est surtout utile pour des fins
de démonstration.
Constructeurs
public ColorCube ()
public ColorCube (double scale)
Ces constructeurs permettent de créer un cube dont les deux coins opposés ont pour coordonnées
(-scale, -scale, -scale) et (scale, scale, scale). Par défaut, scale est égal à 1.
Exemples
La classe com.sun.j3d.utils.geometry.Primitive
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 5
Cette classe abtract dérive des classes SceneGraphObject, Node, Group et est utilisée comme super
classe des classes Box, Sphere, Cylinder et Cone du package com.sun.j3d.utils.geometry. En tant
que groupe, elle représente un conteneur de plusieurs formes 3D qui constituent un objet 3D et
regroupe un ensemble de paramètres applicables à chacune de ces formes.
Par exemple, elle comporte une méthode setAppearance (Appearance appearance) qui permet de
modifier les attributs d'apparence de toutes les formes 3D qu'elle regroupe.
Principaux champs
Ces constantes sont passées en paramètre aux constructeurs des classes dérivées pour que le
générateur de construction géométrique calcule les normales (utiles pour l'éclairage) ou les
coordonnées utilisées pour appliquer les textures. GEOMETRY_NOT_SHARED indique qu'une construction
géométrique séparée doit être créée pour les formes 3D de cette instance.
Cette constante est passée en paramètre aux constructeurs des classes dérivées pour activer les
capacités ALLOW_APPEARANCE_READ and ALLOW_APPEARANCE_WRITE sur les instances de la classe Shape3D
créées.
Cette constante est passée en paramètre aux constructeurs des classes dérivées pour activer la
capacité ALLOW_INTERSECT sur les constructions géométriques créées.
Principales méthodes
Renvoie la forme 3D générée à l'indice partid. Les classes dérivées de la classe Primitive définisse
un ensemble de constantes égales aux indices des différentes formes 3D composant un objet.
Ces méthodes permettent de modifier les attributs d'apparence de toutes les formes 3D dont le
parent est ce groupe. La méthode sans paramètre leur applique des attributs de classe Material
utilisant une couleur noire sans éclairage et blanche sous éclairage.
La classe com.sun.j3d.utils.geometry.Box
Cette classe dérive des classes SceneGraphObject, Node, Group, Primitive et représente un
parallélépipède centré sur l'origine du repère. Elle implémente les méthodes getShape (int partid)
et setAppearance (Appearance appearance) de la classe Primitive.
Champs
Ces constantes désignent les différentes faces du cube (respectivement devant, derrière, droite,
gauche, haut et bas). La classe Box utilise une forme 3D pour chaque face et ces constantes sont
utilisées en paramètre de la méthode getShape () pour interroger chaque instance de Shape3D.
Constructeurs
public Box ()
public Box (float xdim, float ydim, float zdim, Appearance appearance)
public Box (float xdim, float ydim, float zdim, int primflags, Appearance appearance)
Ces constructeurs permettent de créer un parallélépipède dont les deux coins opposés ont pour
coordonnées (-xdim, -ydim, -zdim) et (xdim, ydim, zdim). primflags peut être égal à une des
constantes décrites dans la classe Primitive ou à une combinaison de ces constantes.
Par défaut, xdim, ydim, zdim sont égaux à 1, primflags à GENERATE_NORMALS et appearance à null ce
qui crée un parallélépipède de couleur blanche uniquement sous éclairage.
Exemple
La classe com.sun.j3d.utils.geometry.Sphere
Cette classe dérive des classes SceneGraphObject, Node, Group, Primitive et représente une sphère
centrée sur l'origine du repère. Elle implémente les méthodes getShape (int partid) et
setAppearance (Appearance appearance) de la classe Primitive.
Champs
Cette constante est utilisée en paramètre de la méthode getShape () pour interroger l'instance de
Shape3D représentant la sphère.
Constructeurs
public Sphere ()
public Sphere (float radius, Appearance appearance)
public Sphere (float radius, int primflags, Appearance appearance)
public Sphere (float radius, int primflags, int divisions)
public Sphere (float radius, int primflags, int divisions, Appearance appearance)
Ces constructeurs permettent de créer une sphère de rayon radius. La construction géométrique de
la sphère est un assemblage de quadrilatères et divisions représente le nombre divisions utilisées
sur chaque axe pour décomposer la sphère en facettes. primflags peut être égal à une des constantes
décrites dans la classe Primitive ou à une combinaison de ces constantes.
Par défaut, radius est égal à 1, divisions à 15, primflags à GENERATE_NORMALS et appearance à null
ce qui crée une sphère de couleur blanche uniquement sous éclairage.
Exemples
La classe com.sun.j3d.utils.geometry.Cylinder
Cette classe dérive des classes SceneGraphObject, Node, Group, Primitive et représente un cylindre
centré sur l'origine du repère, dont l'axe central est confondu avec l'axe y. Elle implémente les
méthodes getShape (int partid) et setAppearance (Appearance appearance) de la classe
Primitive.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 7
Champs
Ces constantes désignent les 3 parties du cylindre (respectivement la partie cylindrique, et les
disques bas et haut). Ces constantes sont utilisées en paramètre de la méthode getShape () pour
interroger chaque instance de Shape3D du cylindre.
Constructeurs
public Cylinder ()
public Cylinder (float radius, float height)
public Cylinder (float radius, float height, Appearance appearance)
public Cylinder (float radius, float height, int primflags, Appearance appearance)
public Cylinder (float radius, float height, int primflags,
int xdivision, int ydivision, Appearance appearance)
Exemples
La classe com.sun.j3d.utils.geometry.Cone
Cette classe dérive des classes SceneGraphObject, Node, Group, Primitive et représente un cône
centré sur l'origine du repère, dont l'axe central est confondu avec l'axe y. L'origine du repère est à
mi hauteur de la hauteur du cône. La classe Cone implémente les méthodes getShape (int partid)
et setAppearance (Appearance appearance) de la classe Primitive.
Champs
Ces constantes désignent les 3 parties du cylindre (respectivement la partie conique et le disque
inférieur). Ces constantes sont utilisées en paramètre de la méthode getShape () pour interroger
chaque instance de Shape3D du cylindre.
Constructeurs
public Cone ()
public Cone (float radius, float height)
public Cone (float radius, float height, int primflags, Appearance appearance)
public Cone (float radius, float height, int primflags,
int xdivision, int ydivision, Appearance appearance)
Exemples
Un ensemble de points isolés. Les classes PointArray et IndexedPointArray sont utilisées pour
stocker ces points.
Un ensemble de lignes. Les classes LineArray, LineStripArray, IndexedLineArray et
IndexedLineStripArray sont utilisées pour stocker les extrémités de ces lignes.
Une surface plus ou moins complexe constituée par l'assemblage de facettes triangulaires ou
de quadrilatères. Les classes dérivées de GeometryArray sont utilisées pour stocker les sommets
(vertex /vertices en anglais) de ces facettes.
Un texte avec une profondeur lui donnant un effet 3D (classe Text3D).
Une image (raster ) située à un point de l'espace (classe Raster).
Les classes qui permettent d'utiliser ces types de construction géométrique héritent toutes de la
classe Geometry :
java.lang.Object
javax.media.j3d.SceneGraphObject
javax.media.j3d.NodeComponent
javax.media.j3d.Geometry
javax.media.j3d.CompressedGeometry
javax.media.j3d.GeometryArray
javax.media.j3d.PointArray
javax.media.j3d.LineArray
javax.media.j3d.TriangleArray
javax.media.j3d.QuadArray
javax.media.j3d.GeometryStripArray
javax.media.j3d.LineStripArray
javax.media.j3d.TriangleStripArray
javax.media.j3d.TriangleFanArray
javax.media.j3d.IndexedGeometryArray
javax.media.j3d.IndexedPointArray
javax.media.j3d.IndexedLineArray
javax.media.j3d.IndexedTriangleArray
javax.media.j3d.IndexedQuadArray
javax.media.j3d.IndexedGeometryStripArray
javax.media.j3d.IndexedLineStripArray
javax.media.j3d.IndexedTriangleStripArray
javax.media.j3d.IndexedTriangleFanArray
javax.media.j3d.Raster
javax.media.j3d.Text3D
Chacune des classes CompressedGeometry, GeometryArray, Raster et Text3D n'est pas équivalente à
l'autre dans le sens où il n'est pas possible de définir une forme 3D avec la classe Shape3D en
mélangeant les classes GeometryArray et Text3D par exemple. Dans ce cas, il faut créer deux
instances de Shape3D différentes.
Les classes qui dérivent de la classe GeometryArray vous donnent un large choix d'algorithmes pour
décrire comment sont construites les facettes d'une surface à partir de la liste de ses sommets :
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 9
Les classes de formes de base suffisent dans la plupart des cas mais l'utilisation des classes
ci-dessus permettent d'imaginer n'importe quelle surface et d'optimiser sa représentation.
Vous pouvez aussi utiliser la classe com.sun.j3d.utils.geometry.GeometryInfo qui simplifie la
programmation des constructions géométriques. Le constructeur de cette classe prend en paramètre
une constante désignant l'algorithme de construction des facettes à utiliser avec la liste des
sommets. Ces algorithmes correspondent aux classes dérivant de la classe GeometryArray comme
suit :
javax.media.j3d.Geometry javax.media.j3d.Geometry
javax.media.j3d.GeometryArray javax.media.j3d.GeometryArray
javax.media.j3d.IndexedGeometr
javax.media.j3d.PointArray javax.media.j3d.IndexedPo
javax.media.j3d.LineArray javax.media.j3d.IndexedLi
javax.media.j3d.TriangleArray javax.media.j3d.IndexedTr
javax.media.j3d.QuadArray javax.media.j3d.IndexedQu
javax.media.j3d.GeometryStripArray javax.media.j3d.IndexedGe
javax.media.j3d.LineStripArray javax.media.j3d.Inde
javax.media.j3d.TriangleStripArray javax.media.j3d.Inde
javax.media.j3d.TriangleFanArray javax.media.j3d.Inde
Toutes les classes qui héritent de la classe IndexedGeometryArray ont le même nom préfixé par
Indexed que les classes qui dérivent directement de la classe GeometryArray (par exemple
IndexedQuadArray pour QuadArray). Ces classes utilisent leur liste de sommets en respectant le même
algorithme que leur classes "sœur" mais l'ensemble des sommets est un tableau d'indices au lieu
d'être un tableau de coordonnées, comme le montre la figure ci-dessous.
Cette figure montre deux manières de construire les 4 faces d'une pyramide, l'une avec la classe
TriangleFanArray l'autre avec la classe IndexedTriangleFanArray. La pyramide peut être
décomposée en deux éventails, l'un avec trois triangles centré en s0 et l'autre avec un seul triangle
centré en s1.
L'instance de la classe TriangleFanArray n'a besoin que d'un seul tableau avec les coordonnées des 8
sommets pour décrire les faces. L'instance de la classe IndexedTriangleFanArray a besoin de deux
tableaux :
L'usage des constructions géométriques indicées est souvent plus complexe à programmer mais
permet d'économiser en mémoire et sur certains calculs des sommets pour Java 3D. Dans le cas de
la pyramide, la moitié de ces calculs est économisée car les calculs sur s1, s2 et s3 ne seront
exécutés qu'une seule fois avec la classe IndexedTriangleFanArray.
Cette complexité est simplifiée grâce à la classe com.sun.j3d.utils.geometry.GeometryInfo qui
calcule automatiquement une instance de IndexedGeometryArray à partir d'une construction
géométrique non indicée.
Voici l'applet Pyramid qui construit une pyramide colorée. Une couleur différente a été donnée à
chaque sommet, ce qui donne par défaut cet effet de dégradé obtenu par interpolation des couleurs.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 11
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.applet.MainFrame;
return root;
}
L'ordre dans lequel sont cités les sommets des facettes est important.
Pour qu'une facette extérieure soit visible, ses sommets doivent respecter le sens direct, c'est à dire
qu'ils doivent être énumérés dans le sens inverse des aiguilles d'une montre quand la facette est
face à vous.
La classe javax.media.j3d.NodeComponent
Cette classe abstract dérive de la classe SceneGraphObject. C'est la super classe des classes
Geometry et Appearance et de toutes les classes d'attributs. Elle définit quelques méthodes utilisées
pour la duplication des classes dérivées.
Contrairement aux instances de la classe Node, les objets de classes NodeComponent peuvent être
référencés plusieurs fois : une même instance de construction géométrique peut être utilisée par
plusieurs formes 3D différentes.
La classe javax.media.j3d.Geometry
Cette classe abstract dérive des classes SceneGraphObject et NodeComponent. C'est la super classe de
toutes les classes de constructions géométriques GeometryArray, Text3D, Raster, CompressedGeometry
qui peuvent être passées en paramètre aux méthodes addGeometry (), insertGeometry () et
setGeometry () de la classe Shape3D.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 12
Cette capacité permet d'autoriser l'utilisation de cette construction géométrique dans les calculs
d'intersection.
La classe javax.media.j3d.GeometryArray
Cette classe abstract dérive des classes SceneGraphObject, NodeComponent, Geometry. Toutes les
classes qui en héritent permettent de créer une construction géométrique sous forme d'un ensemble
de points, de lignes, de facettes triangulaires ou de quadrilatères.
Cette classe peut stocker au plus quatre types de données différentes :
Toutes les valeurs (coordonnées, couleurs,...) passées en paramètre à une instance de GeometryArray
sont recopiées dans des structures internes.
Ces capacités permettent d'autoriser la lecture ou la modification des coordonnées des sommets, de
leur couleur, de leur normale ou leurs coordonnées de texture.
La classe com.sun.j3d.utils.geometry.GeometryInfo
Cette classe utilitaire permet de générer des constructions géométriques de classe GeometryArray.
Comme pour cette classe, elle peut stocker les coordonnées des sommets, leur couleur, leur normale
et leurs coordonnées de texture. Accompagnée des classes NormalGenerator, Triangulator,
Stripifier, elle évite la programmation des tâches suivantes au moment de la création de
constructions géométriques :
Une fois renseignées les coordonnées des sommets et éventuellement les autres paramètres d'une
construction géométrique, une instance de classe GeometryArray configurée avec toutes les données
peut être générée grâce aux méthodes getGeometryArray () et getIndexedGeometryArray ().
Champs
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 13
Ces constantes sont passées en paramètre au constructeur de la classe GeometryInfo pour spécifier
quel algorithme de construction géométrique est utilisé pour le tableau des sommets.
La constante POLYGON_ARRAY indique que les sommets représentent le contour d'une surface
polygonale. Il n'est pas obligatoire que cette surface soit plane et la méthode setContourCounts ()
permet d'indiquer si cette surface comporte des trous. La surface polygonale est automatiquement
décomposée en facettes triangulaires.
Constructeur
Crée une instance de la classe GeometryInfo. primitive peut être égal à TRIANGLE_ARRAY, QUAD_ARRAY,
TRIANGLE_FAN_ARRAY, TRIANGLE_STRIP_ARRAY, POLYGON_ARRAY pour indiquer quel algorithme utilisé.
Principales méthodes
Ces méthodes permettent de modifier la liste des sommets des facettes. Les valeurs passées en
paramètre sont recopiées.
Ces méthodes permettent de modifier la couleur des sommets. Les attributs d'apparence positionnés
sur la forme 3D permettent d'interpréter cette information de couleur de différentes façons et de
positionner une couleur directement sur une forme 3D. Par défaut, les facettes sont colorées par
interpolation entre les différentes couleurs de ses sommets.
Ces méthodes permettent de modifier les normales des sommets. Vous pouvez aussi utiliser la
classe NormalGenerator pour éviter de programmer leur calcul. Toutes les normales doivent avoir
une longueur (norme) égale à 1.
Ces méthodes renvoient une instance des classes GeometryArray ou IndexedGeometryArray à passer
en paramètre aux méthodes addGeometry (), insertGeometry () et setGeometry () de la classe
Shape3D.
Si l'instance de GeometryInfo a été créée avec POLYGON_ARRAY comme algorithme, la surface décrite
par les sommets sera d'abord transformée en facettes triangulaires.
Exemples
La classe com.sun.j3d.utils.geometry.NormalGenerator
Cette classe utilitaire permet de calculer les normales aux sommets d'une construction géométrique
de classe GeometryInfo. Pour rappel, la normale à un sommet est le vecteur dont la direction est
orthogonale au plan auquel appartient le sommet. Les normales sont utiles en 3D pour calculer les
effets d'éclairage.
La méthode generateNormals () utilise un angle de pli (crease ) pour générer les normales : soit
alpha l'angle entre les normales des sommets en commun de deux facettes adjacentes : si alpha <
creaseAngle une normale moyenne est utilisée plutôt que les deux normales géométriques, ce qui
donne un effet visuel adoucissant l'angle. Pour obtenir un effet de pli entre toutes les facettes,
utilisez creaseAngle égal à 0.
Principal constructeur
public NormalGenerator ()
public NormalGenerator (double creaseAngle)
Ces constructeurs créent une instance de la classe NormalGenerator. creaseAngle est l'angle de pli en
radian compris entre 0 et PI (par défaut égal à 44° en radian).
Principale méthode
Exemples
Voici un exemple de classe réalisant une construction géométrique, à partir d'un profil projeté
autour d'un axe central. Ceci correspond aux objets fabriquées sur un tour de potier.
La classe AxisShape calcule les facettes de ce type de surface en utilisant un tableau points de
points 2D comme profil et l'axe y comme axe de symétrie. Le profil est appliqué tous les 2 PI /
divisions radians sur tout un tour pour obtenir les sommets des facettes la forme 3D. L'algorithme
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 15
de construction géométrique des quadrilatères est utilisé ici mais il est aussi possible d'utiliser
d'autres algorithmes comme celui en bande de la classe TriangleStripArray.
Les classes GeometryInfo et NormalGenerator sont finalement utilisées pour simplifier le calcul des
normales et obtenir une instance de IndexedGeometryArray sans peine.
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
if ( points [0].x != 0
|| points [points.length - 1].x != 0)
throw new IllegalArgumentException ("First and last point x must be 0");
// Implémentation de la copie
public Node cloneNode (boolean forceDuplicate)
{
AxisShape node = new AxisShape (points, divisions);
node.duplicateNode (this, forceDuplicate);
return node;
}
}
Voici l'applet AxisShapeDemo qui crée la forme 3D d'un vase à partir de son profil pour tester la
classe AxisShape.
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
return root;
}
La classe javax.media.j3d.Text3D
Cette classe dérive des classes SceneGraphObject, NodeComponent, Geometry et permet de créer une
construction géométrique de texte 3D à partir d'une chaîne de caractères. Ce texte est dessiné avec
une police de caractères 3D de classe Font3D à la position donnée dans le plan (x,y) et élevé suivant
l'axe z.
Voir aussi l'applet TextTranslation pour un exemple de génération de texte sur plusieurs lignes.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 17
Champs
Ces constantes sont utilisées pour aligner le texte sur son premier caractère, au centre ou sur son
dernier caractère par rapport à la position du texte.
Ces constantes sont utilisées pour indiquer le sens d'écriture du texte (vers la gauche, la droite, le
haut ou le bas).
Constructeurs
public Text3D ()
public Text3D (Font3D font3D)
public Text3D (Font3D font3D, String string)
public Text3D (Font3D font3D, String string, Point3f position)
public Text3D (Font3D font3D, String string, Point3f position, int alignment, int path)
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier la chaîne de caractères affichée par ce texte
3D.
Ces méthodes permettent d'interroger ou de modifier la position de ce texte 3D. Le texte est aligné
sur le premier caractère, au centre ou sur le dernier caractère par rapport à ce point suivant
l'alignement utilisé.
Si le sens d'écriture est horizontal, la ligne de base du texte est alignée verticalement sur ce point. Si
le sens d'écriture est vertical, le texte est alignée à gauche de ce point.
Ces méthodes permettent d'interroger ou de modifier l'alignement de ce texte 3D. alignment peut
être égal à ALIGN_FIRST, ALIGN_CENTER ou ALIGN_LAST.
Ces méthodes permettent d'interroger ou de modifier le sens d'écriture de ce texte 3D. path peut être
égal à PATH_LEFT, PATH_RIGHT, PATH_UP ou PATH_DOWN.
Ces méthodes permettent d'interroger ou de modifier l'espacement entre caractères de ce texte 3D.
Cette distance est l'espace supplémentaire ajouté entre chaque caractère du texte. Une valeur de 1
correspond au plus large des caractères dans la police utilisée. Par défaut, cet espacement vaut 0.
Met à jour le paramètre bounds avec les coordonnées de la boite englobante de ce texte 3D.
import java.awt.Font;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
return root;
}
Autre exemple
Applet TextTranslation .
La classe javax.media.j3d.Font3D
Cette classe représente une police de caractères 3D, créée à partir d'une police de caractères AWT
de classe java.awt.Font et une instance de FontExtrusion qui définit le profil des caractères dans la
profondeur. La hauteur du texte 3D est celle de la police de caractères utilisée à l'instantiation de la
classe java.awt.Font.
Principal constructeur
Exemple
La classe javax.media.j3d.FontExtrusion
Cette classe est utilisée par la classe Font3D pour définir le profil des caractères dans la profondeur
(suivant l'axe z).
Principaux constructeurs
public FontExtrusion ()
public FontExtrusion (java.awt.Shape extrusionShape)
Ces constructeurs définissent le profil des caractères. Par défaut, c'est une ligne de 0.2 unité de
haut.
Exemple
Cette classe dérive des classes SceneGraphObject, Node, Leaf et représente le fond d'écran d'une
scène 3D. L'ajout d'une instance de Background dans l'arbre d'une scène 3D permet de changer la
couleur noir de fond utilisée par défaut. Ce fond est toujours dessiné en premier avant le reste de la
scène 3D, et peut être une couleur unie ou une image associée éventuellement à une sous-scène. Il
est représenté par un nœud dans l'arbre d'une scène 3D.
Les fonds d'écran sont appliqués à un espace limité. Cette limite (application bounds ), null par
défaut, permet d'avoir plusieurs fonds d'écran actifs sur des zones différentes.
Il faut obligatoirement déterminer cette limite pour que le fond d'écran soit pris en compte.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 20
Ces capacités permettent d'autoriser la lecture ou la modification des limites dans lesquelles est
appliqué le fond d'écran.
Constructeurs
public Background ()
public Background (Color3f color)
public Background (float r, float g, float b)
public Background (ImageComponent2D image)
public Background (BranchGroup branch)
Ces constructeurs permettent de créer un fond d'écran de couleur unie color (ou r g b), utilisant
une image image ou une sous-scène branch. Par défaut, color est égal au noir, image et branch à
null.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'image du fond d'écran. Si une image est
utilisée, la couleur n'est plus prise en compte.
Ces méthodes permettent d'interroger ou de modifier l'arbre de la sous-scène du fond d'écran. Cette
scène est dessinée par dessus la couleur ou l'image de fond. Elle est projetée à l'infini et doit tenir
dans une sphère d'une unité de rayon.
Ces méthodes permettent d'interroger ou de modifier la limite dans lesquelles est appliqué le fond
d'écran.
Exemples
SunEarthMoonMotion .
La classe javax.media.j3d.Bounds
Cette classe abstract est la super classe des classes BoundingBox, BoundingSphere et
BoundingPolytope. Ces classes représentent en Java 3D les limites spatiales utilisées notamment
pour les fonds d'écran, l'éclairage et les comportements. La classe Bounds déclare un ensemble de
méthodes abstract non décrites ici qui permettent de calculer la combinaison ou l'intersection d'un
ensemble de limites.
La classe javax.media.j3d.BoundingBox
Cette classe dérive de la classe Bounds et représente une limite parallélépipédique spécifiée par deux
coins opposés de la limite.
Principaux constructeurs
public BoundingBox ()
public BoundingBox (Point3d lower, Point3d upper)
Ces constructeurs permettent de créer une limite parallélépipédique dont les deux points opposés
sont lower et upper (par défaut égaux aux points (-1,-1,-1) et (1,1,1)).
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier les deux points opposés de cette limite.
Exemple
La classe javax.media.j3d.BoundingSphere
Principaux constructeurs
public BoundingSphere ()
public BoundingSphere (Point3d center, double radius)
Ces constructeurs permettent de créer une limite sphérique centrée en center et de rayon radius
(par défaut égaux à l'origine et 1).
Principales méthodes
Exemples
La classe javax.media.j3d.BoundingLeaf
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 22
Cette classe dérive des classes SceneGraphObject, Node, Leaf. Ce type de nœud qui mémorise une
limite spatiale de classe Bounds peut être partagé par un ensemble de nœuds d'un arbre 3D ayant
besoin d'une limite, ce qui évite à chaque nœud de cet ensemble de créer sa propre instance de
limite. Par exemple, une instance de la classe BoundingLeaf peut représenter les limites d'une pièce
qui seront utilisées par les sources lumineuses et sonores de cette pièce. Une instance de cette classe
est représentée par un nœud dans l'arbre d'une scène 3D.
N'oubliez pas d'ajouter les instances de la classe BoundingLeaf à l'arbre de la scène 3D, pour que
Java 3D puisse prendre en compte les groupes de transformations parents susceptibles de modifier
la limite associée.
Constructeurs
public BoundingLeaf ()
public BoundingLeaf (Bounds region)
Ces constructeurs permettent de créer une limite (region est égal à null par défaut).
Principales méthodes
Exemple
Applet Clock3D .
L'importation de scènes 3D
La programmation d'une scène 3D est souvent longue et n'est pas toujours la manière la plus simple
pour subvenir à tous les besoins d'un programme. Java 3D fournit le package com.sun.j3d.loaders
avec les interfaces Loader et Scene dont l'implémentation vise à permettre l'importation de fichiers
créés avec d'autres logiciels 3D : ceci permet de mettre en scène dans Java 3D des objets 3D issus
de bibliothèques existantes ou de créer vos objets avec un logiciel 3D pour l'importer ensuite dans
Java 3D.
Java 3D fournit deux classes implémentant ces interfaces :
Les éditeurs de logiciels 3D fournissent le même type de classe pour la plupart des autres formats
de fichiers.
Voici l'applet ObjectFileDemo qui affiche le fichier p51_mustang.obj fourni avec les exemples de
Java 3D. Sans effet d'éclairage, les formes 3D définies dans ce fichier sont noires, c'est pourquoi un
fond bleu est ajouté à la scène 3D.
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 23
import javax.media.j3d.*;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.loaders.Scene;
import com.sun.j3d.utils.applet.MainFrame;
// Création d'un fond bleu ciel pour apercevoir l'avion qui est noir par défaut
Background background = new Background (0.4f, 0.6f, 0.8f);
background.setApplicationBounds (new BoundingBox ());
root.addChild (background);
try
{
// Chargement du fichier p51_mustang.obj
ObjectFile loader = new ObjectFile ();
Scene scene = loader.load ("p51_mustang.obj");
BranchGroup objectRoot = scene.getSceneGroup ();
transformGroup.addChild (objectRoot);
root.addChild (transformGroup);
}
catch (Exception e)
{
System.err.println (e);
}
return root;
}
L'interface com.sun.j3d.loaders.Loader
vendredi 13 octobre 2000 Du C/C++ à Java : Objets 3D Page: 24
Cette interface est implémentée pour importer des sources de données 3D.
Les exceptions Runtime de classes IncorrectFormatException et ParsingErrorException sont
déclenchées par les méthodes load () pour indiquer respectivement que les données ne sont pas au
format attendu ou que la lecture des données à rencontrer un problème.
Champs
Ces constantes ou une combinaison de ces constantes sont passées en paramètre à la méthode
setFlags () pour indiquer quelles parties de la scène doit être chargée.
Méthodes
Ces méthodes permettent d'interroger ou de modifier les indicateurs de chargement flags. flags
peut être égal à l'une des constantes LOAD_... ou à la combinaison de ces constantes avec l'opérateur
|, et indiquent quelle partie de la scène doit être chargée.
La méthode load () permettent de charger une scène à partir du fichier fileName. Les méthodes
getBasePath () et setBasePath () permettent d'interroger ou de modifier le chemin utilisé pour
trouver les éventuels fichiers référencés par fileName. Par défaut, ce chemin est le même répertoire
que celui du fichier fileName.
La méthode load () permettent de charger une scène à partir de l'URL url. Les méthodes
getBaseUrl () et setBaseUrl () permettent d'interroger ou de modifier le chemin utilisé pour
trouver les éventuels fichiers référencés par url. Par défaut, ce chemin est le même répertoire que
celle de l'url.
Charge une scène à partir de la source de données reader. Le chemin utilisé pour trouver les
éventuels fichiers référencés par reader est le répertoire courant de l'utilisateur (issu de la propriété
système user.dir).
Exemple
Applet ObjectFileDemo .
L'interface com.sun.j3d.loaders.Scene
Cette interface permet d'interroger les différentes parties d'une scène 3D importée par les méthodes
load () de la classe Loader.
Méthodes
Ces méthodes permettent d'interroger les différentes parties de la scène 3D. L'instance de la classe
BranchGroup renvoyée par getSceneGroup () peut être directement rattachée à une instance de
SimpleUniverse.
Exemple
Applet ObjectFileDemo .
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 1
Ce chapitre décrit comment modifier les attributs d'apparence des objets 3D que vous avez créés.
Ceux-ci permettent de modifier leurs couleurs, leur transparence, les paramètres de rendu et leur
texture.
Le tableau suivant présente la liste détaillée de tous les attributs d'apparence décrits dans ce
chapitre, avec leur valeur par défaut :
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 2
VALEUR
ATTRIBUT CLASSE PROPRIÉTÉ
PAR DÉFAUT
Couleur ColoringAttributes color noir (0,0,0)
Modèle de remplissage ColoringAttributes shadingModel SHADE_GOURAUD
Cette liste n'est pas exhaustive mais vous donne un aperçu des possibilités d'utilisation des attributs
d'apparence en Java 3D et satisferont la plupart de vos besoins. Toutes ces classes dérivent des
classes SceneGraphObject et NodeComponent.
La classe javax.media.j3d.Appearance
Cette classe dérive des classes SceneGraphObject et NodeComponent. La modification des attributs
d'apparence d'une forme 3D s'effectue en passant en paramètre une instance de cette classe à la
méthode setAppearance () de la classe Shape3D. Plutôt que de mémoriser directement tous les
attributs d'apparence, la classe Appearance référence un ensemble d'instances de classes dérivant de
la classe NodeComponent et représentant les différents attributs d'apparence. Ceci permet de partager
certains attributs entre différentes formes. Les valeurs par défaut de ces attributs sont modifiés en
appelant la méthode set... () correspondante.
Ces capacités permettent d'autoriser la lecture ou la modification des différentes instances d'attributs
d'apparence.
Principales méthodes
public Appearance ()
Crée une instance de la classe Appearance avec tous ses attributs initialisés à null.
Principales méthodes
Méthode Capacité
Ces méthodes permettent d'interroger ou de modifier les différents attributs d'apparence. Ces
attributs ont une valeur par défaut qui correspond à celle définie par le constructeur par défaut de
leur classe respective.
Exemples
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 4
1. Les couleurs des sommets attribuées pour les constructions géométriques de classe
GeometryArray et l'attribut ignoreVertexColors de la classe RenderingAttributes.
2. La couleur des attributs de la classe ColoringAttributes.
3. Le calcul des normales des constructions géométriques de classe GeometryArray et les couleurs
des attributs de la classe Material.
4. L'utilisation de lumières pour éclairer la scène 3D.
Ces critères sont optionnels et la couleur affichée par Java 3D dépend de leur combinaison comme
le montre le tableau suivant, qui teste toutes les combinaisons possibles :
CRITÈRES
Couleur Normales
des sommets CRITÈRE
Couleur de + DOMINANT
+ Lumières
ColoringAttributes Couleurs de
ignoreVertexColors
Material
= false
Chaque cellule des colonnes Critères contient ou non un symbole pour indiquer si ce critère est
vrai ou faux. La colonne Critère dominant permet à chaque ligne du tableau de déduire la couleur
qui sera choisie par Java 3D pour une combinaison donnée :
CRITÈRE
COULEUR FINALE
DOMINANT
Couleur des sommets
Couleur des attributs de la classe ColoringAttributes
Combinaison des couleurs des attributs de la classe Material actives sous la lumière
Couleur d'émission des attributs de la classe Material
Couleur des attributs d'apparence par défaut (blanche par défaut)
Remarques
Pour les versions < 1.2 de Java 3D, l'attribut ignoreVertexColors n'existe pas et est
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 5
Conclusions
Si l'attribut ignoreVertexColors est égal à true, la couleur des sommets est toujours ignorée.
La couleur des sommets attribuées pour les constructions géométriques est toujours utilisée si
l'attribut ignoreVertexColors est égale à false (sa valeur par défaut). C'est le cas de la classe
ColorCube qui définit une couleur pour chacune des facettes de sa construction géométrique.
Sans lumière, la couleur d'une forme 3D ayant des attributs d'apparence de la classe Material,
est la couleur d'émission de l'instance de la classe Material. C'est le cas des classes Box,
Sphere, Cylinder et Cone : l'exemple SimpleObjects utilise un fond blanc pour apercevoir ces
formes dont la couleur d'émission est noire.
Voici l'applet Clown représentant un clown. Le clown est construit avec 8 formes de bases
sphérique, cylindrique et conique sur lesquels sont appliqués des transformations. Chaque forme
reçoit une couleur donnant le résultat suivant :
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.applet.MainFrame;
// Création du corps
Cylinder body = new Cylinder (1.75f, 10f);
Appearance shapeAppearance = new Appearance ();
// Application d'une couleur bleue
shapeAppearance.setColoringAttributes (new ColoringAttributes (0.4f, 0.6f, 1f, defaultShadi
body.setAppearance (shapeAppearance);
// Création de la tête
Sphere head = new Sphere (1.6f);
shapeAppearance = new Appearance ();
// Application d'une couleur peau
shapeAppearance.setColoringAttributes (new ColoringAttributes (1f, 0.8f, 0.6f, defaultShadi
head.setAppearance (shapeAppearance);
// Création du nez
Sphere nose = new Sphere (0.5f);
shapeAppearance = new Appearance ();
// Application d'une couleur rouge
shapeAppearance.setColoringAttributes (new ColoringAttributes (1, 0, 0, defaultShading));
nose.setAppearance (shapeAppearance);
// Création du chapeau
Cone hat = new Cone (2f, 4);
shapeAppearance = new Appearance ();
// Application d'une couleur orange
shapeAppearance.setColoringAttributes (new ColoringAttributes (1, 0.6f, 0, defaultShading))
hat.setAppearance (shapeAppearance);
// Construction de l'arbre
leftArmTransformationGroup.addChild (leftArm);
rightArmTransformationGroup.addChild (rightArm);
leftLegTransformationGroup.addChild (leftLeg);
rightLegTransformationGroup.addChild (rightLeg);
headTransformationGroup.addChild (head);
noseTransformationGroup.addChild (nose);
hatTransformationGroup.addChild (hat);
clown.addChild (body);
clown.addChild (leftArmTransformationGroup);
clown.addChild (rightArmTransformationGroup);
clown.addChild (leftLegTransformationGroup);
clown.addChild (rightLegTransformationGroup);
clown.addChild (headTransformationGroup);
clown.addChild (noseTransformationGroup);
clown.addChild (hatTransformationGroup);
return clown;
}
La classe javax.media.j3d.ColoringAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
ColoringAttributes passée en paramètre à la méthode setColoringAttributes () de la classe
Appearance permet de modifier les attributs de couleur et le mode de remplissage applicables à une
forme 3D. Consultez le tableau de choix des couleurs pour déterminer comment s'applique la
couleur définie par cet attribut.
Champs
Ces constantes représentent le mode utilisé pour colorer les facettes de la forme 3D. SHADE_FLAT
utilise une couleur unie et SHADE_GOURAUD colore les facettes par interpolation des différentes
couleurs de ses sommets.
Constructeurs
public ColoringAttributes ()
public ColoringAttributes (float red, float green, float blue, int shadeModel)
public ColoringAttributes (Color3f color, int shadeModel)
Ces constructeurs créent une instance de la classe ColoringAttributes initialisée avec la couleur
(red, green, blue) ou color et le modèle de remplissage shadeModel (par défaut égaux à la couleur
blanche et à SHADE_GOURAUD).
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier la couleur définie par cet attribut.
Ces méthodes permettent d'interroger ou de modifier le modèle de remplissage défini par cet
attribut.
Exemples
La classe javax.media.j3d.Material
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
Material passée en paramètre à la méthode setMaterial () de la classe Appearance permet de
modifier les couleurs utilisées une forme 3D éclairée. Consultez le tableau de choix des couleurs
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 9
Ces capacités permettent d'autoriser la lecture ou la modification des couleurs de cet attribut.
Constructeurs
public Material ()
public Material (Color3f ambientColor,
Color3f emissiveColor,
Color3f diffuseColor,
Color3f specularColor,
float shininess)
Ces constructeurs créent une instance de la classe Material initialisée avec les couleurs
ambientColor, emissiveColor, diffuseColor, specularColor et l'effet de brillance shininess. Par
défaut ambientColor est égal à (0.2,0.2,0.2), emissiveColor à (0,0,0), diffuseColor à (1,1,1),
specularColor à (1,1,1) et shininess à 64.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier la couleur de cet attribut émise par la forme
3D. Sur une scène 3D non éclairée, cette couleur a le même effet qu'un attribut de classe
ColoringAttributes.
Ces méthodes permettent d'interroger ou de modifier la couleur d'ambiance de cet attribut réfléchie
par la lumière d'ambiance.
Ces méthodes permettent d'interroger ou de modifier la couleur de diffusion de cet attribut réfléchie
par les lumières de la scène.
Ces méthodes permettent d'interroger ou de modifier la couleur de cet attribut donnant l'effet de
spécularité. Cette couleur est utilisée pour afficher un point de brillance sur une forme 3D.
Ces méthodes permettent d'interroger ou de modifier l'effet de brillance définie par cet attribut.
shininessest compris entre 1 (pas brillant) et 128 (très brillant).
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 10
Ces méthodes permettent d'interroger ou de modifier l'utilisation ou non de cet attribut. Si state est
égal à false, l'instance de classe Material est ignorée.
Exemple
La classe PolygonAttributes permet de choisir entre ces trois modes. Les classes PointAttributes
et LineAttributes permettent de modifier les attributs d'affichage des points représentant les
sommets et ceux des arêtes des facettes.
La couleur utilisée pour les points, les arêtes ou le remplissage des facettes est choisie en respectant
les règles du tableau de choix des couleurs.
Voici l'applet SphereConstruction qui affiche une même sphère avec les trois modes d'affichage :
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.applet.MainFrame;
return root;
}
La classe javax.media.j3d.PolygonAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
PolygonAttributes passée en paramètre à la méthode setPolygonAttributes () de la classe
Appearance permet de modifier le mode d'affichage et le mode d'élimination des facettes d'une
forme 3D.
Champs
Ces constantes représentent le mode d'affichage des facettes de la forme 3D. POLYGON_POINT
n'affiche que les sommets, POLYGON_LINE les arêtes ou POLYGON_FILL provoque le remplissage des
facettes.
Ces constantes indiquent les facettes à éliminer (cull ) à l'affichage de la forme 3D. CULL_NONE
n'élimine aucune facette, CULL_BACK élimine les facettes orientées vers l'arrière et CULL_FRONT élimine
les facettes orientées vers l'observateur.
Ces capacités permettent d'autoriser la lecture ou la modification du mode d'affichage des facettes
ou d'élimination des facettes.
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 12
Principaux constructeurs
public PolygonAttributes ()
public PolygonAttributes (int polygonMode, int cullFace, float polygonOffset)
Ces constructeurs créent une instance de la classe PolygonAttributes initialisée avec le mode
d'affichage polygonMode, le mode d'élimination des facettes cullFace et le décalage de facettes en
profondeur polygonOffset (par défaut égaux à POLYGON_FILL, à CULL_BACK et à 0).
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier le mode d'affichage défini par cet attribut.
polygonMode peut être égal à POLYGON_POINT, POLYGON_LINE ou POLYGON_FILL.
Ces méthodes permettent d'interroger ou de modifier le mode d'élimination des facettes définie par
cet attribut. cullFace peut être égal à CULL_NONE, CULL_BACK ou CULL_FRONT.
Exemple
Applet SphereConstruction .
La classe javax.media.j3d.PointAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
PointAttributes passée en paramètre à la méthode setPointAttributes () de la classe Appearance
permet de modifier les attributs de tracé des points d'une forme 3D quand son mode d'affichage
utilise le dessin des sommets.
Constructeurs
public PointAttributes ()
public PointAttributes (float pointSize, boolean pointAntialiasing)
Ces constructeurs créent une instance de la classe PointAttributes initialisée avec la taille en pixel
des points pointSize et l'utilisation de méthode d'antialiasing pointAntialiasing (par défaut égaux à
1 et à false).
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier la taille en pixel des point définie par cet
attribut.
Exemple
Applet SphereConstruction .
La classe javax.media.j3d.LineAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
LineAttributes passée en paramètre à la méthode setLineAttributes () de la classe Appearance
permet de modifier les attributs de tracé des lignes d'une forme 3D quand son mode d'affichage
utilise le dessin des arêtes.
Champs
Ces constantes représentent le type de trait utiliser pour tracer les arêtes.
Ces capacités permettent d'autoriser la lecture ou la modification de l'épaisseur des arêtes, du type
de trait et de l'utilisation de méthode d'antialiasing (anticrénelage).
Constructeurs
public LineAttributes ()
public LineAttributes (float lineWidth,
int linePattern,
boolean lineAntialiasing)
Ces constructeurs créent une instance de la classe LineAttributes initialisée avec l'épaisseur des
arêtes lineWidth, le type de trait linePattern et l'utilisation de méthode d'antialiasing
lineAntialiasing (par défaut égaux à 1, PATTERN_SOLID et à false).
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'épaisseur des arêtes définie par cet attribut.
Ces méthodes permettent d'interroger ou de modifier le type de trait défini par cet attribut.
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 14
Exemple
Applet SphereConstruction .
L'applet WaterGlass qui suit réutilise une forme de classe AxisShape et un cylindre avec des
attributs de transparence pour créer une scène représentant un verre d'eau.
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.*;
La classe javax.media.j3d.TransparencyAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
TransparencyAttributes passée en paramètre à la méthode setTransparencyAttributes () de la
classe Appearance permet de modifier la transparence d'une forme 3D.
La valeur de transparence est inverse à celle utilisée pour le canal alpha dans le codage RGBA des
couleurs Java : 0 correspond à une opacité totale et 1 à une transparence totale.
Champs
Ces constantes représentent le mode de transparence à appliquer à la forme 3D. NONE n'applique
aucune transparence, BLENDED réalise l'effet de transparence en mélangeant (blend ) la couleur de la
forme 3D avec celle de l'arrière plan et SCREEN_DOOR en construisant une trame ayant la couleur de la
forme 3D dont les pixels éteints laissent passer la couleur de l'arrière plan.
Principaux constructeurs
public TransparencyAttributes ()
public TransparencyAttributes (int transparencyMode, float transparencyVal)
Ces constructeurs créent une instance de la classe TransparencyAttributes initialisée avec le mode
de transparence transparencyMode et la valeur de transparence transparencyVal (par défaut égaux à
NONE et à 0).
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier le mode de transparence défini par cet attribut.
transparencyMode peut être égal à NONE, BLENDED, SCREEN_DOOR, FASTEST ou NICEST.
Ces méthodes permettent d'interroger ou de modifier la valeur de transparence définie par cet
attribut. transparencyVal est compris entre 0 (totalement opaque) et 1 (totalement transparent).
Exemple
Applet WaterGlass .
La classe javax.media.j3d.RenderingAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
RenderingAttributes passée en paramètre à la méthode setRenderingAttributes () de la classe
Appearance permet de modifier les attributs de rendu.
Les deux attributs les plus intéressants, ceux de visibilité et d'utilisation des couleurs des sommets
ne sont pas disponibles pour les versions < 1.2 de Java 3D.
Principal constructeur
public RenderingAttributes ()
Ces constructeurs créent une instance de la classe RenderingAttributes initialisée avec le. Par
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 17
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier la visibilité définie par cet attribut. Si visible
est égal à true (false par défaut), la forme 3D n'est pas visible mais peut être interceptée ou entrer
en collision avec d'autres objets.
Ces méthodes permettent d'interroger ou de modifier l'utilisation des couleurs des sommets définie
par cet attribut. Si ignoreVertexColors est égal à true (false par défaut), les couleurs des sommets
ne seront plus prises en compte pour déterminer la couleur de la forme 3D.
Considérez l'image de la texture comme si elle était affichée dans un repère de coordonnées (s,t)
entre les points (0,0) et (1,1), correspondant à sa largeur et sa hauteur en pixels. Les coordonnées
de texture de chaque sommet d'une forme 3D indiquent un point (s,t) de l'image. Pour chaque
facette, Java 3D utilise ces coordonnées pour extraire la portion d'image correspondante et
l'appliquer sur la facette. Cette portion d'image est déformée pour s'adapter aux dimensions de la
facette.
La texture est répétée comme un motif si certaines coordonnées de texture (s,t) sont en dehors de
l'intervalle [0,1] (mode de limite par défaut WRAP).
Les images utilisées pour une texture peuvent provenir d'une image dessinée en Java ou d'un fichier
d'image. La classe TextureLoader permet de gérer facilement le chargement de fichier et la création
d'une instance de classe Texture. Pour optimiser le traitement des textures, Java 3D requiert que la
largeur et la hauteur des images soient une puissance de 2 (1, 2, 4, 8, 16,...).
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 18
Les coordonnées de texture d'une construction géométrique peuvent être spécifiées grâce à la
méthode setTextureCoordinates () de la classe GeometryInfo. Les coordonnées de texture des
classes de formes simples Box, Sphere, Cylinder et Cone ne sont définies que si la constante
GENERATE_TEXTURE_COORDS est utilisée dans le paramètre primflags de leur constructeur. Elles
permettent d'appliquer une texture de la manière suivante :
Voici l'applet SimpleTexturedObjects qui recrée la même scène que l'applet SimpleObjects avec
les formes simples utilisant des coordonnées de texture et des attributs d'apparence avec une texture.
L'image de la texture peut être modifiée grâce au paramètre ImageFile. Pour une utilisation avec la
commande java , la valeur du paramètre est passée de la manière suivante :
java SimpleTexturedObjects ImageFile=tweed.jpg
L'arbre de cette scène 3D est le même que celui de l'applet SimpleObjects .
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.applet.MainFrame;
return root;
}
La classe javax.media.j3d.Texture
Cette classe abstract dérive des classes SceneGraphObject et NodeComponent. C'est la super classe
des classes Texture2D et Texture3D dont une instance est passée en paramètre à la méthode
setTexture () de la classe Appearance pour modifier la texture utilisée par une forme 3D. Cette
classe définit l'image à appliquer comme texture à une forme 3D.
Champs
Ces constantes représentent le format de l'image utilisée par la texture. RGB et RGBA sont les formats
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 20
les plus courants car ils utilisent tel quelles les couleurs des pixels de l'image pour la texture. Les
autres peuvent être intéressants à utiliser pour faire varier par exemple la transparence (format
ALPHA) ou la luminosité (format LUMINANCE) de la forme 3D en fonction des valeurs des pixels de
l'image.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'image (ou les images) utilisées pour la
texture. Utilisez 0 pour level. La largeur et la hauteur de l'image doivent être une puissance de 2 (1,
2, 4, 8, 16,...).
Exemples
La classe javax.media.j3d.Texture2D
Cette classe dérive des classes SceneGraphObject, NodeComponent, Texture. Le moyen le plus simple
d'instancier cette classe est d'utiliser la méthode getTexture () de la classe TextureLoader qui
permet d'avoir une image avec les dimensions requises et un format donné.
La classe javax.media.j3d.TextureAttributes
Cette classe dérive des classes SceneGraphObject et NodeComponent. Une instance de la classe
TextureAttributes passée en paramètre à la méthode setTextureAttributes () de la classe
Appearance permet de modifier le mode d'application de la texture sur une forme 3D.
Champs
Ces constantes représentent le mode d'application de la texture sur les facettes de la forme 3D.
REPLACE utilise la texture à la place de la couleur des facettes, DECAL applique la texture comme un
décalcomanie qui laisse apparaître la couleur des facettes sur les zones transparentes de la texture,
BLEND mélange la texture avec la couleur renvoyée par la méthode getTextureBlendColor () et
MODULATE combine la texture avec la couleur des facettes ce qui permet de calculer l'effet d'éclairage
sur la forme 3D.
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 21
Ces constantes représentent le mode (le plus rapide ou celui donnant le meilleur aspect) utilisé pour
appliquer la texture en cas de correction du à l'effet de perspective.
Principaux constructeurs
public TextureAttributes ()
public TextureAttributes (int textureMode, Transform3D transform,
Color4f textureBlendColor, int perspectiveCorrectionMode)
Ces constructeurs créent une instance de la classe TextureAttributes initialisée avec le mode
d'application de la texture textureMode et la couleur de mélange textureBlendColor. Par défaut,
textureMode est égal à REPLACE, textureBlendColor au noir, transform à la transformation identité et
perspectiveCorrectionMode à NICEST.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier le mode d'application de la texture défini par
cet attribut. textureMode peut être égal à REPLACE, MODULATE, DECAL ou BLEND.
Ces méthodes permettent d'interroger ou de modifier la couleur avec laquelle la texture soit être
mélangée quand le mode d'application de la texture est BLEND.
Exemple
La classe javax.media.j3d.ImageComponent
Cette classe abstract dérive des classes SceneGraphObject et NodeComponent. C'est la super classe
des classes d'image ImageComponent2D et ImageComponent3D utilisées pour les textures.
La classe javax.media.j3d.ImageComponent2D
vendredi 13 octobre 2000 Du C/C++ à Java : Les attributs d'apparence Page: 22
Cette classe dérive des classes SceneGraphObject, NodeComponent, ImageComponent et représente une
image 2D utilisée pour les fonds d'écran et les textures. Le moyen le plus simple d'instancier cette
classe est d'utiliser la méthode getImage () de la classe TextureLoader.
La classe com.sun.j3d.utils.image.TextureLoader
Cette classe utilitaire permet de charger une image et de générer une instance de classe
ImageComponent2D ou Texture à partir de l'une des 4 sources suivantes :
un fichier d'image
une URL (Uniformed Resource Locator ) désignant une image
une instance de java.awt.Image
une instance de java.awt.image.BufferedImage.
Les images provenant d'un fichier ou d'une URL sont générées sans avoir à s'occuper de la gestion
asynchrone du chargement d'images en Java.
Principaux constructeurs
Ces constructeurs créent une instance de la classe TextureLoader qui charge une image à partir des
sources bufferedImage ou image, du fichier fileName ou de l'URL url. Pour information, Java
supporte les formats GIF et JPEG , et à partir du JDK 1.3 le format PNG . Le composant
observer, instance de la classe java.awt.Component, peut être par exemple une instance des classes
Canvas3D, java.awt.Applet ou java.awt.Frame. Le format (par défaut égal à "RGBA") peut être égal à
"RGBA", "RGBA4", "RGB5_A1", "RGB", "RGB4", "RGB5", "R3_G3_B2", "LUM8_ALPHA8", "LUM4_ALPHA4",
"LUMINANCE" and "ALPHA" correspondent à ceux utilisés par la classe ImageComponent.
Méthodes
Exemple
Eclairage 3D
Activation
Les différentes sources lumineuses
Combinaison des sources lumineuses
Les classes d'éclairage
Après avoir appris comment créer des objets 3D et modifier leurs attributs d'apparence, ce chapitre
présente comment appliquer un éclairage à une scène 3D, pour lui donner une touche plus réaliste.
Activation
Il suffit d'ajouter n'importe où dans l'arbre d'une scène 3D une instance de la classe Light pour
activer l'éclairage de cette scène. La couleur des objets sous éclairage dépend de leurs attributs
d'apparence. Les attributs de classe Material ne sont pas obligatoires pour que Java 3D puissent
déterminer la couleur des différentes formes d'une scène 3D mais il est conseillé de les utiliser pour
un rendu plus réaliste de vos objets.
Le tableau suivant vous indique quelle sera la couleur d'une forme 3D en fonction de l'attribut
d'apparence dominant (voir aussi le tableau de choix des couleurs qui permet de déterminer l'attribut
dominant) :
Material
Combinaison des couleurs de Material et des couleurs des différentes sources
lumineuses
Couleur de l'instance de ColoringAttributes. Même effet avec ou sans
ColoringAttributes
éclairage.
Couleur des sommets Couleur des sommets. Même effet avec ou sans éclairage.
Classe Effet
Source lumineuse ambiante
Cette lumière est utilisée pour éclairer un minimum
les zones d'une scène 3D ne recevant aucune lumière
AmbientLight des autres types de sources lumineuses. La couleur
d'ambiance (couleur ambiantColor de la classe
Material) des formes 3D réfléchit cette lumière de
manière uniforme.
Source lumineuse unidirectionnelle
Cette lumière rayonne dans une direction unique dir.
Ceci correspond à une approximation d'une source
lumineuse ponctuelle située à une distance très
éloignée, comme le soleil éclairant une scène. Les
couleurs de diffusion et de spécularité (couleurs
DirectionalLight
diffuseColor et specularColor de la classe Material)
des formes 3D réfléchissent cette lumière en
fonction de l'orientation de chacune des facettes par
rapport à la direction de la lumière. Toutes les
facettes qui ne sont pas orientée vers la source
lumineuse ne sont pas éclairées.
Source lumineuse ponctuelle
Comme pour une ampoule, cette lumière rayonne à
partir d'un point pos de l'espace dans toutes les
directions. De plus, son intensité diminue avec
PointLight l'éloignement de la forme éclairée. Les couleurs de
diffusion et de spécularité (couleurs diffuseColor et
specularColor de la classe Material) des formes 3D
réfléchissent cette lumière en fonction de
l'orientation de chacune des facettes.
Spot
Comme une source lumineuse ponctuelle, cette
lumière rayonne à partir d'un point pos de l'espace
mais les directions des rayons sont conscrits dans un
SpotLight cône d'angle alpha et d'axe dir. Les couleurs de
diffusion et de spécularité (couleurs diffuseColor et
specularColor de la classe Material) des formes 3D
réfléchissent cette lumière en fonction de
l'orientation de chacune des facettes.
Voici l'applet LightEffect qui permet de tester sur une sphère les différents types d'éclairage décrits
ci-dessus, grâce au paramètre LightClass. Ce paramètre doit prendre pour valeur le nom d'une des
classes de source lumineuse. Pour une utilisation avec la commande java , la valeur du paramètre
est passée de la manière suivante :
java LightEffect LightClass=SpotLight
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.*;
Les sources lumineuses permettent d'éclairer une scène mais n'ont pas de représentation physique
à l'écran. En cas de besoin, c'est à vous d'ajouter à votre scène une forme 3D supplémentaire
représentant la source lumineuse (par exemple une sphère est utilisée pour le soleil dans l'applet
SunEarthMoonMotion ).
La couleur finale de chaque facette est le résultat de la combinaison des effets des lumières décrits
vendredi 13 octobre 2000 Du C/C++ à Java : Eclairage 3D Page: 4
couleur emissiveColor
+ couleurs de toutes les sources lumineuses ambiantes réfléchies par la couleur ambiantColor
+ couleurs de toutes les autre sources lumineuses réfléchies par les couleurs diffuseColor et
specularColor
= couleur de la facette
Chacune des composantes Rouge, Vert, Bleue s'additionne pour donner la couleur finale de la
facette.
La couleur émise est toujours active qu'une source lumineuse ambiante soit présente dans la
scène 3D ou non. Si elle est la seule couleur à n'être pas noire dans les attributs de classe
Material, elle a le même effet que la couleur d'un attribut de classe ColoringAttributes.
La couleur d'ambiance est combinée avec les couleurs des sources lumineuses ambiantes d'une
scène 3D pour donner la couleur finale d'ambiance d'une forme 3D, tandis que la couleur
émise n'est pas modifiée par les couleurs des sources lumineuses de la scène 3D.
L'intensité lumineuse de la couleur est déterminée à chaque sommet des facettes grâce à sa normale,
la direction des sources lumineuses à ce sommet et la position de l'observateur. Une fois calculée la
couleur à chaque sommet, la facette est colorée par interpolation de ces différentes couleurs (sauf si
le modèle de remplissage de l'attribut de classe ColoringAttributes est SHADE_FLAT). L'effet des
sources lumineuses ponctuelles et des spots sur une forme 3D, plane ou non, sera beaucoup mieux
rendu si cette forme est découpée en un grand nombre de facettes : ceci va augmenter le nombre de
sommets et de normales et par conséquent le nombre de calculs de couleurs.
Le modèle d'éclairage utilisé par Java 3D ne calcule pas les ombres des objets les uns sur les autres
ni leur reflet (pas de ray tracing ou d'algorithmes du même genre) : Si vous tenez à rendre une
touche très réaliste à vos scènes sous Java 3D, c'est à vous de calculer les ombres et d'ajouter à la
scène 3D les objets représentant ces ombres.
Eclairage et texture
Pour éclairer une forme 3D sur laquelle est appliquée une texture, il faut ajouter à ses attributs
d'apparence une instance de la classe TextureAttributes dont le mode d'application est MODULATE.
Comme ce mode provoque la combinaison de la texture avec la couleur des facettes, il vaut mieux
que les couleurs des attributs de classe Material de la forme 3D soient blanches ou grises, sauf pour
faire des effets spéciaux (le constructeur par défaut de la classe Material suffit généralement).
L'applet LitPlane suivante dérive de la classe LightEffect et permet de tester l'effet des différents
types d'éclairage sur un plan dont le nombre de sommets par côté peut varier en fonction du
paramètre precision. Une texture peut être appliquée sur ce plan en mode MODULATE en utilisant
l'image passée par le paramètre imageFile (par exemple grain.jpg), ce qui permet de combiner la
texture avec le résultat du calcul de l'éclairage.
Les captures d'écran suivantes vous montre l'effet d'une source lumineuse de classe SpotLight sur
un plan en fonction du nombre de ses sommets (et de ses normales) : avec une précision de 2x2 et
5x5 facettes, l'effet du spot est bien visible mais les facettes se devinent (en fait, il y a deux fois
plus de facettes car elles sont triangulaires). Si une texture est appliquée sur le plan, les facettes
sous-jacentes se devinent beaucoup moins même avec une précision faible.
Pour information, si vous utilisez une précision égale à 1, vous obtiendrez un plan décrit par deux
facettes triangulaires dont les sommets sont les coins du plan. La lumière spot n'éclairant ici aucun
coin du plan, le plan sera noir résultat de l'interpolation par Java 3D de la couleur noire calculée
avec les normales à chaque sommet.
vendredi 13 octobre 2000 Du C/C++ à Java : Eclairage 3D Page: 5
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.geometry.*;
transformGroup.addChild (plane);
return transformGroup;
}
vendredi 13 octobre 2000 Du C/C++ à Java : Eclairage 3D Page: 6
// Crée une surface plane carrée dont les deux points opposées ont
// pour coordonnées (-size / 2, -size / 2, 0) et (size / 2, size / 2, 0)
// et dont les coordonnées de texture se répartissent entre (0,0) et (size,size)
// Cette surface est constituée de precision * precision facettes
public Shape3D createPlane (float size, int precision)
{
float halfSize = size / 2;
// Calcul des sommets d'un carré construit avec des bandes
float [] coords = new float [2 * 3 * (precision + 1) * precision];
float [] texCoords = new float [2 * 2 * (precision + 1) * precision];
for (int i = 0, k = 0, l = 0; i < precision; i ++)
for (int j = 0; j < precision + 1; j ++)
{
coords [k++] = size / precision * i - halfSize;
coords [k++] = size / precision * j - halfSize;
coords [k++] = 0;
coords [k++] = size / precision * (i + 1) - halfSize;
coords [k++] = size / precision * j - halfSize;
coords [k++] = 0;
L'applet LitApplet3D qui suit permet d'éclairer les applets de classe Applet3D décrites
précédemment pour les tester sous l'effet de trois sources lumineuses : une source lumineuse
ambiante et deux sources lumineuses unidirectionnelles de couleurs et de directions différentes. La
classe LitApplet3D définie aussi une méthode checkMaterial () qui attribue des attributs de classe
vendredi 13 octobre 2000 Du C/C++ à Java : Eclairage 3D Page: 7
AxisShapeDemo ObjectFileDemo
Clown SphereConstruction
import javax.media.j3d.*;
import javax.vecmath.*;
import java.util.Enumeration;
import com.sun.j3d.utils.applet.MainFrame;
{
BoundingSphere influencingBounds = new BoundingSphere (new Point3d (), 100.0);
Light light2 = new DirectionalLight (new Color3f (0.8f, 0.8f, 1f), // bleu
new Vector3f (-1, -0.5f, -0.5f));
light2.setInfluencingBounds (influencingBounds);
root.addChild (light2);
Cette classe abstract dérive des classes SceneGraphObject, Node, Leaf. C'est la super classe des
classes de sources lumineuses AmbientLight, DirectionalLight, PointLight.
vendredi 13 octobre 2000 Du C/C++ à Java : Eclairage 3D Page: 9
Toutes les sources lumineuses ont une limite d'influence (influencing bounds ) null par défaut.
Cette limite permet d'optimiser les calculs de Java 3D en lui évitant de prendre en compte à tout
moment toutes les sources lumineuses sur les scènes 3D de grande taille.
Il faut obligatoirement déterminer cette limite pour que la source lumineuse est un effet.
Ces capacités permettent d'autoriser la lecture ou la modification de l'état allumé ou éteint de cette
source lumineuse, de sa couleur et de ses limites d'influence.
Principales méthodes
Ces méthodes permettent d'interroger l'état allumé (true) ou éteint (false) de cette source lumineuse
ou de modifier son état.
Ces méthodes permettent d'interroger ou de modifier la limite d'influence de cette source lumineuse.
La classe javax.media.j3d.AmbientLight
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Light et représente une source
lumineuse ambiante que réfléchit de manière uniforme la couleur d'ambiance (couleur ambiantColor
de la classe Material) des formes 3D.
Constructeurs
public AmbientLight ()
public AmbientLight (Color3f color)
public AmbientLight (boolean lightOn, Color3f color)
Ces constructeurs permettent de créer une source lumineuse ambiante initialisée avec la couleur
color et dans l'état allumé ou éteint lightOn (par défaut, égaux à la couleur blanche et true).
Exemples
La classe javax.media.j3d.DirectionalLight
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Light et représente une source
lumineuse unidirectionnelle que réfléchissent les couleurs de diffusion et de spécularité (couleurs
vendredi 13 octobre 2000 Du C/C++ à Java : Eclairage 3D Page: 10
Constructeurs
public DirectionalLight ()
public DirectionalLight (Color3f color, Vector3f dir)
public DirectionalLight (boolean lightOn, Color3f color, Vector3f dir)
Ces constructeurs permettent de créer une source lumineuse unidirectionnelle initialisée avec la
couleur color, de direction dir et dans l'état allumé ou éteint lightOn (par défaut, égaux à la couleur
blanche, au vecteur (0,0,-1) et true).
Principales méthodes
Exemples
La classe javax.media.j3d.PointLight
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Light et représente une source
lumineuse ponctuelle que réfléchissent les couleurs de diffusion et de spécularité (couleurs
diffuseColor et specularColor de la classe Material) des formes 3D.
Constructeurs
public PointLight ()
public PointLight (Color3f color, Point3f pos, Point3f attenuation)
public PointLight (boolean lightOn, Color3f color, Point3f pos, Point3f attenuation)
Ces constructeurs permettent de créer une source lumineuse ponctuelle initialisée avec la couleur
color, de position pos, d'atténuation attenuation et dans l'état allumé ou éteint lightOn. Par défaut,
color est égale à la couleur blanche, pos au point (0,0,0), attenuation à (1,0,0) et lightOn à true.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'atténuation de cette source lumineuse. Les
trois champs x, y et z du paramètre attenuation correspondent aux paramètres constant, linear et
quadratic, et sont utilisés pour déterminer l'atténuation de la lumière en fonction de la distance d
entre la position de la source lumineuse et un point de l'espace avec la formule suivante :
1 / (constant + linear * d + quadratic * d * d)
Si linear et quadratic sont égaux à 0 la lumière ne s'atténue pas avec l'éloignement de la position
de la source lumineuse.
Exemple
La classe javax.media.j3d.SpotLight
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Light, PointLight et représente une
source lumineuse spot que réfléchissent les couleurs de diffusion et de spécularité (couleurs
diffuseColor et specularColor de la classe Material) des formes 3D.
Constructeurs
public SpotLight ()
public SpotLight (Color3f color, Point3f pos, Point3f attenuation,
Vector3f dir, float spreadAngle, float concentration)
public SpotLight (boolean lightOn, Color3f color, Point3f pos, Point3f attenuation,
Vector3f dir, float spreadAngle, float concentration)
Ces constructeurs permettent de créer un spot initialisé avec la couleur color, de position pos,
d'atténuation attenuation et dans l'état allumé ou éteint lightOn. dir donne la direction de l'axe du
cône d'éclairage, spreadAngle l'angle de ce cône et concentration la concentration de la lumière du
spot.
Par défaut, color est égale à la couleur blanche, pos au point (0,0,0), attenuation à (1,0,0), dir à
(-1,0,0), spreadAngle à PI et concentration à 0 et lightOn à true.
Principales méthodes
Exemple
Applet LightEffect .
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 1
Animation 3D
Interaction
Animation
Un exemple complet : Du soleil à la lune
Plus loin avec Java 3D...
Ce dernier chapitre présente comment animer une scène 3D, suite aux interactions avec l'utilisateur
ou en définissant le mouvement des objets.
Interaction
Comportement et stimulus
Java 3D permet d'afficher des images statiques mais fournit aussi un ensemble de mécanismes
d'interaction simples à mettre en œuvre pour permettre d'animer une scène 3D. Toutes les classes
gérant les interactions dérivent de la classe Behavior qui décrit un comportement en réponse à un ou
plusieurs stimuli de classe WakeupCriterion. Une fois paramétré un nœud de comportement, il suffit
de l'ajouter à l'arbre d'une scène 3D pour le rendre actif.
Pour votre information, voici la hiérarchie des classes de stimuli disponibles dans Java 3D :
java.lang.Object
javax.media.j3d.WakeupCondition
javax.media.j3d.WakeupAnd
javax.media.j3d.WakeupAndOfOrs
javax.media.j3d.WakeupOr
javax.media.j3d.WakeupOrOfAnd
javax.media.j3d.WakeupCriterion
1. Méthode initialize () : dans cette méthode, il faut notamment appeler la méthode wakeupOn
(WakeupCondition criteria) en précisant en paramètre quels sont les stimuli auxquels cette
classe veut réagir.
La méthode initialize () est invoquée automatiquement une fois qu'un nœud de classe
Behavior est vivant, c'est-à-dire au moment où l'arbre auquel il appartient est rattaché à un
univers.
2. Méthode processStimulus (java.util.Enumeration criteria) : cette méthode est invoquée
automatiquement quand survient un stimulus respectant les conditions passées à la méthode
wakeupOn (). La méthode processStimulus () décrit les actions à entreprendre suite à la liste
de stimuli reçus dans le paramètre criteria.
Finalement, la méthode wakeupOn () doit être rappelée si la méthode processStimulus () doit
être rappelée suite à un nouveau stimulus.
Le plus souvent cette méthode modifie les propriétés des autres nœuds de l'arbre de la scène :
par exemple, en réponse au mouvement de la souris, une classe de comportement peut
modifier un groupe de transformation pour déplacer des objets ou en réponse à la collision de
deux objets, elle peut modifier la géométrie d'une forme 3D pour simuler une déformation.
Ces deux méthodes n'étant pas invoquées par Java 3D dans des threads séparés, leur traitement ne
doit pas être trop long pour ne pas bloquer Java 3D.
Comparativement aux sources lumineuses, les comportements définissent une zone limite
d'activation (scheduling bounds ), null par défaut. Un comportement n'est actif qu'au moment où
sa zone d'activation a une intersection avec celle de l'instance courante de la classe ViewPlatform.
Il faut obligatoirement déterminer la zone limite du comportement pour l'activer.
Tous les nœuds dont les propriétés sont modifiées par un comportement doivent avoir les
capacités adéquats.
java.lang.Object
javax.media.j3d.SceneGraphObject
javax.media.j3d.Node
javax.media.j3d.Leaf
javax.media.j3d.Behavior
javax.media.j3d.Billboard
javax.media.j3d.Interpolator
com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior
javax.media.j3d.LOD
class javax.media.j3d.DistanceLOD
com.sun.j3d.utils.behaviors.mouse.MouseBehavior
com.sun.j3d.utils.behaviors.mouse.MouseRotate
com.sun.j3d.utils.behaviors.mouse.MouseTranslate
com.sun.j3d.utils.behaviors.mouse.MouseZoom
com.sun.j3d.utils.behaviors.picking.PickMouseBehavior
com.sun.j3d.utils.behaviors.picking.PickRotateBehavior
com.sun.j3d.utils.behaviors.picking.PickTranslateBehavior
com.sun.j3d.utils.behaviors.picking.PickZoomBehavior
Figure 21. Interactions avec la souris Figure 22. Interactions avec le clavier
Ces figures indiquent sur quel type de transformation (translations dans les 3 directions, rotations
autour des axes x et y ou Identité) agissent les déplacements de souris ou les touches du clavier.
Voici l'applet MouseApplet3D qui dérive de la classe LitApplet3D. Cette applet permet d'interagir
sur toutes les scènes 3D décrites par les classes de ce manuel, en ajoutant à la scène illuminée trois
comportements de classe MouseRotate, MouseTranslate et MouseZoom. Ceux-ci modifient la
transformation du groupe mouseTransform en fonction des mouvements de la souris (bouton
enfoncé) pour obtenir un point de vue différent sur la scène.
La classe de l'applet à manipuler est spécifiée grâce au paramètre Applet3DClass. Pour une
utilisation avec la commande java , la valeur du paramètre est passée de la manière suivante :
java MouseApplet3D Applet3DClass=AxisShapeDemo
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.applet.MainFrame;
// Construction de l'arbre
root.addChild (rotate);
root.addChild (translate);
root.addChild (zoom);
root.addChild (mouseTransform);
mouseTransform.addChild (subTree);
return root;
}
Le symbole est utilisé pour les feuilles représentant un comportement dans l'arbre d'une scène
3D. Généralement, un nœud de comportement est un enfant du groupe qu'il modifie ou du
groupe parent le plus proche (ici les nœuds sont des enfants du groupe ) et désigne par une
flèche le nœud qu'il modifie. Vous noterez qu'ici le groupe de transformation sur lequel agissent
les comportements est un parent de la forme 3D de classe AxisShape mais aussi des trois sources
lumineuses. C'est-à-dire que la transformation modifie l'orientation et la position de la forme 3D
mais aussi des sources lumineuses.
La classe javax.media.j3d.Behavior
Cette classe abstract dérive des classes SceneGraphObject, Node, Leaf. C'est la super classe de
toutes les classes de comportement.
Principales méthodes
Cette méthode est invoquée automatiquement une fois que ce nœud est vivant. Son implémentation
doit notamment appeler la méthode wakeupOn () pour préciser quels sont les stimuli auxquels cette
classe veut réagir.
Cette méthode est invoquée automatiquement quand survient un stimulus respectant les conditions
voulues. criteria est une énumération d'instances de classe WakeupCondition.
La classe javax.media.j3d.WakeupCondition
Méthodes
Ces méthodes renvoient une énumération des stimuli de classe WakeupCriterion de cet ensemble. La
seconde méthode ne renvoie que la liste des stimuli qui ont provoqué l'appel à la méthode
processStimulus () de la classe Behavior.
La classe javax.media.j3d.WakeupCriterion
Cette classe abstract dérive de la classe WakeupCondition. C'est la super classe des classes de
stimuli.
Méthode
La classe com.sun.j3d.utils.behaviors.mouse.MouseBehavior
Cette classe abstract dérive des classes SceneGraphObject, Node, Leaf, Behavior. C'est la super
classe des classes MouseRotate, MouseTranslate et MouseZoom qui définissent les interactions avec les
mouvements de la souris. Ces classes permettent de modifier le point de vue sur une scène 3D en
appliquant une rotation (avec la classe MouseRotate) ou une translation (en x et y pour la classe
MouseTranslate et en z pour la classe MouseZoom) sur un groupe de transformation parent de cette
scène. Il est possible de cumuler ces trois comportements sur un même groupe de transformation,
comme dans l'applet MouseApplet3D.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier le groupe de transformation sur lequel agit ce
comportement.
La classe com.sun.j3d.utils.behaviors.mouse.MouseRotate
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, MouseBehavior. Elle permet
de créer un comportement de rotation en réponse à un déplacement de la souris, bouton (gauche)
enfoncé. La rotation est appliquée au groupe de transformation associé, qui doit avoir les capacités
ALLOW_TRANSFORM_READ et ALLOW_TRANSFORM_WRITE. Une rotation autour de l'axe y est appliquée en
réponse à un déplacement de la souris vers la gauche ou la droite, et une rotation autour de l'axe x
est appliquée en réponse à un déplacement dans l'autre direction.
Principaux constructeurs
public MouseRotate ()
public MouseRotate (TransformGroup transformGroup)
Principales méthodes
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 6
Cette méthode peut être outrepassée dans une classe dérivée pour réagir aux transformations
successives. transform contient la transformation actuellement appliquée.
Exemple
Applet MouseApplet3D .
La classe com.sun.j3d.utils.behaviors.mouse.MouseTranslate
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, MouseBehavior. Elle permet
de créer un comportement de translation en x et y en réponse à un déplacement de la souris, bouton
droit enfoncé (ou bouton de la souris et touche pomme enfoncés sous MacOS). La translation est
appliquée au groupe de transformation associé, qui doit avoir les capacités ALLOW_TRANSFORM_READ et
ALLOW_TRANSFORM_WRITE.
Principaux constructeurs
public MouseTranslate ()
public MouseTranslate (TransformGroup transformGroup)
Principales méthodes
Cette méthode peut être outrepassée dans une classe dérivée pour réagir aux transformations
successives. transform contient la transformation actuellement appliquée.
Exemple
Applet MouseApplet3D .
La classe com.sun.j3d.utils.behaviors.mouse.MouseZoom
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, MouseBehavior. Elle permet
de créer un comportement de translation en z en réponse à un déplacement de la souris vers le haut
ou le bas, bouton (gauche) et touche ALT enfoncés, ce qui rapproche ou éloigne une scène 3D. La
translation est appliquée au groupe de transformation associé, qui doit avoir les capacités
ALLOW_TRANSFORM_READ et ALLOW_TRANSFORM_WRITE.
Principaux constructeurs
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 7
public MouseZoom ()
public MouseZoom (TransformGroup transformGroup)
Principales méthodes
Cette méthode peut être outrepassée dans une classe dérivée pour réagir aux transformations
successives. transform contient la transformation actuellement appliquée.
Exemple
Applet MouseApplet3D .
La classe com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior. Elle permet de créer un
comportement qui répond aux touches du clavier en modifiant la transformation du groupe de
transformation associé, qui doit avoir les capacités ALLOW_TRANSFORM_READ et ALLOW_TRANSFORM_WRITE.
Constructeur
Animation
Une animation est un type de comportement qui modifie une scène 3D de manière prédéterminée en
fonction du temps écoulé. Bien que la classe WakeupOnElapsedTime puisse être utilisée comme
stimulus pour réaliser une animation, Java 3D fournit un ensemble de classes qui permettent de
réaliser la plupart des animations beaucoup plus facilement. Les classes dérivées de la classe
d'animation Interpolator permettent d'effectuer des animations qui modifient la couleur d'une
forme 3D ou un groupe de transformation pour déplacer ou faire tourner des formes 3D. Un
comportement d'animation utilise un opérateur alpha de classe Alpha pour déterminer comment doit
évoluer dans le temps l'animation. Un opérateur alpha utilise la courbe suivante alpha = f (t)
comme base, modifiée par les paramètres de la classe Alpha :
Suivant le choix et la valeur des différentes phases, vous pouvez obtenir un grand choix de courbes,
comme par exemple :
La valeur de l'opérateur alpha est utilisée par les classes d'animation comme facteur d'interpolation
entre deux valeurs initiale et finale, 0 correspondant à la valeur initiale et 1 à la valeur finale. Par
exemple :
Si alpha est utilisé comme facteur d'interpolation d'un angle compris entre 0 et 2 PI, vous
obtenez un angle de rotation qui varie dans le temps. En recopiant cet angle variable dans la
transformation d'un groupe de transformation , vous obtenez une animation de rotation pour
tous les enfants du groupe . Ce type d'animation est implémentée par la classe
RotationInterpolator.
Si alpha est utilisé comme facteur d'interpolation des couleurs noir et blanc, vous obtenez une
couleur grise plus ou moins foncée dans le temps. En recopiant cette couleur dans un attribut
d'apparence de classe Material, vous obtenez une animation modifiant la couleur de toutes les
formes 3D qui référence cet attribut d'apparence. Ce type d'animation est implémentée par la
classe ColorInterpolator.
Pour tester l'effet des différentes paramètres utilisés pour construire la courbe d'un opérateur alpha,
vous pouvez tester l'applet AlphaTest décrite avec la classe Alpha.
java.lang.Object
javax.media.j3d.SceneGraphObject
javax.media.j3d.Node
javax.media.j3d.Leaf
javax.media.j3d.Behavior
javax.media.j3d.Interpolator
javax.media.j3d.ColorInterpolator
com.sun.j3d.utils.behaviors.interpolators.KBSplinePathInterpolator
com.sun.j3d.utils.behaviors.interpolators.KBRotPosScaleSplinePath
javax.media.j3d.PathInterpolator
javax.media.j3d.PositionPathInterpolator
javax.media.j3d.RotationPathInterpolator
javax.media.j3d.RotPosPathInterpolator
javax.media.j3d.RotPosScalePathInterpolator
javax.media.j3d.PositionInterpolator
javax.media.j3d.RotationInterpolator
javax.media.j3d.ScaleInterpolator
javax.media.j3d.SwitchValueInterpolator
com.sun.j3d.utils.behaviors.interpolators.TCBSplinePathInterpolator
com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePat
javax.media.j3d.TransparencyInterpolator
Les formes 3D dépendant d'un comportement d'animation sont mises à jour régulièrement par Java
3D pour donner l'effet d'animation. Vous n'avez pas à vous soucier du taux de rafraîchissement des
animations : celui-ci peut varier en fonction de la puissance de calcul disponible sur votre machine
à un moment donné.
import java.util.StringTokenizer;
import java.awt.Font;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
{
String currentText = tokens.nextToken ();
// Ajout des lignes qui ont autre chose que des blancs
if (currentText.trim ().length () > 0)
{
Text3D textGeometry = new Text3D (font, currentText, position,
Text3D.ALIGN_CENTER, Text3D.PATH_RIGHT);
// Calcul du texte le plus large pour l'afficher entièrement à l'écran
textGeometry.getBoundingBox (bounds);
bounds.getLower (lower);
bounds.getUpper (upper);
if (maxWidth < upper.x - lower.x)
maxWidth = upper.x - lower.x;
// Ajout du texte au groupe
textGroup.addChild (new Shape3D (textGeometry));
}
}
Le symbole est utilisé pour les feuilles représentant un comportement d'animation dans l'arbre
d'une scène 3D. Comme pour un nœud de comportement , un nœud d'animation est
généralement un enfant du groupe qu'il modifie ou du groupe parent le plus proche (ici le nœud
est un enfant du groupe qu'il modifie). A partir de la version 1.2 de Java 3D, il est possible de
n'utiliser qu'une seule forme 3D pour tout le texte en utilisant la méthode addGeometry () de la
classe Shape3D.
La classe javax.media.j3d.Interpolator
Cette classe abstract dérive des classes SceneGraphObject, Node, Leaf, Behavior. C'est la super
classe de toutes les classes d'animation notamment PositionInterpolator, RotationInterpolator,
ScaleInterpolator, ColorInterpolator et TransparencyInterpolator et aussi de la classe
javax.media.j3d.PathInterpolator et des classes du package
com.sun.j3d.utils.behaviors.interpolators qui permettent de créer des comportements
d'animation pour parcourir un chemin (path ) constitué d'un ensemble de points.
La classe Interpolator mémorise l'opérateur alpha utilisé pour les animations.
N'oubliez pas de déterminer la zone limite sur laquelle l'animation est active avec la méthode
setSchedulingBounds (), héritée de la classe Behavior.
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'opérateur alpha. setAlpha (null) provoque
l'arrêt de l'animation.
La classe javax.media.j3d.Alpha
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 12
Cette classe dérive des classes SceneGraphObject, NodeComponent et représente un opérateur alpha
qui décrit comment évolue dans le temps la valeur renvoyée par la méthode value (). Cette valeur
comprise entre 0 et 1 est utilisée pour faire varier les comportements d'animation.
Toutes les méthodes qui permettent de modifier les paramètres d'un opérateur alpha ne sont pas
citées, car les différents constructeurs suffisent généralement pour programmer un opérateur.
Champs
Ces constantes déterminent quelles phases montante et/ou descendante doit utiliser cet opérateur.
Constructeurs
public Alpha ()
public Alpha (int loopCount, int mode,
long triggerTime, long phaseDelayDuration,
long increasingAlphaDuration, long increasingAlphaRampDuration,
long alphaAtOneDuration,
long decreasingAlphaDuration, long decreasingAlphaRampDuration,
long alphaAtZeroDuration)
public Alpha (int loopCount, long triggerTime,
long phaseDelayDuration,
long increasingAlphaDuration, long increasingAlphaRampDuration,
long alphaAtOneDuration)
public Alpha (int loopCount, long increasingAlphaDuration)
Ces constructeurs permettent de créer une instance d'opérateur alpha. mode peut être égal à
INCREASING_ENABLE ou DECREASING_ENABLE ou une combinaison de ces deux constantes avec
l'opérateur |. loopCount est égal au nombre de cycles à appliquer ou -1 pour une infinité. Les
différentes phases ont une durée mesurée en milliseconde et permettent de paramétrer la courbe de
l'opérateur alpha.
Par défaut, loopCount est égal à -1, mode à INCREASING_ENABLE, triggerTime à 0, phaseDelayDuration
à 0, increasingAlphaDuration à 1000, increasingAlphaRampDuration à 0, alphaAtOneDuration à 0,
decreasingAlphaDuration à 0, decreasingAlphaRampDuration à 0 et alphaAtZeroDuration à 0.
Principales méthodes
Ces méthodes renvoient la valeur comprise entre 0 et 1 de cet opérateur alpha au moment présent ou
au moment atTime. Elles sont appelées par les classes d'animation pour obtenir un facteur
d'interpolation entre deux valeurs initiale et finale.
Voici l'applet AlphaTest qui permet de tester les combinaisons des paramètres de l'opérateur alpha,
appliquées au déplacement en translation d'un cube à l'aide du comportement d'animation
PositionInterpolator. Les valeurs des propriétés loopCount, mode, triggerTime,
phaseDelayDuration, increasingAlphaDuration, increasingAlphaRampDuration, alphaAtOneDuration,
decreasingAlphaDuration, decreasingAlphaRampDuration et alphaAtZeroDuration de l'opérateur
alpha sont passées directement par des paramètres de même nom. Pour une utilisation avec la
commande java , vous pouvez tester par exemple :
java AlphaTest
java AlphaTest increasingAlphaDuration=10000 increasingAlphaRampDuration=1000
java AlphaTest "mode=INCREASING_ENABLE|DECREASING_ENABLE" decreasingAlphaDuration=1000
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 13
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.*;
// Construction de l'arbre
root.addChild (position);
position.addChild (animation);
position.addChild (createScene ());
return root;
}
Autres exemples
La classe javax.media.j3d.PositionInterpolator
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, Interpolator et représente
un comportement d'animation qui applique une translation à un groupe de transformation, pour faire
varier la position des formes 3D enfants de ce groupe. La translation le long de l'axe x est calculée à
un moment donné en interpolant les positions de départ et de fin en fonction de la valeur de
l'opérateur alpha. La direction de la translation peut être modifiée en utilisant la transformation
axisOfTranslation qui applique une transformation temporaire au repère courant pour changer la
direction de l'axe x.
Constructeurs
Principales méthodes
Exemples
La classe javax.media.j3d.RotationInterpolator
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, Interpolator et représente
un comportement d'animation qui applique une rotation à un groupe de transformation, pour faire
varier la position des formes 3D enfants de ce groupe. La rotation autour de l'axe y est calculée à un
moment donné en interpolant les angles de départ et de fin en fonction de la valeur de l'opérateur
alpha. La direction de l'axe de rotation peut être modifiée en utilisant la transformation
axisOfRotation qui applique une transformation temporaire au repère courant pour changer la
direction de l'axe y.
Constructeurs
Principales méthodes
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.applet.MainFrame;
// Transformation secondes
Transform3D secondsTransformation = new Transform3D ();
secondsTransformation.rotY (-currentSecond * Math.PI / 30);
Transform3D secondsTranslation = new Transform3D ();
secondsTranslation.setTranslation (new Vector3f (0, 0.03f, -(0.75f / 2 - 0.1f)));
secondsTransformation.mul (secondsTranslation);
TransformGroup secondsTransformationGroup = new TransformGroup (secondsTransformation);
// Transformation minutes
Transform3D minutesTransformation = new Transform3D ();
minutesTransformation.rotY (-currentMinute * Math.PI / 30);
Transform3D minutesTranslation = new Transform3D ();
minutesTranslation.setTranslation (new Vector3f (0, 0.02f, -(0.6f / 2 - 0.05f)));
minutesTransformation.mul (minutesTranslation);
TransformGroup minutesTransformationGroup = new TransformGroup (minutesTransformation);
// Transformation hours
Transform3D hoursTransformation = new Transform3D ();
hoursTransformation.rotY ((-currentHour * 60 - currentMinute) * Math.PI / 360);
Transform3D hoursTranslation = new Transform3D ();
hoursTranslation.setTranslation (new Vector3f (0, 0.01f, -(0.4f / 2 - 0.05f)));
hoursTransformation.mul (hoursTranslation);
TransformGroup hoursTransformationGroup = new TransformGroup (hoursTransformation);
// Construction de l'horloge
BranchGroup root = new BranchGroup();
root.addChild (clockTransformationGroup);
clockTransformationGroup.addChild (schedulingBounds);
clockTransformationGroup.addChild (clockFace);
clockTransformationGroup.addChild (secondsAnimationGroup);
clockTransformationGroup.addChild (minutesAnimationGroup);
clockTransformationGroup.addChild (hoursAnimationGroup);
return root;
}
Applet SunEarthMoonMotion .
La classe javax.media.j3d.ScaleInterpolator
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, Interpolator et représente
un comportement d'animation qui applique une homothétie à un groupe de transformation, pour
faire varier l'échelle des formes 3D enfants de ce groupe. Les homothéties sur les 3 axes sont
calculées à un moment donné en interpolant les facteurs minimumScale et maximumScale en fonction
de la valeur de l'opérateur alpha. Les axes d'homothétie peuvent être modifiés en utilisant la
transformation axisOfScale qui applique une transformation temporaire au repère courant.
Constructeurs
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier la transformation qui modifie les axes sur
lesquels s'applique le facteur d'homothétie.
La classe javax.media.j3d.ColorInterpolator
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, Interpolator et représente
un comportement d'animation qui affecte la couleur de diffusion d'un attribut d'apparence de classe
Material, pour faire varier la couleur des formes 3D utilisant cet attribut. La couleur est calculée à
un moment donné en interpolant les couleurs de début et de fin en fonction de la valeur de
l'opérateur alpha.
Constructeurs
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'attribut d'apparence target dont la couleur de
diffusion sera affectée par ce comportement d'animation. target doit avoir la capacité
ALLOW_COMPONENT_WRITE.
La classe javax.media.j3d.TransparencyInterpolator
Cette classe dérive des classes SceneGraphObject, Node, Leaf, Behavior, Interpolator et représente
un comportement d'animation qui affecte la valeur de transparence d'un attribut d'apparence de
classe TransparencyAttributes, pour faire varier la transparence des formes 3D utilisant cet attribut.
La transparence est calculée à un moment donné en interpolant les valeurs de transparence
minimum et maximum en fonction de la valeur de l'opérateur alpha. Pour rappel, une valeur de
transparence est compris entre 0 (totalement opaque) et 1 (totalement transparent).
Constructeurs
Principales méthodes
Ces méthodes permettent d'interroger ou de modifier l'attribut d'apparence target dont la valeur de
transparence sera affectée par ce comportement d'animation. target doit avoir la capacité
ALLOW_VALUE_WRITE.
lune. La terre a une texture utilisant le fichier demo/java3d/images/earth.jpg fourni avec Java 3D :
recopiez-le dans votre répertoire courant.
Comme pour toutes les applets de ce manuel, les tailles et positions du soleil, de la terre et de la
lune utilisent une unité qui permet de les afficher ici avec les dimensions par défaut de l'univers 3D
fixées par la classe Applet3D . Néanmoins, comme Java 3D permet de manipuler des dimensions
qui vont de moins d'un Angström à des millions d'années lumière, il est aussi possible de créer un
univers 3D avec une scène utilisant les dimensions réelles.
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.image.TextureLoader;
// Translation de la terre
Transform3D earthTranslation = new Transform3D ();
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 21
// Translation de la lune
Transform3D moonTranslation = new Transform3D ();
moonTranslation.setTranslation (new Vector3f (-0.15f, 0, 0));
TransformGroup moonTranslationGroup = new TransformGroup (moonTranslation);
// Construction de la scène
BranchGroup root = new BranchGroup();
root.addChild (stars);
root.addChild (sceneTransformationGroup);
// Le soleil et la source lumineuse sont au centre du repère
sceneTransformationGroup.addChild (sunLight);
sceneTransformationGroup.addChild (sun);
// La terre tourne autour du soleil (centre du repère)
sceneTransformationGroup.addChild (sunEarthAnimationGroup);
// La lune tourne autour de la terre
earthTranslationGroup.addChild (moonAnimationGroup);
return root;
}
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 22
Ce manuel vous a présenté une grande partie des différentes possibilités offertes par Java 3D. Cette
bibliothèque a aussi d'autres fonctionnalités qui n'ont pas été développées ici comme :
Si ces fonctionnalités vous intéressent, vous pouvez vous référer aux documentations (en anglais)
fournies pas Sun sur http://www.javasoft.com/, notamment les spécifications de Java 3D (Java 3D API
Specification ) et le tutorial Getting Started with Java 3D .
Solutions alternatives
vendredi 13 octobre 2000 Du C/C++ à Java : Animation 3D Page: 23
Java 3D est une bibliothèque très complète pour réaliser des scènes 3D. Mais cette
solution peut vous sembler lourde à utiliser dans le cadre de développement d'applets par
exemple.
L'applet Cube3D ci-contre et l'applet JavaParse développées par eTeks utilise
uniquement la bibliothèque Java 1.0 et montre qu'il est possible d'obtenir des effets 3D
simples autrement (Cube3D utilise 6 classes pour un total de 12 Ko).
Voici des liens vers d'autres solutions :
Historique
13 Octobre 2000
Version 1.2 :
Ajout de 5 chapitres traitant Java 3D : introduction, les objets 3D, les attributs d'apparence, l'éclairage 3D
et les animations 3D.
Modification des paragraphes sur les packages et sur les classes d'erreurs.
Remplacement dans le texte du mot variable de classe ou d'instance par le mot champ de classe ou
d'instance, pour éviter d'écrire des aberrations comme variable constante !
Corrections de fautes de frappe et d'erreurs de texte.
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 (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 :
vendredi 13 octobre 2000 Historique du site Page: 2