Vous êtes sur la page 1sur 424

Du C/C++ à JavaT M

Mise à jour du 13/10/2000 (version 1.2)

Emmanuel PUYBARET

http://www.eteks.com
Copyrights

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

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

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

Marques déposées

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

eTeks est indépendant de Sun Microsystems, Inc.

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

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

Table des Matières

Le langage Java
La bibliothèque Java 1.0
Les extensions Java

Copyrights

Avant propos

Le langage Java
Démarrer en Java

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


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

Les notions de base

Objets, classes et héritage


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

Création et utilisation des classes

Déclaration des classes et des interfaces


Identifiants
Les classes
Les interfaces
Déclaration des champs
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 2

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

Objets, tableaux et chaînes de caractères

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

Les instructions et les opérateurs

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

Les exceptions

throw, try, catch,...


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

Les threads

Définition d'un thread


Le partage du temps entre threads
La création d'un thread
Applet Chrono
Les états d'un thread
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 3

La synchronisation des threads


Utilisation de synchronized
Applet AfficheurDeCalcul
Synchronisation avec wait () et notify ()
La classe Thread

Les classes internes

Les classes internes


Syntaxe
Utilisation
Les classes anonymes
Autres nouveautés Java 1.1
Initialisations d'instance
Initialisation de tableaux
Utilisation du mot-clé class
Variables locales et paramètres final

Conventions d'écriture et portage

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

La bibliothèque Java 1.0


La bibliothèque Java 1.0

Les packages de la bibliothèque Java 1.0


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

Les classes de bases

Gestion des objets


La classe java.lang.Object
L'interface java.lang.Cloneable
La classe java.lang.Class
La classe java.lang.ClassLoader
La classe java.lang.Compiler
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 4

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

Les outils Java

Gestion de collections de données


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

La gestion des fichiers et des flux de données

Gestion des entrées-sorties


Mode d'accès aux données
Gestion de l'accès aux données avec les exceptions
Manipulation des fichiers
L'interface java.io.FilenameFilter
La classe java.io.File
La classe java.io.FileDescriptor
Gestion des flux de données
Accès à un flux de données en lecture
Application LectureFichier
Application NumerotationLigne
La classe java.io.InputStream
La classe java.io.FileInputStream
La classe java.io.PipedInputStream
La classe java.io.ByteArrayInputStream
La classe java.io.StringBufferInputStream
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 5

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

Les accès au réseau

Accès via une URL


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

Les applications et les applets

Les applications Java


Les applets
Caractéristiques
La classe java.applet.Applet
L'intégration des applets dans les navigateurs
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 6

L'interface java.awt.AppletContext
L'interface java.applet.AppletStub
Applet PlayApplet
L'interface java.applet.AudioClip
Applet Piano
Transformer une applet en application

Les composants de l'interface utilisateur

Les composants Java


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

Les containers et la disposition des composants

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

La gestion de l'interface utilisateur


vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 7

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

Plus loin avec Java...


vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 8

Les évolutions du langage


Java 1.1
Java 2
Conclusion

Les extensions Java


Java 3D

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

Les formes de base


Les différentes formes
Applet SimpleObjects
La classe javax.media.j3d.Shape3D
La classe com.sun.j3d.utils.geometry.ColorCube
La classe com.sun.j3d.utils.geometry.Primitive
La classe com.sun.j3d.utils.geometry.Box
La classe com.sun.j3d.utils.geometry.Sphere
La classe com.sun.j3d.utils.geometry.Cylinder
La classe com.sun.j3d.utils.geometry.Cone
Les constructions géométriques
Les différents types de constructions géométriques
Les différents algorithmes de construction
Constructions géométriques indicées
Applet Pyramid
La classe javax.media.j3d.NodeComponent
La classe javax.media.j3d.Geometry
vendredi 13 octobre 2000 Du C/C++ à Java : Table des matières Page: 9

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

Les attributs d'apparence

Les différents attributs


La classe javax.media.j3d.Appearance
Les attributs de couleur
Applet Clown
La classe javax.media.j3d.ColoringAttributes
La classe javax.media.j3d.Material
Les attributs d'affichage des facettes
Applet SphereConstruction
La classe javax.media.j3d.PolygonAttributes
La classe javax.media.j3d.PointAttributes
La classe javax.media.j3d.LineAttributes
Les attributs de transparence et de rendu
Applet WaterGlass
La classe javax.media.j3d.TransparencyAttributes
La classe javax.media.j3d.RenderingAttributes
Les attributs de texture
Applet SimpleTexturedObjects
La classe javax.media.j3d.Texture
La classe javax.media.j3d.Texture2D
La classe javax.media.j3d.TextureAttributes
La classe javax.media.j3d.ImageComponent
La classe javax.media.j3d.ImageComponent2D
La classe com.sun.j3d.utils.image.TextureLoader

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).

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

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

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

Interpréteur de fonctions

Démineur Java

Historique du manuel

Dernière mise à jour le 13/10/2000 (version 1.2)


vendredi 13 octobre 2000 Du C/C++ a Java : Avant propos Page: 1

Avant propos

Du C/C++ à Java est un manuel divisé en 3 parties :

Le langage Java décrit la version 2 du langage Java. Les similitudes et les


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

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

Comment utiliser ce manuel ?


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

Si vous voulez apprendre Java par l'exemple :


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

Ce manuel comporte 25 chapitres et représente environ 3 Mo de données (fichiers


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

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

Ces symboles vous signalent les différences entre le C/C++ ou le C++, et Java
(utilisation différente, absence ou ajout dans le langage,...).
vendredi 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 !"

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


Introduction

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

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

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

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

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

Description de l'environnement et des commandes


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

Principe de fonctionnement
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 :

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

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

Pourquoi ne pas interpréter directement le programme Java ?

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

Pourquoi ne pas compiler directement un exécutable ?

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

Limites du système :

A vu d'œil, ce système a l'air d'être parfait : Vous écrivez des programmes, les compilez
et le résultat de la compilation fonctionnera en l'état sur toutes les machines ! Forcément,
il doit y avoir un os quelque part...
Le plus gros problème d'un programme Java est son manque de rapidité d'exécution :
L'interpréteur prend forcément un certain temps pour interpréter le code des fichiers
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.

Description des principales commandes

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

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

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

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

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


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

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

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

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

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


-classpath .

Exemple : javac Cube.java


vendredi 13 octobre 2000 Du C/C++ à Java : Démarrer en Java Page: 5

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.

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

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

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


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

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


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

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


-classpath .

Exemple : java ClasseExec

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

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

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


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

L'argument de CODE, ClasseApplet, doit correspondre à un fichier .class qui est la


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

appletviewer crée pour chaque balise APPLET, une fenêtre de taille (WIDTH,HEIGHT), où
vendredi 13 octobre 2000 Du C/C++ à Java : Démarrer en Java Page: 6

sera exécutée l'applet correspondant à la classe de CODE.


Entre <APPLET ...> et </APPLET> peuvent figurer la description d'un certain nombre de
paramètres à passer à l'applet, sous la forme <PARAM NAME="appletAttributei"
VALUE="valuei">. Tout le reste du fichier HTML est ignoré.
Vous pouvez aussi exécuter les applets d'un fichier HTML avec un navigateur intégrant
un interpréteur Java. Ceci vous est même vivement conseillé afin de pouvoir vérifier
comment s'intègre vos applets au milieu du texte dans lequel elles sont utilisées.

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

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

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

Exemple : appletviewer index.html

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

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


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

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

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

public class HelloWorld extends Applet


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

public static void main (String [ ] args)


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

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


Vous pouvez le compiler de la manière suivante : javac HelloWorld.java
Voici le source HMTL. Donnez lui le nom que vous voulez (MonPremierProgrammeJava.html si ça
vous chante !) :
vendredi 13 octobre 2000 Du C/C++ à Java : Démarrer en Java Page: 7

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

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


<APPLET CODE="HelloWorld.class" WIDTH=150 HEIGHT=25>
</APPLET>
</BODY>
</HTML>

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


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

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

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

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

Windows 95/98/NT MacOS


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

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.

Enregistrer vos programmes dans le répertoire où


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

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

Les notions de base

Objets, classes et héritage


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

Objets, classes et héritage


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

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

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

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

Notre banquier décide de s'informatiser, et engage un informaticien expert dans la technologie


magique dont il entend parler dans tous les journaux : "les technologies objets".
Celui-ci lui explique sa démarche : "Pour un informaticien, chaque relevé que vous imprimez est un
vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 2

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 :

Figure 3. Graphe d'héritage

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

La différence principale entre une structure C et une classe est évidente : on ne peut pas déclarer
des méthodes (ou des fonctions) à l'intérieur du corps d'une structure C. A l'opposé, Java ne permet
pas de déclarer de méthodes en dehors du corps d'une classe.
Une classe peut comporter uniquement des 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;
};

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


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

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

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

Les opérateurs * et & n'existent pas en Java. Le seul opérateur d'accès aux 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 mots-clés de Java

abstract default if private throw

boolean do implements protected throws

break double import public transient

byte else instanceof return try

case extends int short void

catch final interface static volatile

char finally long super while

class float native switch

const for new synchronized

continue goto package this

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 mots-clés du C/C++ absent en Java


vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 5

auto extern register typedef

#define friend sizeof union

delete inline struct unsigned

enum operator template virtual

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

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

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


21474836487)
Le type int occupe toujours la même taille en Java : 32
bits.
long Entier signé occupant 64 bits (valeurs de 0L
-9223372036854775808 à 9223372036854775807)
Le type long occupe 64 bits en Java contrairement à 32
bits en C.
float Nombre à virgule flottante occupant 32 bits (IEEE 754) 0.0f

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

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


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

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


à la compilation la bonne valeur Unicode pour ces caractères.
Utilisez alors les valeurs hexadécimales ('\u00e9' au lieu de 'é'
par exemple).
Le type char occupe 16 bits en Java contrairement à 8
bits en C. Utilisez byte pour un 8 bits. Les valeurs
littérales des caractères spéciaux sont les mêmes : '\n'
pour un saut de ligne, '\t' pour une tabulation, '\'' pour
le caractère ', '\"' pour le caractère ", '\\' pour le
caractère \,...
boolean Booléen dont la valeur est true ou false false
Le type boolean n'existe pas en C. En Java, il est
obligatoire dans certaines expressions (if (...) par
vendredi 13 octobre 2000 Du C/C++ à Java : Les notions de base Page: 6

exemple).

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

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


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

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

Chacun des types de base Java occupe toujours la même place mémoire quelque soit la plate-forme
d'exécution. La taille d'un entier de type int est toujours de 32 bits (ou 4 octets).

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

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

Structure d'un programme


La structure d'un programme Java est plus simple qu'en C.
Chaque fichier qui le compose, se divise en trois parties (optionnelles) :

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

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


package nomPackage;

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


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

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


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

class NouvelleClasse2
{
// Corps de NouvelleClasse2
}

interface NouvelleInterface
{
// Corps de NouvelleInterface
}

// ...

/* Fin du fichier NouvelleClasse.java */

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

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

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

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


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

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

// ...
}

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


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

Les packages
import

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 nomClasse; // Importer une classe sans package


import nomPackage.nomClasse; // Importer une classe d'un package
import nomPackage.*; // Importer toutes les classes d'un package

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.

import est optionnel dans les cas suivants :

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 :

1. Utiliser import nomPackage.nomClasse; au début du fichier et écrire nomClasse ensuite.


2. Utiliser import nomPackage.*; au début du fichier et écrire nomClasse ensuite.
3. Ecrire nomPackage.nomClasse à chaque fois que vous voulez utiliser la classe nomClasse.

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.

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


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

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

L'équivalent de #include est plus ou moins import.


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

Définir un package

import permet d'importer n'importe quelle classe d'une bibliothèque, mais 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

Création et utilisation des classes

Déclaration des classes et des interfaces


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

Déclaration des classes et des interfaces

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

Identifiants

Les identifiants que vous créez en Java (classes, interfaces, 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

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

// Déclararation d'une classe simple


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

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


ModifieurDeClasse class NomDeClasseDerivee extends NomDeSuperClasse
{
// Corps de NomDeClasseDerivee :
// Déclaration des champs, des méthodes, des constructeurs
// et/ou initialisations static
}
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 2

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


ModifieurDeClasse class NomDeClasse2 implements NomInterface //, NomInterface2, ...
{
// Corps de NomDeClasse2 :
// Déclaration des champs, des méthodes, des constructeurs
// et/ou initialisations static
// et implémentation des méthodes de nomInterface
}

// 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).

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


vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 3

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


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

Les interfaces

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

// Déclararation d'une interface simple


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

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


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

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

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

A quoi sert une interface et quelle est la différence avec une classe abstract ?
Tout d'abord, vous noterez qu'une classe ne peut hériter que d'une super classe, mais par contre peut
implémenter plusieurs interfaces. Ensuite, une classe abstract peut déclarer des 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 Classe1 extends SuperClasse1 implements CouleurDominante


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

// Autres méthodes éventuelles


}

class Classe2 extends SuperClasse2 implements CouleurDominante


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

// Autres méthodes éventuelles


}

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

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

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

class Classe1 extends SuperClasse1 implements ConstantesCommunes


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

class Classe2 extends SuperClasse2 implements ConstantesCommunes


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

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

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

Déclaration des champs


Le corps d'une classe est un ensemble de déclarations de champs (fields ) et de méthodes
implémentées ou non, déclarées dans n'importe quel ordre.

Syntaxe

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


vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 5

class Classe1
{
// Déclaration de champs
TypeChamp champ1;
TypeChamp champ2, champ3;
ModifieurDeChamp TypeChamp champ4;
TypeChamp champ5 = valeurOuExpression ; // Initialisation d'un champ

// Création d'une référence sur un tableau


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

// ...
}

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.

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

public, protected ou private :


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

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

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++;
}

// ...
}

Déclaration des méthodes


Syntaxe
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 8

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

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

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


{
// Corps de methode2 ()
}

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


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

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


// Dans ce cas, Classe1 doit être aussi abstract
abstract ModifieurDeMethode TypeRetour methode4 (/* ... */);

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


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

// ...
}

TypeRetour peut être :

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

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

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

public, protected ou private :


Une méthode public est accessible partout où est accessible la classe Classe1 dans
laquelle elle est déclarée. Ce sont les méthodes public que l'utilisateur d'une classe peut
appeler.
Une méthode protected est accessible par les autres classes du même package que
Classe1, et par les classes dérivées de Classe1.
Une méthode private n'est accessible qu'à l'intérieur du corps de Classe1. Les méthodes
private sont typiquement des routines internes de calcul pour lesquels il n'y a pas de
raison de donner un accès à l'utilisateur d'une classe.
static : Si une méthode est static, c'est une méthode de classe qui ne peut utiliser que les
champs et les méthodes de Classe1, qui sont déclarées static. L'appel à cette méthode se fait
grâce à l'opérateur point (.) en indiquant le nom de la classe ou une référence à un objet
(Classe1.methode () ou objetClasse1.methode ()).
A l'opposé, si une méthode n'est pas static, c'est une méthode d'instance, accessible pour
chaque instance objetClasse1 de Classe1 grâce à l'opérateur point (.) , de la manière
objetClasse1.methode ().
Les méthodes déclarées dans le corps d'une interface ne peuvent être static.
final : Une méthode final ne peut pas être outrepassée par les classes dérivant de Classe1.
abstract : Une méthode abstract permet de déclarer une méthode d'instance sans en donner
l'implémentation, et ne peut apparaître qu'au sein d'une classe abstract. Toute classe non
abstract dérivée de cette classe, doit implémenter cette méthode, en l'outrepassant.
Les méthodes déclarées dans le corps d'une interface sont implicitement abstract et public.
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 9

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).

Surcharge des méthodes

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

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

class Classe2 extends Classe1


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

void methodeSurchargee (float nombre, short param)


{
// Corps de methodeSurchargee ()
}
}

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

Constructeur

Chaque 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
}

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


{
// Corps du constructeur
}

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


{
// Corps du constructeur
}
}

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

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

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

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

class Classe1 class Classe1


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

Le constructeur Classe1 (int valeur) sera appelé avec la valeur donnée par l'instruction de création
d'un objet de classe Classe1. Ce constructeur permet ainsi de créer un objet de classe Classe1 dont
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 :

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


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

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

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

class Classe2 extends Classe1


{
int var2;
Classe2 (int val)
{
// Appel du constructeur de Classe1 avec la valeur 3
super (3);
var2 = 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.

Création d'objets : opérateur new


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

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

Un nouvel objet de classe Classe1 est créé, l'espace mémoire nécessaire pour les 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;
}

/* Fonction renvoyant la division */ // Méthode renvoyant la division


/* entre les champs de objet */ // entre les champs de cet objet
int division (Classe1 *objet) public int division ()
{ {
return objet->var1 / objet->var2; return var1 / var2;
} }

void main () public static void main


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

Outrepasser une méthode


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

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

class Compte
{
private int numero;
protected float soldeInitial; // 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;
}
}

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


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

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

void ajouterOperation (float debitCredit)


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

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


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

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


class CompteEpargne extends Compte
{
private float tauxInteretPourcentage;

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

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


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

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

CompteDepot compte105 = new CompteDepot (101);


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

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

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


// des comptes. Cette méthode prend en paramètre une
// référence de classe Compte : Les objets désignés par
vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 17

// compte105 et compte1003 sont d'une classe qui dérive


// de la classe Compte, c'est pourquoi ils peuvent être
// acceptés en paramètre comme des objets de classe Compte
editerSoldeCompte (compte101);
editerSoldeCompte (compte105);
editerSoldeCompte (compte1003);
}

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


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

Le résultat de ce programme donne ceci :

Compte : 105 Solde = 201.1


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

La méthode editerSoldeCompte () reçoit en paramètre la variable compte. compte est une référence
désignant une instance de la classe Compte, ou d'une des classes dérivées de Compte, ici CompteDepot
ou CompteEpargne.
Que se passe-t-il à l'appel de la méthode calculerSolde () sur cette référence ?
La Machine Virtuelle connait à l'exécution la classe de l'objet désigné par la référence compte : en
plus, des 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;
}

class Classe2 extends Classe1


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

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

Dans cet exemple, Classe2 a besoin de donner une valeur différente à cste1 pour les objets de cette
classe : si on redéclare cette constante dans Classe2 avec une valeur différente, on pourrait s'attendre
à ce que objet1.cste1 vaille 1 puisque objet1 désigne un objet de classe Classe2. En fait,
objet1.cste1 renvoie la valeur de cste1 de la classe Classe1 (pas de ligature dynamique sur les
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;
}
}

class Classe2 extends Classe1


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

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

Utilisation de classes abstract

Les classes abstract sont une catégorie spéciale de classe : il est impossible de créer une instance
d'une classe abstract classeAbstract, mais par contre il est possible de déclarer 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 :

abstract class Vehicule


{
abstract int nombreDeRoues ();
}

class Velo extends Vehicule


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

class Voiture extends Vehicule


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

class VoitureAvecRemorque extends Voiture


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

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

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

unVehicule = unVelo; // cast de Voiture vers Vehicule


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

Dans cet exemple, unVehicule est une référence permettant de désigner un objet de classe Vehicule
ou toute autre classe dérivée de Vehicule. Quand nombreDeRoues () est invoquée sur la référence
unVehicule, l'interpréteur va consulter la classe réelle d'appartenance de l'objet référencé par
unVehicule ; une fois, qu'il a déterminé cette classe, il va appeler la méthode nombreDeRoues () de
cette classe.
vendredi 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) :

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


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

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


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

class Classe2 implements MethodeX


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

// Déclaration d'une classe utilisant


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

void appelMethodeXClasse1 () void appelMethodeXClasse1 ()


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

void appelMethodeXClasse2 () void appelMethodeXClasse2 ()


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

Destruction des objets


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

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 :

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


vendredi 13 octobre 2000 Du C/C++ à Java : Création et utilisation des classes Page: 22

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

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

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

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

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


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

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


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

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


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

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

void main () public static void main


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

L'instruction liste = null entraîne que l'unique référence sur l'objet de tête de liste est perdue, donc
le Garbage Collector peut supprimer cet objet. Quand il le supprime, 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

Objets, tableaux et chaînes de caractères

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

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

public final Class getClass ()

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

public boolean equals (Object obj)

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

public int hashCode ()

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

protected Object clone () throws CloneNotSupportedException


vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 2

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 () :

class Classe0 implements Cloneable


{
double unNombre;

public Object clone () throws CloneNotSupportedException


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

class Classe1 implements Cloneable


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

public Object clone () throws CloneNotSupportedException


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

public String toString ()

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

public final void notify () throws IllegalMonitorStateException


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

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

protected void finalize() throws Throwable


vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 3

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

public static Class forName (String className) throws ClassNotFoundException

Renvoie un objet de classe Class correspondant à la classe ou à l'interface de nom className.


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

public Object newInstance () throws InstantiationException, IllegalAccessException

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

public ClassLoader getClassLoader ()

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

public String getName ()

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

public Class getSuperclass ()

Renvoie la super classe d'une classe.

public Class [ ] getInterfaces ()

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

public boolean isInterface ()

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

public String toString ()

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

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

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

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

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

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


int [ ] tableauVide = new int [0];

// Création de tableaux initialisés


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

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


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

import java.lang.reflect.*;

public class ClasseJava11


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

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

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

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

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


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

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


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

return table1;
}
}

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

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

Type des éléments


Caractère
du tableau
B byte

C char

D double

F float

I int

J long

L classe ou interface
S short

Z boolean

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


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

Les tableaux implémentant implicitement l'interface Cloneable, il est possible de les dupliquer grâce
à la méthode clone () de la classe Object. Si le tableau comporte plus d'une dimension, il faut
dupliquer aussi les sous-tableaux pour obtenir une copie complète, comme dans l'exemple :

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

// Duplication de la première dimension


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

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

Les chaînes de caractères


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

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

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

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

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

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

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

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


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

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

La classe String
La classe String qui est final comporte de nombreuses méthodes. En voici la liste (quelques
méthode mineures ont été ajoutées à partir de Java 1.1) :
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 8

Constructeurs

public String (String value)

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

public String (char value [ ])

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

public String (byte ascii [ ], int hibyte)

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

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

Méthodes

public int length ()

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

public char charAt (int index)

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

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

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).

public int hashCode ()

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.

public boolean equals (Object anObject)

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

public boolean equalsIgnoreCase (String anotherString)

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

public int compareTo (String anotherString)

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

public boolean regionMatches (int toffset, String other, int ooffset, int len)
public boolean regionMatches (boolean ignoreCase, int toffset,
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 9

String other, int ooffset, int len)


public boolean startsWith (String prefix, int toffset)
public boolean startsWith (String prefix)
public boolean endsWith (String suffix)

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

public int indexOf (int ch)


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

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

public String substring (int beginIndex)


public String substring (int beginIndex, int endIndex)

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

public String concat (String str)

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

public String replace (char oldChar, char newChar)

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

public String toLowerCase ()


public String toUpperCase ()

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

public String trim ()

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

public static String valueOf (char data [ ])


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

Ces méthodes permettent de créer des chaînes de caractères à partir de tableaux de caractères.

public static String valueOf (Object obj)

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

public static String valueOf (boolean b)


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

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

public String intern ()

public String toString ()


vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 10

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

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

Constructeurs

public StringBuffer ()

Construit une chaîne vide.

public StringBuffer (int length) throws NegativeArraySizeException

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

public StringBuffer (String str)

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

Méthodes

public int length ()

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

public int capacity ()

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

public synchronized void ensureCapacity (int minimumCapacity)

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

public synchronized void setLength (int newLength)


throws IndexOutOfBoundsException

Modifie la taille de la chaîne. Si la chaîne actuellement mémorisée est plus longue, elle sera
tronquée. Les caractères éventuellement ajoutés sont nuls ('\u0000'), pour que length () renvoie la
valeur newLength.

public synchronized char charAt (int index)


throws IndexOutOfBoundsException

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

public synchronized void getChars (int srcBegin, int srcEnd,


char dst [ ], int dstBegin)
throws IndexOutOfBoundsException

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

public synchronized void setCharAt (int index, char ch)


throws IndexOutOfBoundsException

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

public synchronized StringBuffer append (Object obj)


public synchronized StringBuffer append (String str)
public synchronized StringBuffer append (char str [ ])
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 11

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

public synchronized StringBuffer append (char str [ ],


int beginIndex, int length)
throws IndexOutOfBoundsException

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

public synchronized StringBuffer append (char c)


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

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

public synchronized StringBuffer insert (int offset, Object obj)


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

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

public synchronized StringBuffer insert (int offset, char c)


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

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

public synchronized StringBuffer reverse ()

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

public String toString ()

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

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

public static InputStream in

Entrée standard pour saisir des caractères.

public static PrintStream out

Sortie standard pour écrire des messages.

public static PrintStream err

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

Méthodes

public static void arraycopy (Object source, int srcOffset,


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

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

public static long currentTimeMillis ()

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

public static void exit (int status) throws SecurityException

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

public static void gc ()

Lance volontairement le Garbage Collector.

public static void runFinalization ()

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

public static SecurityManager getSecurityManager ()


public static void setSecurityManager (SecurityManager s)
throws SecurityException

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


appelé qu'une seule fois.

public static String getProperty (String property) throws SecurityException


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

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

Les valeurs par défaut des propriétés du système peuvent être modifiées directement au lancement
de la Machine Virtuelle en utilisant une ou plusieurs options -Dpropriete=valeur avec la
vendredi 13 octobre 2000 Du C/C++ à Java : Objets, tableaux et chaînes de caractères Page: 13

commande java .

public static Properties getProperties () throws SecurityException


public static void setProperties (Properties props) throws SecurityException

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

public static void load (String filename)


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

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

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

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

public class SystemProperties


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

Les instructions et les opérateurs

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

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

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

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

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

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


}

Une instruction peut être éventuellement vide (ne rien faire) : dans ce cas, elle se note ; . Ce type
d'instruction est quelques fois utilisé comme corps des instructions de boucles.
TypeVariable est soit un type de base, soit le nom d'une classe.
A partir de Java 1.1, une variable locale ou un paramètre peut être déclaré final, pour garantir que
la valeur qu'ils contiennent ne sera pas modifiée.
Une variable locale ne peut être déclarée deux fois avec le même nom à l'intérieur d'un même bloc,
ni porter le même nom qu'un des paramètres d'une méthode. De plus, dans le cas d'un bloc imbriqué
dans un autre, une variable locale ne peut porter le même nom qu'une autre variable déclarée dans
le bloc externe.
Une variable locale (ou un paramètre d'une méthode) variable1 déclarée dans un bloc d'une classe
Classe1 peut porter le même nom 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 ... else, switch

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

if (expressionBooleenne )
instructionOuBlocSiVrai

if (expressionBooleenne )
instructionOuBlocSiVrai
else
instructionOuBlocSiFaux

switch (expressionEntiere )
{
case expressionConstante1 :
instructionOuBlocCas1
break; // ou return; ou rien pour passer au case suivant
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.

instructionOuBloc... peut éventuellement contenir les instructions return; ou return valeur;


pour sortir de la méthode courante (valeur doit être du type de retour de la méthode).

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

while, do ... while, for

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

while (expressionBooleenne )
instructionOuBloc

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


instructionOuBloc est exécuté.

do
instructionOuBloc
while (expressionBooleenne );

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

for (expressionInit ; expressionBooleenne ; expressionIncrementation )


instructionOuBloc

for (TypeVariable variableLocale = valeurOuExpression ;


expressionBooleenne ;
expressionIncrementation )
instructionOuBloc

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

instructionOuBloc peut éventuellement contenir les instructions suivantes :

break; pour sortir de la boucle courante.


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

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

{
expressionInit ;
while (expressionBooleenne )
{
instructionOuBloc ;
expressionIncrementation ;
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 .

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


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

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

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


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

return tableau;
}
}

Comme pour if, l'expression déterminant la poursuite d'une boucle while ou for doit être du type
boolean.
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 :

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


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

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

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


++variable1, --variable1, variable1++ ou variable1--.
Un appel à une méthode, de la forme methode (arg1, arg2, ...) ou expression .methode
(arg1, arg2, ...). expression est soit une classe ou une interface si methode () est une
vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 5

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.

Utilisation de this et de super

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

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

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

La méthode equals () de la classe Object requiert comme paramètre une référence sur un objet. Ici,
l'argument this lui est passé, qui en fait désigne objet1 (au passage le résultat est forcément true).
this ne peut être utilisé que dans des méthodes non static (une méthode static n'est pas invoquée
sur un objet).
vendredi 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);
} }
}

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

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


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

this peut être utilisé seul pour désigner une référence d'un objet, instance de la classe Classe1 ou
suivi de l'opérateur point (.) pour accéder aux 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;

Classe1 (int var)


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

En fait, à chaque fois que vous utilisez une méthode ou 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

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


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

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

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

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

L'opérateur instanceof

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

class Classe0
{ }

class Classe1 extends Classe0


{ }

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

boolean resultat1 = objet1 instanceof Classe1; // true


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

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

Java introduit l'opérateur instanceof qui permet de vérifier si une référence désigne un objet qui
est une instance d'une classe ou d'une interface donnée.

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


vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 9

OPERATEUR DESCRIPTION

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


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

delete Opérateur de destruction d'objet

Les liens définis dans le tableau désignent les endroits où il est traité de l'absence de ces opérateurs.

Les conversions (casts)


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

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

Conversions entre types de base avec gain de précision

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


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

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

Conversions entre types de base avec perte de précision

Conversions de byte en char.


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

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

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

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

Toutes les conversions suivantes sont possibles sans cast explicite dans la programmation :
1. Conversion d'une référence de classe ClasseY dans une référence de classe ClasseX, si ClasseY est
une classe dérivée directement ou indirectement de ClasseX (qui peut être la classe Object). Sachant
que toute objet objetClasseY de la classe ClasseY hérite des 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
{ }

interface InterfaceZ extends InterfaceX


{ }

class ClasseX implements InterfaceX


{ }

class ClasseY extends ClasseX


{ }

class ClasseZ implements InterfaceZ


{ }

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

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


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

InterfaceZ [] tabInterfaceZ = new InterfaceZ [1];

// Cast implicite sur les objets


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

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


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

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


objetInterfaceX = objetInterfaceZ; // cast de InterfaceZ vers InterfaceX

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


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

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


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

InterfaceX [] tab2InterfaceX = tabClasseX;


tabInterfaceX = tabInterfaceZ;

InterfaceZ [] tabInterfaceZ = tabClasseZ;


tabInterfaceX = tabInterfaceZ;

tabObjets = tabInterfaceX;
}
}

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

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

interface InterfaceX
{ }

interface InterfaceZ extends InterfaceX


{ }

class ClasseX
{ }

class ClasseY extends ClasseX implements InterfaceX


{ }

class ClasseZ implements InterfaceZ


{ }

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

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


Object tab1 = new ClasseX [1];

// Cast explicite sur les objets


vendredi 13 octobre 2000 Du C/C++ à Java : Les instructions et les opérateurs Page: 12

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


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

InterfaceX objet InterfaceX =


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

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

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

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


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

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

figure 8. Conversions implicites/explicites

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

Classe2 objetClasse2 = new Classe2 ();


Classe1 objetClasse1 = (Classe1)objetClasse2;

objetClasse1 et objetClasse2 ne sont que des références sur un objet de classe Classe2.
Le fait d'effectuer le cast (Classe1) (qu'il soit explicite ou non) ne transforme en rien l'objet de
classe Classe2. Ceci est très important, car si vous invoquez une méthode de Classe1, par
l'expression objetClasse1.methode1 () et que methode1 () est outrepassée dans Classe2, ce sera
effectivement la méthode methode1 () de Classe2 qui sera appelée, car objetClasse1 désigne un
objet de Classe2.
vendredi 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.

Priorité des opérateurs


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

OPERATEUR ASSOCIATIVITE DESCRIPTION


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

Les exceptions

throw, try, catch,...


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

throw, try, catch,...

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

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

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

Syntaxe

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

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


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

dérivées de ClasseException respecte la syntaxe suivante :

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 ?
}
}

void methodeY () throws ClasseException


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

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

figure 9. Chemin parcouru lors du traitement d'exceptions

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

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

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


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

public class EssaiException


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

int main () public static void main


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

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

public class InstantiationAvecNom


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

public static void main (String [ ] args)


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

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


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

Autres exemples
vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 6

Applets Chrono , ObservateurCalcul , EchoClient , PaperBoardClient , PlayApplet ,


CalculetteSimple , BoutonsNavigation , AnimationFleche , ScrollText et Horloge .
Applications LectureFichier , NumerotationLigne , ConcatenationFichiers , TestProtocole ,
HelloFromNet , EchoServer et PaperBoardServer .

Avantages des exceptions

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

Chaque exception est une instance d'une classe : cette classe peut comporter toute sorte de
méthodes et de 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 ();
}

private void methode1 () throws Exception


{
methodeQuiDeclencheUneException ();
}

private void methode2 () throws Exception


{
methode1 ();
methodeQuiDeclencheUneException ();
}

private void methode3 () throws Exception


{
methode1 ();
}

public void methodePrincipale ()


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

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

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

abstract class Classe1


{
abstract Class chercherClasse (String nomClasse);
}

class Classe2 extends Classe1


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

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


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

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

class Classe1
{
void methode1 () throws Exception
{
vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 8

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

class Classe2 extends Classe1


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

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

class Classe1 implements Cloneable


{
// ...

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


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

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

La classe 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

public String getMessage ()


vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 9

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

public void printStackTrace ()


public void printStackTrace (PrintStream s)

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

public Throwable fillInStackTrace ()

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

public String toString ()

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

Les exceptions Runtime


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

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.

Les classes d'erreurs


Les classes d'erreurs dérivent de la classe Error, qui dérive elle-même de la classe Throwable. Elles
sont généralement provoquée par la Machine Virtuelle Java à l'exécution, suite à un problème sur le
chargement ou l'utilisation des classes, ou sur la Machine Virtuelle elle-même. Elles sont
intéressantes à analyser quand elles sont déclenchées par un programme. Voici la liste de ces
erreurs :

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 ?

java.lang.ClassCircularityError : Une référence circulaire a été détectée dans la hiérarchie d'une


classe.
java.lang.ClassFormatError : Une classe n'est pas au bon format. Cette erreur peut être déclenchée
par la méthode defineClass
() de la classe ClassLoader.
java.lang.IncompatibleClassChangeError : Cette classe est la super classe des classes d'erreurs
déclenchées quand la Machine Virtuelle Java utilise un champ ou une méthode qui a été changée ou
supprimée d'une classe. Comme le compilateur interdit ces erreurs, elles ne peuvent survenir que si
la Machine Virtuelle utilise des fichiers .class qui ne sont pas à jour (voir aussi la super classe
LinkageError).
java.lang.AbstractMethodError : Tentative d'appeler une méthode abstract.
java.lang.IllegalAccessError : Tentative d'accéder un champ ou une méthode inaccessible
(elle est devenue private par exemple).
java.lang.InstantiationError : Tentative d'instancier une classe abstract ou une interface.
java.lang.NoSuchFieldError : Tentative d'accéder un champ qui n'existe pas.
java.lang.NoSuchMethodError : Tentative d'accéder une méthode qui n'existe pas.
java.lang.NoClassDefFoundError : La classe requise n'a pas été trouvée par la Machine Virtuelle
Java. Cette erreur survient le plus souvent parce que la classe n'a pas été retrouvée avec le
CLASSPATH utilisé.
java.lang.UnsatisfiedLinkError : Une méthode native n'a pas été trouvée par la Machine Virtuelle
Java.
java.lang.VerifyError : La classe est incohérente.
java.lang.ThreadDeath : Cette erreur est déclenchée par la méthode stop () sans paramètre de la classe
vendredi 13 octobre 2000 Du C/C++ à Java : Les exceptions Page: 11

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.

Les autres exceptions


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

java.lang.ClassNotFoundException : Une classe ou une interface d'un certain nom n'a pas été trouvée.
Cette exception peut être déclenchée par 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).

java.net.MalformedURLException : Une chaîne de caractères décrivant un URL était mal formattée ou


indiquait un protocole inconnu.
java.net.ProtocolException : Erreur sur le protocole réseau.
java.net.SocketException : Une opération sur un socket ne s'est pas déroulé correctement.
java.net.UnknownHostException : L'hôte (host) d'un réseau n'a pu être atteint.
java.net.UnknownServiceException : La connection réseau ne supporte pas ce service.

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


java.awt.AWTException : Utilisée pour certaines erreurs dans AWT.

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

Définition d'un thread


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

Définition d'un thread


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

Ne confondez pas le multi-threads avec le traitement événementiel grâce à un timer, dont le


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

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

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

Le partage du temps entre threads

Comment faire tourner plusieurs threads en même temps alors que votre ordinateur ne possède
qu'un seul microprocesseur ? La réponse vient de manière assez évidente : A tout moment, il n'y a
en fait qu'un seul thread en cours d'exécution, et éventuellement d'autres threads en attente
d'exécution.
Si le système permet de partager le temps d'exécution entre les différents threads, il leur donne
chacun leur tour un petit temps d'exécution du processeur (quelques millisecondes). Sinon, chaque
fois qu'un thread a terminé d'exécuter une série d'instructions et qu'il cède le contrôle au système, le
système exécute un des threads en attente, et ainsi de suite... Si la série d'instructions qu'exécute
chaque thread prend un temps assez court, l'utilisateur aura l'illusion que tous les threads
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 !

La création d'un thread


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

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

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


(Runnable objetRunnable).
Quand vous créer un objet instance d'une classe ClasseThread dérivée de Thread, le premier est
appelé implicitement par le constructeur de ClasseThread.
Le second est utilisé quand vous voulez créer un objet de classe Thread dont la méthode run
() à exécuter se trouve implémentée dans un classe implémentant l'interface Runnable. Au
passage, vous noterez que le paramètre du second constructeur doit être une référence
d'interface Runnable : En fait, vous passerez en argument une référence sur un objet d'une
classe ClasseRunnable implémentant Runnable ; ceci est un exemple de conversion d'une
référence d'une classe ClasseX dans une référence d'interface InterfaceY, si ClasseX
implémente l'interface InterfaceY, appliquée avec ClasseRunnable pour ClasseX et Runnable
pour InterfaceY.
start () : Cette méthode permet de démarrer effectivement le thread sur lequel elle est
invoquée, ce qui va provoquer l'appel de la méthode run () du thread. Cet appel est
obligatoire pour démarrer l'exécution d'un thread. En effet, la création d'un objet de classe
Thread ou d'une classe dérivée de Thread (par exemple, grâce à l'appel new Thread ()) ne fait
que créer un objet sans appeler la méthode run ().
stop () : Cette méthode permet d'arrêter un thread en cours d'exécution. Elle est utile
principalement pour stopper des threads dont la méthode run () ne se termine jamais (comme
par exemple, dans la classe TimeCounter, la méthode run () n'a pas de raison de s'arrêter
d'elle-même puisqu'aux dernières nouvelles, on n'a pas encore trouver le moyen d'arrêter le
temps !).
sleep () : Ces méthodes static permettent d'arrêter le thread courant pendant un certain laps
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 3

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) :

et le programme Java correspondant (à copier dans un fichier dénommé Chrono.java et invoqué à


partir d'un fichier HTML) :

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

public class Chrono extends Applet implements Runnable


{
private Thread chronometre;
private int dixiemeseconde = 0;

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


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

public void run ()


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

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


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

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


public void paint (Graphics gc)
{
// Dessine le temps écoulé sous forme de hh:mm:ss:d en noir et helvetica
gc.setColor (Color.black);
gc.setFont (new Font ("Helvetica", Font.BOLD, size ().height));
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 4

gc.drawString (dixiemeseconde / 36000


+ ":" + (dixiemeseconde / 6000) % 6 + (dixiemeseconde / 600) % 10
+ ":" + (dixiemeseconde / 100) % 6 + (dixiemeseconde / 10) % 10
+ ":" + dixiemeseconde % 10,
2, size ().height - 2);
}
}

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

Les états d'un thread


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

figure 10. Etats d'un thread

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

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

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


A l'appel de yield (), le thread passe dans l'état exécutable et rejoint la fin de la file
d'attente.
Sur les systèmes permettant de partager le temps d'exécution entre différents threads, le thread
passe dans l'état exécutable après qu'un certain laps de temps se soit écoulé.
En attentant que des opérations d'entrée/sortie (IO) se terminent, le thread passe dans l'état
bloqué .
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 5

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.

La synchronisation des threads


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

Utilisation de synchronized

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

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

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

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

Voici le programme Java (à copier dans un fichier dénommé AfficheurDeCalcul.java et invoqué à


partir d'un fichier HTML) :

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

public class AfficheurDeCalcul extends Applet


{
private Thread calculateur;
private Thread afficheur;

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


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

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


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

private int [ ] courbe;

// calculerCourbe () et paint () sont


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

synchronized public void dessinerCourbe ()


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

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


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

class Calculateur extends Thread


{
private AfficheurDeCalcul applet;

public Calculateur (AfficheurDeCalcul applet)


{
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 7

this.applet = applet;
}

public void run ()


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

class Afficheur extends Thread


{
private AfficheurDeCalcul applet;

public Afficheur (AfficheurDeCalcul applet)


{
this.applet = applet;
}

public void run ()


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

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


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

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

Il existe une autre syntaxe d'utilisation de synchronized :

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

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

Synchronisation avec wait () et notify ()

Comme il est expliqué dans l'exemple précédent, synchronized permet d'éviter que plusieurs threads
aient accès en même temps à même objet, mais ne garantit pas l'ordre dans lequel les threads ces
méthodes seront exécutées. Pour cela, il existe plusieurs méthodes de la classe Object qui
permettent de mettre en attente volontairement un thread sur un objet (avec les méthodes wait ()),
et de prévenir des threads en attente sur un objet que celui-ci est à jour (avec les méthodes notify
() ou notifyAll ()).
Ces méthodes ne peuvent être invoquées que sur un objet verrouillé par le thread courant ,
c'est-à-dire que le thread courant est en train d'exécuter une méthode ou un bloc synchronized, qui
a verrouillé cet objet. Si ce n'est pas le cas, une exception IllegalMonitorStateException est
déclenchée.

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

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

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

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

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

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

public class AfficheurDeCalcul extends Applet


{
// ...

synchronized public void calculerCourbe ()


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

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


vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 9

notifyAll ();
}

synchronized public void dessinerCourbe ()


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

// ...
}

A l'appel de wait () (ici sur l'objet this), le thread afficheur qui appelle la méthode
dessinerCourbe () est mis en attente jusqu'à ce que ce que calculerCourbe () appelle notifyAll
() pour prévenir les threads en attente qu'une nouvelle courbe est maintenant disponible. Ceci
évite que dessinerCourbe () soit éventuellement exécutée plusieurs fois de suite alors qu'aucune
nouvelle courbe n'a été calculée.
Il vaut mieux utiliser notifyAll () que notify (), car il est possible d'enrichir ce programme en
créant par exemple des threads qui devront appeler dessinerCourbe () pour mettre à jour des
fenêtres supplémentaires, ou en créant un autre thread appelant une méthode synchronized qui
enregistre la courbe dans un fichier. Si la méthode notify () était appelée, un seul thread serait
prévenu et mis à jour en ignorant les autres.

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


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

public class AfficheurDeCalcul extends Applet


{
// ...

synchronized public void calculerCourbe ()


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

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


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

notifyAll ();
}

synchronized public void dessinerCourbe ()


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

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


courbe = null;
notify ();
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 10

}
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

public final static int MIN_PRIORITY


public final static int NORM_PRIORITY
public final static int MAX_PRIORITY

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 ()

public Thread (Runnable target)

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

public Thread (ThreadGroup group, Runnable target)


throws SecurityException, IllegalThreadStateException

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

public Thread (String name)


public Thread (ThreadGroup group, String name)
throws SecurityException, IllegalThreadStateException
public Thread (Runnable target, String name)
public Thread (ThreadGroup group, Runnable target, String name)
throws SecurityException, IllegalThreadStateException
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 11

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

Méthodes

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

public static Thread currentThread ()

Renvoie une référence sur le thread actuellement en cours d'exécution.

public synchronized void start () throws IllegalThreadStateException

Provoque le démarrage du thread sur lequel start () est invoqué puis l'appel à la méthode run ().
Cette méthode rend la main immédiatement (le nouveau thread est lancé en parallèle au thread
courant).

public void run ()

run () est la méthode où sont décrites les instructions que doit exécuter un thread. Elle est appelée
une fois que le thread a démarré. run () doit être outrepassée dans une classe dérivée de Thread ou
une classe implémentant l'interface Runnable.

public final synchronized void stop () throws SecurityException

Arrête le thread sur lequel stop () est invoqué.

public final void stop (Throwable exception)


throws SecurityException

Arrête le thread sur lequel stop () est invoqué en déclenchant l'exception exception.

public final boolean isAlive ()

Renvoie true si un thread est vivant, c'est-à-dire que ce thread a démarré avec start () et n'a pas
été arrêté soit avec stop (), soit parce qu'il a terminé d'exécuter toutes les instructions de la
méthode run ().

public static void yield ()

Permet de suspendre le thread courant pour laisser la main à d'autres threads en attente d'exécution.

public static void sleep (long millis) throws InterruptedException


public static void sleep (long millis,
int nanos) throws InterruptedException

Provoque l'arrêt du thread courant pendant millis millisecondes, ou pendant millis millisecondes
et nanos nanosecondes. Ces méthodes sont susceptibles de déclencher une exception
InterruptedException, qui n'est pas utilisée dans Java 1.0, mais vous oblige quand même à inclure
l'appel à sleep () dans un try ... catch.

public final void suspend () throws SecurityException

Suspend l'exécution d'un thread vivant. Si plusieurs suspend () ont été invoquées sur un même
thread, un seul resume () est nécessaire pour que ce thread reprenne son activité.

public final void resume () throws SecurityException

Reprend l'exécution d'un thread, après une suspension avec suspend (). Si ce thread n'a pas été
suspendu, le thread poursuit son exécution.

public void checkAccess ()

Vérifie si le thread courant peut modifier le thread sur lequel checkAccess () est invoqué. Si cela lui
est interdit, une exception SecurityException est déclenchée.
vendredi 13 octobre 2000 Du C/C++ à Java : Les threads Page: 12

public final void setPriority (int newPriority)


throws SecurityException, IllegalArgumentException
public final int getPriority ()

Modifie ou renvoie la priorité d'un thread. newPriority doit avoir une valeur comprise entre
MIN_PRIORITY et MAX_PRIORITY.

public final void setName (String name) throws SecurityException


public final String getName ()

Modifie ou renvoie le nom d'un thread.

public final void setDaemon (boolean on)


throws SecurityException, IllegalThreadStateException
public final boolean isDaemon ()

Permet de spécifier ou de savoir si un thread est un thread qui tourne en tâche de fond, pour rendre
en général des services aux autres threads (daemon thread en anglais). Quand il n'a plus que des
threads qui tournent en tâche de fond dans le système, la Machine Virtuelle Java s'arrête.

public final synchronized void join (long millis) throws InterruptedException


public final synchronized void join (long millis, int nanos)
throws InterruptedException
public final void join () throws InterruptedException

Ces méthodes provoquent l'arrêt du thread courant jusqu'à la mort du thread sur lequel est invoqué
join (), et pendant un délai maximum de de millis millisecondes, ou millis millisecondes et
nanos nanosecondes.

public static void dumpStack ()

Imprime sur System.err l'état de la pile d'exécution du thread courant.

public final ThreadGroup getThreadGroup ()

Renvoie le groupe de threads auquel appartient un thread.

public static int activeCount ()


public static int enumerate (Thread tarray [ ])

Ces méthodes renvoient le nombre de threads actifs dans le groupe auquel appartient le thread
courant, et la liste des threads actifs de ce groupe dans le tableau tarray ( tarray doit exister et être
de taille supérieure ou égale à la valeur renvoyée par activeCount ()).

public void interrupt ()

Cette méthode et les deux suivantes ne sont implémentées qu'à partir de Java 1.1. Cette méthode
permet d'interrompre un thread. Si ce thread est en attente, il est exécuté et une exception
InterruptedException est déclenchée.

public static boolean interrupted ()


public boolean isInterrupted ()

public String toString ()

Renvoie une chaîne de caractère représentant un thread (comprenant son nom, sa priorité et le nom
du groupe de thread auquel il appartient). Cette méthode outrepasse celle de la classe Object.

public void destroy ()


public int countStackFrames () throws IllegalThreadStateException

Exemples

Application PaperBoardServer .
Applets Chrono , AfficheurDeCalcul , ObservateurCalcul , PaperBoardClient ,
AnimationFleche , ScrollText et Horloge .
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 1

Les classes internes

Les classes internes


Les classes anonymes
Autres nouveautés Java 1.1

Ce chapitre décrit les ajouts apportés dans le langage Java à partir de Java 1.1, et particulièrement
les classes internes (inner classes ).

Les classes internes


Syntaxe

Avec Java 1.0, il n'est possible de créer des classes public ou non qu'au niveau le plus haut dans un
fichier .java , c'est à dire des classes externes dépendant directement d'un package. Java 1.1
introduit la possibilité de créer des classes internes ou interfaces internes qui sont déclarées à
l'intérieur d'une classe ou d'une interface, en plus des méthodes et des champs :

class ClasseExterne
{
// Déclararation d'une classe interne
ModifieurClasseInterne class ClasseInterne
{
// Corps de ClasseInterne :
// Déclaration des champs, des méthodes, des constructeurs,...
}

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


// et implémentant une interface
ModifieurClasseInterne class ClasseInterne2 extends nomDeSuperClasse
implements nomInterface //, nomInterface2, ...
{
// ...
}

// Déclararation d'une interface interne


ModifieurInterfaceInterne interface InterfaceInterne
{
}

// Autres déclarations
}

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

public, protected ou private : Ces mots clés indiquent le contrôle d'accès de la classe interne
et ont le même sens que pour la déclaration des 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.

ModifieurInterfaceInterne peut prendre toutes les valeurs que ModifieurClasseInterne , sauf


final (abstract est implicite).
Une classe interne non static ne peut pas déclarer des champs et des méthodes static.

De même dans un bloc, il est possible de déclarer des classes internes locales dont la portée est
limitée au bloc. Dans ce cas, ModifieurClasseInterne ne peut être prendre comme valeur que final
ou abstract.

Une classe interne peut déclarer elle-même d'autres classes internes.

Pour chacune des classes internes déclarées est généré un fichier .class à la compilation. Pour
assurer l'unicité du nom de ces fichiers, la syntaxe suivante est utilisée : La classe interne
ClasseInterne déclarée à l'intérieur d'une classe externe ClasseExterne, sera stockée dans le fichier
ClasseExterne$ClasseInterne.class .
Pour les classes internes déclarées dans un bloc, le nom de fichier comporte en plus un identifiant
numérique généré par le compilateur donnant comme nom de fichier par exemple
ClasseExterne$1$ClasseInterne.class .

Java 1.1 permet de déclarer des classes internes (inner classes ). Les classes internes static
correspondent aux classes internes du C++ (nested classes ). Par contre les classes internes non
static sont un concept inexistant en C++ et permettent aux instances de ces classes de garder
implicitement un lien avec l'instance de la classe externe dont elles dépendent.

Le mécanisme utilisé par le compilateur Java 1.1 pour générer les classes internes est entièrement
compatible avec la Machine Virtuelle Java 1.0.
Donc même si vous faites fonctionner vos applets avec Java 1.0, vous pouvez leur permettre
quand même d'utiliser les classes internes en les compilant avec un compilateur Java 1.1 en
donnant comme classpath la librairie des classes de Java 1.0.

Utilisation

Bien qu'il ne soit pas obligatoire de s'en servir, les classes internes apportent un plus pour
l'organisation et la programmation des classes de votre programme :

L'existence de certaines classes n'a de sens que dans le contexte d'une autre classe. Dans ce cas, il peut
être intéressant de les déclarer comme classes internes pour montrer aux utilisateurs de ces classes dans
quel contexte elles s'utilisent.
Les classes externes peuvent avoir un contrôle d'accès public ou friendly . Toutes les classes non public
d'un package donné sont accessibles à toutes les autres classes de ce package, ce qui n'a pas toujours
l'effet désiré. Une classe interne dont le contrôle d'accès est private n'est accessible que par la classe
externe dans laquelle elle est déclarée.
Le nommage des classes est simplifié : certaines classes utilitaires de même genre peuvent avoir à être
déclarées dans des contextes différents. Si ces classes sont déclarées comme classes internes, elles
pourront porter le même nom sans interférer entre elles, et vous n'aurez pas à inventer des noms à
rallonge pour les différencier.
Comme une classe interne peut être déclarée n'importe où dans une classe, il est permis de la rapprocher
de l'endroit où elle est le plus utilisée, pour améliorer la lisibilité du programme.
La possibilité d'utiliser directement toutes les 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 :

Sans classe interne Avec classe interne


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

public class AfficheurDeCalcul extends Applet public class AfficheurDeCalcul extends Applet
{ {
private Thread calculateur; private Thread calculateur;
private Thread afficheur; private Thread afficheur;

public void start () public void start ()


{ {
setBackground (Color.white); setBackground (Color.white);
// Création de deux instances des classes // Création de deux instances des classes
// Calculateur et Afficheur // internes Calculateur et Afficheur
calculateur = new Calculateur (this); calculateur = new Calculateur ();
afficheur = new Afficheur (this); afficheur = new Afficheur ();

calculateur.start (); calculateur.start ();


afficheur.start (); afficheur.start ();
} }

// ... // ...
// Les méthodes stop (), calculerCourbe () // Les méthodes stop (), calculerCourbe ()
// dessinerCourbe () et paint () // dessinerCourbe () et paint ()
// sont inchangées // sont inchangées

} // Fin de AfficheurDeCalcul

// Classe friendly dérivant de Thread // Classe interne dérivant de Thread


class Calculateur extends Thread private class Calculateur extends Thread
{ {
private AfficheurDeCalcul applet; public void run ()
{
public Calculateur (AfficheurDeCalcul applet) while (isAlive ())
{ // calculerCourbe (), méthode de
this.applet = applet; // la classe externe AfficheurDeCalcul
} // peut être appelée directement
calculerCourbe ();
public void run () }
{ }
while (isAlive ())
applet.calculerCourbe ();
}
}

// Classe friendly dérivant de Thread // Classe interne dérivant de Thread


class Afficheur extends Thread private class Afficheur extends Thread
{ {
private AfficheurDeCalcul applet; public void run ()
{
public Afficheur (AfficheurDeCalcul applet) while (isAlive ())
{ // dessinerCourbe (), méthode de
this.applet = applet; // la classe externe AfficheurDeCalcul
} // peut être appelée directement
dessinerCourbe ();
public void run () }
{ }
while (isAlive ())
applet.dessinerCourbe ();
}
}
} // Fin de AfficheurDeCalcul

Les classes anonymes


Par extension des classes internes locales, vous pouvez déclarer aussi des classes "anonymes" en
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 4

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é.

SuperClasse objet = new SuperClasse (/* argument1, argument2, ...*/)


{
// Méthodes de SuperClasse outrepassées
// pour modifier le comportement de SuperClasse
};

Dans la même logique, il est possible de créer une instance d'une classe anonyme implémentant une
interface InterfaceX, grâce à l'instruction :

InterfaceX objet2 = new InterfaceX ()


{
// Implémentation de toutes les méthodes de InterfaceX
};

Dans ce cas, le bloc qui suit new InterfaceX () doit implémenter toutes les méthodes de InterfaceX
pour qu'il soit possible de créer une instance d'une telle classe.
Comme toute classe interne, une classe anonyme peut déclarer un ensemble de 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.*;

public class AfficheurDeCalcul extends Applet


{
private Thread calculateur;
private Thread afficheur;

public void start ()


{
setBackground (Color.white);
// Création de deux instances de classes
// anonymes implémentant la méthode run ()
// de la classe Thread
calculateur = new Thread ()
{
public void run ()
{
while (isAlive ())
// calculerCourbe (), méthode de
// la classe externe AfficheurDeCalcul
// peut être appelée directement
calculerCourbe ();
}
};
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 5

afficheur = new Thread ()


{
public void run ()
{
while (isAlive ())
// dessinerCourbe (), méthode de
// la classe externe AfficheurDeCalcul
// peut être appelée directement
dessinerCourbe ();
}
};

calculateur.start ();
afficheur.start ();
}

// ...
// Les méthodes stop (), calculerCourbe ()
// dessinerCourbe () et paint ()
// sont inchangées
}

Les classes anonymes permettent de transformer facilement un programme existant pour exécuter
un bloc d'instructions dans un thread isolé en ajoutant sur place les quelques instructions suivantes :

Avant Après
// ... // ...
new Thread ()
{
public void run ()
{ {
// Bloc d'instructions // Bloc d'instructions
} }
}.start ();

Pour éviter toute confusion avec le reste des instructions, utilisez des règles d'écriture et une
indentation claires pour l'écriture des classes anonymes.

Autres nouveautés Java 1.1


Initialisations d'instance

En plus des initialisations static, il est possible d'ajouter à une classe des blocs d'initialisations
d'instance. Ces blocs d'instructions sont exécutés à la création d'un objet juste après le constructeur
de sa super classe et avant tout constructeur de sa classe.
Ces initialisations sont surtout utiles pour les classes anonymes qui ne peuvent pas déclarer de
constructeurs.

Sauf pour les classes anonymes, les blocs d'initialisations d'instance d'une classe Classe1 ne peuvent
déclencher d'exceptions que si tous les constructeurs de Classe1 déclarent qu'ils sont susceptibles de
déclencher ces classe d'exceptions avec la clause throws.

Initialisation de tableaux

Les tableaux peuvent être initialisés à leur création en faisant suivre l'instruction de création du
tableau new Classe0 [], par la liste des éléments à stocker, comme dans l'exemple suivant :

class Classe1
{
// Les deux instructions suivantes sont équivalentes
int [ ] tab1 = {1, 2};
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes internes Page: 6

int [ ] tab2 = new int [] {1, 2};

void methode1 (String [] tab)


{
}

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.

Utilisation du mot-clé class

Toute classe ou interface peut être suivie du mot-clé class : ceci produit le même effet que
l'utilisation de la méthode forName () de la classe Class.
L'instruction String.class équivalente à Class.forName ("java.lang.String") est bien plus
pratique à utiliser car vous n'êtes pas obligé de donner le package complet de la classe String et
d'intercepter l'exception ClassNotFoundException que peut déclencher la méthode forName ().

Cette nouvelle syntaxe peut être aussi utilisée pour tous les types de base et void, de la manière
suivante :

byte.class
short.class
int.class
long.class
float.class
double.class
char.class
boolean.class

void.class

Ceci est utilisé en particulier par les classes du package java.lang.reflect pour manipuler tous les
types Java (que ce soient des classes ou des types de base) sous forme d'un objet de classe Class.

Variables locales et paramètres final

Les variables locales et les paramètres d'une méthode ou d'un constructeur peuvent être déclarés
final.

TypeRetour methode1 (final TypeParam1 param1Name /*,... */)


{
final TypeVariable variableLocale1 = valeur ;
// ...
}

Il n'est pas obligatoire d'initialiser une variable locale final dès sa déclaration, mais par contre il
n'est possible de lui assigner qu'une seule fois une valeur.
Tout paramètre ou toute variable locale que vous voulez utiliser dans une classe anonyme doivent
être déclarés final. Ceci permet à cette classe anonyme d'utiliser ces variables temporaires sans
risque qu'elles soient modifiées ultérieurement.
Par contre, 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 et portage

Conventions d'écriture
Portage de programmes écrits en C/C++

Ce chapitre vous indique les conventions généralement utilisées dans l'écriture des programmes et
des suggestions pour vous aider à récupérer des programmes écrits en C ou C++.
Ces suggestions et les exemples qui sont donnés peuvent vous servir aussi comme complément
d'information avant de passer à l'étude de la bibliothèque Java.

Conventions d'écriture
Par convention, l'écriture de vos classes devrait respecter les conventions suivantes :

Si un fichier .java définit une classe ou une interface public, cette classe et le nom du fichier
doivent avoir le même nom.
Le nom des classes commence par une majuscule, et représente une suite d'un ou plusieurs
mots en minuscules, avec chaque début de mot en majuscule (par exemple MaClasse).
Le nom des interfaces commence par une majuscule, et utilise généralement un adjectif
qualificatif (se terminant souvent par able).
Le nom des méthodes commence par une minuscule, et représente une action :
Les méthodes utilisées pour interroger ou modifier la valeur d'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.

Documentation des fichiers .java

Java définit des règles de syntaxe pour l'écriture des commentaires destinés à des fins de
documentation. Si vous les respectez, vous pourrez les extraire pour fabriquer un fichier HTML, en
utilisant la commande javadoc .
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 2

Portage de programmes écrits en C/C++


Vous avez développé un certains nombres de routines en C ou de classes en C++, et vous aimeriez
les porter en Java... Les remarques signalées par les symboles et des chapitres précédents et les
suggestions qui suivent devraient vous guider pour mener à bien cette tâche qui peut prendre un
certain temps suivant les fonctionnalités du C/C++ que vous avez utilisé.

Les suggestions décrites ici ne concernent que les routines C/C++ utilisant la bibliothèque
standard du C (stdio.h , stdlib.h ,...) et pas les applications utilisant les routines de l'Interface
Graphique de tel ou tel système (Windows, XWindow/Motif,...).

Elles ne représentent pas forcément la solution idéale à tel ou tel problème, mais plutôt une
solution pragmatique et rapidement réalisable.
Rien ne vous empêche de revoir complètement l'architecture de vos routines pour qu'elles
respectent les principes de la programmation orientée objets à la lettre.

Conception des classes

Tout d'abord, essayez d'éventuellement modifier et de rassembler vos routines pour qu'elles puissent
former des classes cohérentes.
Si la conception de vos routines n'utilise pas de principes de la programmation orientée objets
comme l'encapsulation, n'ayez pas de scrupules à 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 .

Remplacement des définitions de type typedef

typedef peut être utilisé en C de deux manières différentes :

Pour donner un nom aux types structurés struct, aux unions union et éventuellement aux types pointeurs
sur ces types. Tous les types structurés sont transformés en classes.
Pour renommer un type de base C : Dans ce cas, soit vous créez une classe ne contenant qu'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é.

Remplacement des instructions de précompilation #define

#define s'utilise de deux manières différentes en C, l'une pour déclarer des constantes, l'autre pour créer
des macros :
Les macros doivent être transformées en méthodes : ceci oblige à typer les paramètres d'une macro, mais
grâce à la surcharge des méthodes, vous pouvez créer plusieurs méthodes de même nom mais avec des
paramètres de type différent.
Les constantes doivent être remplacées par des 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.

Remplacement des instructions de précompilation #ifdef, #else, #endif

#ifdef s'utilise en C pour indiquer au compilateur qu'il doit compiler ou non une partie du code selon
qu'un symbole est défini ou non. Il est très souvent utilisé pour ajouter des instructions spéciales de suivi
lorsqu'un programme est en phase de mise au point (ou de debugging ).
L'utilisation de constantes Java 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;

void methode () void methode ()


{ {
//... //...
#ifdef DEBUG if (DEBUG)
printf ("OK"); System.out.println ("OK");
#endif }
} }

Un des avantages de l' instruction #ifdef est notamment qu'il permet d'alléger le code compilé (le fichier
.obj ) des instructions qui sont définis dans le bloc #ifdef si la condition est fausse. Le compilateur Java
fait de même : c'est-à-dire que dans l'exemple précédent si vous définissez DEBUG comme étant égal à
false, l'instruction System.out.println ("OK"); ne sera pas dans le fichier compilé Classe1.class.
Comme la condition if (DEBUG) est forcément fausse, le compilateur utilise dès le départ une
optimisation qui exclut l'instruction qui suit.

Remplacement des énumérations enum

Les énumérations enum définissent un ensemble cohérent de constantes dont la valeur est entière. Vous
pouvez les remplacer soit par des classes déclarant un ensemble de constantes public, soit par des
interfaces implémentées dans les classes qui en ont besoin.

Remplacement des unions union

En C, les variables d'un type union permettent de typer de manière différente leur zone mémoire. Dans
l'exemple :

union
{
double x;
int a;
}
varUnion;

varUnion a pour taille, la taille du plus grand de ses éléments, ici la taille d'un double (8 octets), et
mémorise soit un double par varUnion.x ou soit un int par varUnion.a.

Le portage le plus rapide consiste à remplacer les variables C de type union par une référence de classe
Object. Une 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 :

#include <stdio.h> // Fichier TestExpression.java


#include <stdlib.h> // A exécuter par la commande
// java TestExpression
struct _Expression;
/* Structure Operation mémorisant */ // Classe Operation mémorisant
/* une expression avec opérateur binaire */ // une expression avec opérateur binaire
typedef struct class Operation
{ {
char operateur; char operateur;
struct _Expression *expr1, Object expr1,
*expr2; expr2;
} }
Operation;
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 4

#define TYPE_VALEUR 0
#define TYPE_OPERATION 1

/* Structure _Expression mémorisant */ // Pas besoin d'une classe Expression :


/* une union entre un double et une */ // une expression est mémorisée par une
/* valeur de type Operation */ // référence de classe Object
/* et un champ type permettant de */ // contenant soit un objet de classe
/* connaître le type dans l'union */ // Double, soit une objet de classe
typedef struct _Expression // Operation
{
int type;
union
{
double valeur;
Operation operation;
}
expr;
} // Classe TestExpression
*Expression; public class TestExpression
{
double calculerOperation (Operation oper);
double calculerExpression static double calculerExpression
(Expression expr) (Object expr)
{ {
/* Calcul du résultat suivant le type */ // Calcul du résultat suivant la classe
/* d'expression mémorisée dans l'union */ // de l'objet expr
switch (expr->type)
{
case TYPE_VALEUR : if (expr instanceof Double)
return expr->expr.valeur; return ((Double)expr).doubleValue ();
case TYPE_OPERATION : if (expr instanceof Operation)
return calculerOperation return calculerOperation
(expr->expr.operation); ((Operation)expr);
}
return 0; throw new IllegalArgumentException ();
} }

/* Calcul d'une opération binaire */ // Calcul d'une opération binaire


double calculerOperation (Operation oper) static double calculerOperation
(Operation oper)
{ {
/* Evaluation des deux opérandes */ // Evaluation des deux opérandes
double val1 = calculerExpression double val1 = calculerExpression
(oper.expr1); (oper.expr1);
double val2 = calculerExpression double val2 = calculerExpression
(oper.expr2); (oper.expr2);
switch (oper.operateur) switch (oper.operateur)
{ {
case '+' : return val1 + val2; case '+' : return val1 + val2;
case '-' : return val1 - val2; case '-' : return val1 - val2;
case '*' : return val1 * val2; case '*' : return val1 * val2;
case '/' : return val1 / val2; case '/' : return val1 / val2;
} }
return 0; throw new IllegalArgumentException ();
} }

void main () public static void main (String [] arg)


{ {
/* Allocation de 5 expressions */
Expression expr = malloc
(5 * sizeof (struct _Expression));

/* Construction de l'arbre représentant */ // Construction de l'arbre représentant


/* l'expression 2 + 3 / 4 */ // l'expression 2 + 3 / 4
expr [0].type = Operation expr1 = new Operation ();
expr [1].type = TYPE_OPERATION; expr1.operateur = '/';
expr [0].expr.operation.operateur = '+'; expr1.expr1 = new Double (3);
expr [0].expr.operation.expr1 = &expr [2]; expr1.expr2 = new Double (4);
expr [0].expr.operation.expr2 = &expr [1];
expr [1].expr.operation.operateur = '/'; Operation expr0 = new Operation ();
expr [1].expr.operation.expr1 = &expr [3]; expr0.operateur = '+';
expr [1].expr.operation.expr2 = &expr [4]; expr0.expr1 = new Double (2);
expr0.expr2 = expr1;
expr [2].type =
expr [3].type =
expr [4].type = TYPE_VALEUR;
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 5

expr [2].expr.valeur = 2;
expr [3].expr.valeur = 3;
expr [4].expr.valeur = 4;

printf ("Resultat de 2 + 3/4 = %g", System.out.println


calculerExpression (&expr [0])); ( "Resultat de 2 + 3/4 = "
+ calculerExpression (expr0));
} }
}

Enfin, voici une version plus orientée objet du programme TestExpression.java :

// Déclaration de l'interface Calculable


// (Aurait pu être aussi une classe abstract)
interface Calculable
{
double calculer ();
}

// Classe Nombre mémorisant un double


// Le résultat de calculer () est ce nombre
class Nombre implements Calculable
{
private double nombre;

// Constructeur
Nombre (double nombre)
{
this.nombre = nombre;
}

// Implémentation de la méthode calculer ()


public double calculer ()
{
return nombre;
}
}

// Classe Operation mémorisant une expression avec opérateur binaire


// Le résultat de calculer () est l'évaluation de cette expression
class Operation implements Calculable
{
private char operateur;
private Calculable expr1,
expr2;

// Constructeur
Operation (char operateur, Calculable expr1, Calculable expr2)
{
this.operateur = operateur;
this.expr1 = expr1;
this.expr2 = expr2;
}

// Implémentation de la méthode calculer ()


public double calculer ()
{
// Evaluation des deux opérandes
double val1 = expr1.calculer ();
double val2 = expr2.calculer ();
switch (operateur)
{
case '+' : return val1 + val2;
case '-' : return val1 - val2;
case '*' : return val1 * val2;
case '/' : return val1 / val2;
}
throw new IllegalArgumentException ("Operateur inconnu");
}
}

// Classe de test TestExpression


public class TestExpression
{
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 6

public static void main (String [] arg)


{
// Création de l'expression 2 + 3 / 4
Calculable expr =
new Operation ('+', new Nombre (2),
new Operation ('/', new Nombre (3),
new Nombre (4)));
System.out.println
("Resultat de 2 + 3/4 = " + expr.calculer ());
}
}

Passage des valeurs par adresse

Comme il est signalé au chapitre sur la création des classes, tous les paramètres des fonctions sont passés
par valeur en Java. Si certaines de vos fonctions requièrent de leur passer des paramètres d'un type de
base par adresse, comme dans l'exemple suivant :

void fonction1 (int *param1);


int fonction2 (double *param1);

vous devrez transformer vos fonctions d'une des trois manières suivantes :
Pour une fonction void du style de fonction1 (), vous pouvez la transformer en int fonction1
(int param1), et renvoyer dans fonction1 () la valeur modifiée de param1. L'inconvénient de cette
méthode est qu'elle vous oblige à transformer tous les appels à fonction1 () pour en récupérer la
valeur de retour.
Pour une fonction du style fonction2 (), vous pouvez créer une classe DoubleValue mémorisant 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;
}

int fonction2 (double *param1) int fonction2 (DoubleValue param1)


{ {
*param1 = 1; param1.value = 1;
/* ... */ // ...
return 0; return 0;
} }

void fonction3 (float *param1) int fonction3 (float [] param1)


{ {
*param1 = 1; param1 [0] = 1;
/* ... */ // ...
return 0; return 0;
} }

void fonctionAppelante () void fonctionAppelante ()


{ {
int argInt = 0; int argInt = 0;
double argDouble = 0; DoubleValue argDouble =
float argFloat = 0; new DoubleValue (0);
int valRetour;
int valRetour; float [] argFloat = {0};

fonction1 (&argInt); argInt = fonction1 (argInt);


valRetour = fonction2 (&argDouble); valRetour = fonction2 (argDouble);
fonction3 (&argFloat); fonction3 (argFloat);

/* ... */ // ...
} }
}

// Classe d'emballage du type double


// avec champ d'accès public
class DoubleValue
{
public double value;

public DoubleValue (double value)


{
this.value = value;
}
}

Les classes d'emballage des types de base Boolean, Character, Integer, Long, Float, Double ne
permettent pas de modifier la valeur de type de base qu'elle mémorise, donc elles ne peuvent pas
être utilisées dans ce cas.
Si vous utilisez très souvent le passage de valeurs par adresse, il est conseillé de créer des le
départ vos propres classes d'emballage mémorisant 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 !).

Utilisation des chaînes de caractères

En Java, les chaînes de caractères sont représentées par les classes String et StringBuffer. Il faut
remplacer tous les tests de caractère nul ('\0') permettant de savoir si on a atteint la fin d'une chaîne de
caractères en C, par des appels aux méthodes length () de ces classes.

Arithmétique des pointeurs

L'arithmétique des pointeurs est une notion absente en Java. Vous devrez remplacer son usage par
l'utilisation d'un indice courant associé à un tableau, ou pour les chaînes de caractères utiliser
éventuellement les méthodes de la classe String comme substring (), pour créer des chaînes de
caractères à la volée sans se soucier de la libération de ces chaînes puisque le Garbage Collector est là
pour ça.

Voici un programme C et un programme Java mis en parallèle pour illustrer une manière de transformer
une fonction chercherSousChaine (str1, str2) , qui recherche dans la chaîne de caractères str1 une
chaîne str2 (équivalent de la fonction C strstr (char *str1, char *str2) et de la méthode Java indexOf
(String str) de la classe String) :

#include <string.h> class ClasseChaine


{
char *chercherSousChaine (char *str1, public int chercherSousChaine (String str1,
char *str2) String str2)
{ {
while (strlen (str1) >= strlen (str2)) int i = 0;
if (!strncmp (str1, str2, strlen (str2))) while (str1.length () >= str2.length ())
return str1; if (str1.startsWith (str2))
else return i;
str1++; else
return NULL; // Pas trouvé {
} str1 = str1.substring (1);
i++;
}
return -1; // Pas trouvé
}
}

Transformation des pointeurs sur fonctions

En C, les pointeurs sur fonctions sont très pratiques et beaucoup de bibliothèques C en font l'usage. Son
utilisation se regroupe principalement en deux catégories :
Comme paramètre d'une fonction fct (), elle est utilisée souvent comme fonction à rappeler (call-back
en anglais), pour modifier le comportement de fct (). Par exemple, la fonction de comparaison passée
en paramètre à la fonction qsort () permet de trier un tableau de n'importe quel type d'élément, ou la
procédure passée en paramètre à la fonction CreateDialog () de Windows permet de modifier le
comportement d'une boite de dialogue.
Ce type de pointeurs de fonctions peut être transformé par l'utilisation de méthodes outrepassées et a été
abordée au chapitre sur la création des classes.
Comme élément d'un tableau regroupant un ensemble de pointeurs sur fonctions ayant toutes le même
prototype. Ce procédé est souvent utilisé par les automates, pour qu'ils puissent exécuter une action pour
un état donné.
Dans ce cas, soit vous utilisez le procédé précédent en mémorisant des objets à place des fonctions dans
le tableau, comme dans l'exemple suivant :
vendredi 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);
}

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


/* du même type que Action () */ // la méthode action () de cette interface
class Action1 implements Action
{
void action1 (int param) public void action (int param)
{ /* Corps de action1 () */ } { /* Corps de action () */ }
}

class Action2 implements Action


{
void action2 (int param) public void action (int param)
{ /* Corps de action2 () */ } { /* Corps de action () */ }
}

// Déclaration de la classe Automate


class Automate
{
// Déclaration d'un tableau mémorisant
/* Déclaration d'un tableau mémorisant */ // différents objets dont la classe
/* différents pointeurs de type Action */ // implémente l'interface Action
Action tabAction [] = {action1, NULL, Action tabAction [] =
action2, action1}; {new Action1 (), null,
new Action2 (), new Action1 ()};

void ExecuterAction (int etat, int param) void ExecuterAction (int etat, int param)
{ {
/* Appel de fonction suivant etat */ // Appel de action () en fonction de etat
if (tabAction [etat] != NULL) if (tabAction [etat] != null)
tabAction [etat] (param); tabAction [etat].action (param);
} }

/* ... */ // ...
}

soit vous associez une constante à chacune des fonctions, vous remplacez les pointeurs du tableau par ces
constantes et choisissez la bonne fonction à appeler grâce à l'instruction switch (), comme dans
l'exemple suivant :
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 10

/* Déclaration d'un type pointeur sur */ // Déclaration de la classe Automate


/* fonction prenant en paramètre un int */
typedef void (* Action) (int param); class Automate
{
/* Déclaration de deux fonctions */ // Déclaration de deux fonctions
/* du même type que Action () */ // d'action
void action1 (int param) void action1 (int param)
{ /* Corps de action1 () */ } { /* Corps de action1 () */ }

void action2 (int param) void action2 (int param)


{ /* Corps de action2 () */ } { /* Corps de action2 () */ }

// Déclaration des deux constantes


// représentant les deux méthodes
final static int ACTION1 = 1;
final static int ACTION2 = 2;

/* Déclaration d'un tableau mémorisant */ // Déclaration d'un tableau mémorisant


/* différents pointeurs de type Action */ // différentes constantes d'action
Action tabAction [] = {action1, NULL, int tabAction [] = {ACTION1, 0,
action2, action1}; ACTION2, ACTION1};

void ExecuterAction (int etat, int param) void ExecuterAction (int etat, int param)
{ {
/* Appel de fonction suivant etat */ // Appel de la bonne méthode
if (tabAction [etat] != NULL) // en fonction de etat
tabAction [etat] (param); switch (etat)
} {
case ACTION1 : action1 (param);
/* ... */ break;
case ACTION2 : action2 (param);
break;
}
}
}

Remplacement de l'héritage multiple

Java ne permet pas l'héritage multiple du C++. Si vous voulez néammoins conserver une architecture de
classes qui utilise l'héritage multiple, vous pouvez utiliser les interfaces, en utilisant par exemple la
solution suivante :
vendredi 13 octobre 2000 Du C/C++ à Java : Conventions d'écriture et portage Page: 11

class Type1 class Type1


{ {
public : public void methode1 () { /* ... */ }
void methode1 () { /* ... */ } }
};
// Utilisation d'une interface à la place
// de la seconde classe
class Type2 interface Type2
{ {
public : void methode2 ();
void methode2 () { /* ... */ } }
};
class Classe2 implements Type2
{
public void methode2 () { /* ... */ }
}

class ClasseDerivee : public Type1, public class ClasseDerivee extends Type1


public Type2 implements Type2
{ {
// La classe ClasseDerivee hérite des // Création d'une instance de Classe2
// méthodes methode1 () et methode2 () // pour remplacer le second lien d'héritage
}; Type2 objetClasse2 = new Classe2 ();

// Implémentation de methode2 () pour


// qu'elle appelle la méthode de Classe2
public void methode2 ()
{
objetClasse2.methode2 ();
}

void main () public static void main (String [] args)


{ {
Type1 *objet1 = new Type1 (); Type1 objet1 = new Type1 ();
Type2 *objet2 = new Type2 (); Type2 objet2 = new Classe2 ();

ClasseDerivee *objet3 = new ClasseDerivee (); ClasseDerivee objet3 = new ClasseDerivee ();
objet3->methode1 (); objet3.methode1 ();
objet3->methode2 (); objet3.methode2 ();

// Cast implicite vers la super classe


// Cast implicite vers les super classes // ou l'interface Type2
objet1 = objet3; objet1 = objet3;
objet2 = objet3; objet2 = objet3;
} }
}

Cette solution peut être longue à programmer si les super classes définissent un nombre important de
méthodes. Par contre, cette solution permet de modifier aisément le programme qui utilise les classes
transformées en interfaces : vous n'avez qu'à modifier le nom de la classe utilisée à l'instantiation des
objets (ici transformer new Type2 () en new Classe2 ()).

Autres problèmes propres au C++

Paramètres passés par référence en C++ : si le paramètre est d'un type de base, vous devrez le traiter
comme s'il était passé par adresse. Si le paramètre est une instance d'une classe ou une structure,
supprimer simplement le symbole & de la déclaration.
Les appels à l'opérateur delete du C++ pour des destructeurs n'effectuant que des libérations de mémoire
deviennent normalement inutiles en Java. Pour les autres vous pouvez créer une méthode delete () dans
vos classes, et remplacer les appels delete objet; par objet.delete ();.
vendredi 13 octobre 2000 Du C/C++ à Java : La bibliothèque Java Page: 1

La bibliothèque Java 1.0

Les packages de la bibliothèque Java


Hiérarchie des classes Java 1.0

Maintenant que vous connaissez le langage Java, il vous reste à apprendre les classes Java fournies
en standard avec la Machine Virtuelle Java.

Il est fourni avec le JDK le code source des classes des packages Java standards. Alors, n'hésitez
pas à le consulter si vous recherchez plus d'informations sur le fonctionnement d'une classe ou d'une
méthode.

Dans certaines classes, vous pouvez être étonné que certaines méthodes synchronized semblent
similaires à d'autres qui ne le sont pas. Ceci tient généralement du fait que certaines méthodes
s'appellent entre elles, et seule la méthode finalement appelée est synchronized.

Les packages de la bibliothèque Java 1.0


Les packages ci-après sont cités dans l'ordre des chapitres du manuel.

Classes de base : le package java.lang


Ce package est développé dans le chapitre suivant et rassemble les classes de base de Java. Toutes
les classes et interfaces de java.lang sont automatiquement importées par le compilateur.

Gestion de données et utilitaires : le package java.util


Ce package est développé dans le chapitre sur les outils Java et rassemble des classes d'utilitaires
(gestion des collections de données, génération de nombres aléatoires, énumération, date,...). Il
définit aussi les classes d'exceptions EmptyStackException et NoSuchElementException.

Les entrées-sorties : le package java.io


Ce package est développé dans le chapitre sur la gestion des fichiers et des flux de données et
rassemble les classes permettant de gérer les entrées-sorties (accès fichiers, gestion de
répertoires,...). Il définit aussi les classes d'exceptions IOException, EOFException,
FileNotFoundException, InterruptedIOException et UTFDataFormatException.

Les accès réseau : le package java.net


Ce package est développé dans le chapitre sur les accès au réseau et rassemble les classes
permettant de gérer les accès réseau. Il définit aussi les classes d'exceptions MalformedURLException,
ProtocolException, SocketException, UnknownHostException et UnknownServiceException.

Gestion des applets : le package java.applet


Ce package est développé dans le chapitre sur les applets. La classe Applet et les interfaces de ce
vendredi 13 octobre 2000 Du C/C++ à Java : La bibliothèque Java Page: 2

package permettent de programmer une applet Java et d'intégrer une applet dans un navigateur.

Interface utilisateur : le package java.awt


Ce package est développé dans le chapitre sur les composants AWT et les suivants. Les classes de
ce package permettent de programmer l'interface graphique d'un programme ou d'une applet Java. Il
définit aussi les classes d'exceptions AWTException et AWTError.

Manipulation d'images : le package java.awt.image


Ce package est développé dans le chapitre sur les images. Les classes de ce package permettent de
manipuler les images (gestion du chargement des images, filtres, gestion des couleurs,...).

Liaison avec l'interface utilisateur du système : le package java.awt.peer


Ce package est abordé dans le chapitre sur les composants. Il définit un ensemble d'interfaces
permettant aux composants graphiques Java (boutons, fenêtres,...) d'être représentés à l'écran par les
composants du système sur lequel fonctionne une Machine Virtuelle Java.

Hiérarchie des classes Java 1.0


Voici la hiérarchie des classes Java dans sa version 1.0. Les liens vous permettent d'atteindre la
page où elles sont décrites.

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

(Les classes du package java.awt.peer ne sont pas citées)


vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 1

Les classes de bases

Les classes de gestion des objets


Les classes d'emballage
Calcul mathématique : la classe java.lang.Math
Les classes de gestion des threads
Les classes de gestion du système

Ce chapitre décrit le package java.lang de Java 1.0 qui rassemble les classes de base de Java.
Les principales classes de ce package ayant déjà été étudiées (Class, Object, String, StringBuffer,
System, Thread, Throwable et ses dérivées), voici la description des autres classes et interfaces
fournies par ce package.

Rappel : Toutes les classes et interfaces de java.lang sont automatiquement importées par le
compilateur.

Gestion des objets


La classe java.lang.Object

Cette classe est à la racine de la hiérarchie des classes Java, donc toute classe hérite implicitement
de la classe Object. Elle est détaillée au chapitre sur les objets.

L'interface java.lang.Cloneable

Cette interface ne déclare ni constantes, ni méthodes. Elle doit être implémentée par toute classe qui
veut outrepasser et utiliser la méthode clone () de la classe Object, qui permet de créer une copie
d'un objet.

La classe java.lang.Class

Cette classe final permet de représenter chacune des classes chargées par la Machine Virtuelle
Java. Elle est détaillée au chapitre sur les objets.

La classe java.lang.ClassLoader

La création d'une classe dérivée de cette classe abstract permet de créer un chargeur de classe
spécifique.
Par défaut, chaque classe est chargée dynamiquement à sa première utilisation à partir du fichier
.class de même nom que la classe à 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

protected ClassLoader () throws SecurityException

Méthodes

protected abstract Class loadClass (String name, boolean resolve)


throws ClassNotFoundException

Cette méthode doit être outrepassée pour charger et renvoyer la classe de nom name. Si resolve est
égal à true, les classes qu'utilise cette classe doivent être résolues. Si la classe n'est pas trouvée une
exception de classe ClassNotFoundException doit être déclenchée.
Les trois autres méthodes sont final et protected et sont des outils utilisés pour créer ou préparer
les classes.

protected final Class defineClass (byte data [ ], int offset, int length)
throws IndexOutOfBoundsException, ClassFormatError

Permet de créer une instance de la classe Class à partir des length octets du tableau data pris à
partir de l'indice offset. Si cet ensemble d'octets ne représente pas une classe Java valide, une
exception ClassFormatError est déclenchée.

protected final void resolveClass (Class c)

Permet de résoudre et charger les classes qu'utilise la classe c. Cette opération doit être effectuée
avant d'utiliser la classe c et entraîne que la méthode loadClass () sera appelée pour chacune des
classes que référencent les champs de la classe c.

protected final Class findSystemClass (String name) throws ClassNotFoundException

Charge la classe de nom name avec le chargeur de classe par défaut.

Le constructeur de la classe ClassLoader peut éventuellement déclencher une exception de classe


SecurityException, si le gestionnaire de sécurité interdit de créer d'autres chargeurs de classes,
comme le font par exemple les navigateurs. Donc, si vous créez des applets pour Internet vous ne
pourrez pas changer le chargeur de classe par défaut.

La classe java.lang.Compiler

Cette classe final fournit un ensemble de méthodes relatives à la compilation des classes Java en
code machine du système sur lequel fonctionne la Machine Virtuelle Java.

Méthodes

public static Object command (Object any)


public static boolean compileClass (Class class)
public static boolean compileClasses (String string)
public static void disable ()
public static void enable ()

La classe java.lang.Throwable

Cette classe est à la racine de toutes les classes d'exceptions. Throwable et toutes ses classes dérivées
sont détaillées au chapitre traitant des exceptions.

Les classes d'emballage


La bibliothèque Java fournit un ensemble de classes qui permettent de manipuler les types de base
sous forme d'objets, comme par exemple la classe Boolean pour le type boolean. Chacune de ces
classes ne mémorisant qu'un champ du type de base donné associé, on appelle ce type de classe une
classe d'emballage (wrapper en anglais).
Ces classes peuvent être utile pour les classes ne manipulant que des objets, comme par exemple la
classe java.util.Vector, qui permet de créer un ensemble d'objets quelconques.
Une classe d'emballage existant pour tous les types de base (sauf pour les types byte, short et void
sous Java 1.0), il est donc possible de programmer du "pur objet" en Java, où toutes les variables ne
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 3

seraient que des objets et uniquement cela.

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

public final static Boolean TRUE


public final static Boolean FALSE

TRUE et FALSE sont deux constantes qui correspondent aux valeurs true et false du type boolean.

Constructeurs

public Boolean (boolean value)


public Boolean (String s)

Ces constructeurs permettent de créer une instance de la classe Boolean à partir de la valeur
booléene value ou de la chaîne de caractères s.

Méthodes

public boolean booleanValue ()

Renvoie la valeur booléenne mémorisée.

public static Boolean valueOf (String s)

Renvoie une instance de la classe Boolean à partir de la chaîne de caractères s. s est considérée
égale à true si et seulement si elle est égale à "true" en minuscules ou en majuscules.

public static boolean getBoolean (String s)

Renvoie true ou false si la propriété name du système est égale à "true". Si cette propriété n'existe
pas, cette méthode renvoie false.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet booléen à un objet ou renvoyer une chaîne de caractères décrivant la valeur booléenne.

La classe java.lang.Character

Cette classe final est la classe d'emballage qui correspond au type char. Elle déclare un grand
nombre de méthodes static permettant de reconnaître le type de lettres (majuscule, minuscule,
chiffre,...) et d'effectuer des conversions.

Champs

public final static int MIN_RADIX


public final static int MAX_RADIX

Ces constantes sont les bases de conversion minimum (= 2) et maximum (= 36) des nombres
entiers.

public final static char MIN_VALUE


public final static char MAX_VALUE

Valeurs minimum ('\u0000') et maximum ('\ffff') d'un caractère Unicode.

Constructeur
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 4

public Character (char value)

Construit une instance de la classe Character à partir du caractère value.

Méthodes

public char charValue ()

Renvoie le caractère mémorisé.

public static boolean isLowerCase (char ch)


public static boolean isUpperCase (char ch)
public static boolean isTitleCase (char ch)

Ces méthodes renvoient true si le caractère ch est une minuscule, une majuscule ou une lettre de
titre.

public static boolean isDefined (char ch)

Ces méthodes renvoient true si le caractère ch est défini dans l'ensemble des caractères Unicode.

public static boolean isDigit (char ch)

Renvoie true si le caractère ch est un chiffre.

public static boolean isLetter (char ch)

Renvoie true si le caractère ch est une lettre.

public static boolean isLetterOrDigit (char ch)

Renvoie true si le caractère ch est un chiffre ou une lettre.

public static boolean isJavaLetter (char ch)

Renvoie true si le caractère ch est une lettre Java, c'est-à-dire qu'elle est permise comme première
lettre d'un identifiant Java.

public static boolean isJavaLetterOrDigit (char ch)

Renvoie true si le caractère ch est une lettre ou un chiffre Java, c'est-à-dire qu'elle est permise
comme lettre d'un identifiant Java différente de la première.

public static boolean isSpace (char ch)

Renvoie true si le caractère ch est un espace c'est-à-dire un des caractères ' ', '\t', '\f', '\n' ou
'\r'.

public static char toLowerCase (char ch)


public static char toUpperCase (char ch)
public static char toTitleCase (char ch)

Ces méthodes renvoient le caractère ch converti en minuscule, en majuscule ou en lettre de titre.

public static int digit (char ch, int radix)

Renvoie le nombre correspondant au caractère ch en base radix ou -1 si le caractère ch est incorrect


en base radix.

public static char forDigit (int digit, int radix)

Renvoie le caractère correspondant au nombre digit en base radix, ou le caractère '\0' si le


nombre est négatif ou plus grand que la base radix.

public int hashCode ()


public boolean equals (Object obj)
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 5

public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet caractère à un objet ou renvoyer une chaîne de caractères avec le caractère mémorisé.

Exemples

Applet Unicode .
Classe ToUpperCaseInputStream.

La classe java.lang.Number

Cette classe abstract est la super classe des classes d'emballage Integer, Long, Float et Double qui
permettent de traiter des nombres de type int, long, float et double sous forme d'objets.

Constructeur

public Number ()

Méthodes

public abstract int intValue ()


public abstract long longValue ()
public abstract float floatValue ()
public abstract double doubleValue ()

Ces méthodes doivent renvoyer le nombre mémorisé dans l'un des types de base int, long, float ou
double.

La classe java.lang.Integer

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type
int.

Champs

public final static int MIN_VALUE


public final static int MAX_VALUE

Valeurs minimum (-2147483648) et maximum (21474836487) d'un entier de type int.

Constructeurs

public Integer (int value)


public Integer (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Integer à partir de la valeur entière
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public static String toString (int i, int radix)

Renvoie la chaîne de caractères correspondant au nombre i en base radix.

public static String toHexString (int i)


public static String toOctalString (int i)
public static String toBinaryString (int i)
public static String toString (int i)
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 6

Renvoie la chaîne de caractères correspondant au nombre i en base 16 (hexadécimal), en base 8


(octal), en base 2 (binaire) ou en base 10 (décimal).

public static int parseInt (String s, int radix) throws NumberFormatException


public static int parseInt (String s) throws NumberFormatException

Ces méthodes renvoient le nombre contenu dans la chaîne de caractères s en base radix (radix = 10
par défaut). Si s contient des caractères invalides ou si le nombre contenu dans s est plus grand que
MAX_VALUE ou plus petit que MIN_VALUE, une exception NumberFormatException est déclenchée.

public static Integer valueOf (String s, int radix) throws NumberFormatException


public static Integer valueOf (String s) throws NumberFormatException

Ces méthodes renvoient une instance de la classe Integer mémorisant le nombre contenu dans la
chaîne de caractères s en base radix (radix = 10 par défaut).

public static Integer getInteger (String name)


public static Integer getInteger (String name, int val)
public static Integer getInteger (String name, Integer val)

Ces méthodes renvoient la propriété name du système. Si cette propriété n'existe pas ou n'est pas un
entier, la valeur val (ou par défaut null) est renvoyée.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet entier à un objet ou renvoyer une chaîne de caractères décrivant la valeur entière.

Exemples

Applets PaperBoardClient , Unicode et ListePolices .


Application LectureFichier .

La classe java.lang.Long

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type
long.

Champs

public final static int MIN_VALUE


public final static int MAX_VALUE

Valeurs minimum (-9223372036854775808) et maximum (9223372036854775807) d'un entier de type


long.

Constructeurs

public Long (long value)


public Long (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Long à partir de la valeur entière
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public static String toString (long i, int radix)


vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 7

Renvoie la chaîne de caractères correspondant au nombre i en base radix.

public static String toHexString (long i)


public static String toOctalString (long i)
public static String toBinaryString (long i)
public static String toString (long i)

Renvoie la chaîne de caractères correspondant au nombre i en base 16 (hexadécimal), en base 8


(octal), en base 2 (binaire) ou en base 10 (décimal).

public static long parseLong (String s, int radix) throws NumberFormatException


public static long parseLong (String s) throws NumberFormatException

Ces méthodes renvoient le nombre contenu dans la chaîne de caractères s en base radix (radix = 10
par défaut). Si s contient des caractères invalides une exception NumberFormatException est
déclenchée.

public static Long valueOf (String s, int radix) throws NumberFormatException


public static Long valueOf (String s) throws NumberFormatException

Ces méthodes renvoient une instance de la classe Long mémorisant le nombre contenu dans la
chaîne de caractères s en base radix (radix = 10 par défaut).

public static Long getLong (String name)


public static Long getLong (String name, long val)
public static Long getLong (String name, Long val)

Ces méthodes renvoient la propriété name du système. Si cette propriété n'existe pas ou n'est pas un
entier, la valeur val (ou par défaut null) est renvoyée.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet entier à un objet ou renvoyer une chaîne de caractères décrivant la valeur entière.

La classe java.lang.Float

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type
float.

Champs

public final static float POSITIVE_INFINITY


public final static float NEGATIVE_INFINITY
public final static float NaN

Ces constantes permettent d'obtenir les valeurs correspondantes à l'infini positif, négatif ou
représentant une valeur non significative.

public final static double MAX_VALUE


public final static double MIN_VALUE

Valeurs minimum (1.40129846432481707e-45f) et maximum (3.40282346638528860e+38f) d'un


nombre flottant de type float.

Constructeurs

public Float (float value)


public Float (double value)
public Float (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Float à partir de la valeur flottante
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 8

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public boolean isInfinite ()


public boolean isNaN ()

Ces méthodes renvoie true si le nombre mémorisé est infini ou si c'est une valeur non significative.

public static boolean isInfinite (float v)


public static boolean isNaN (float v)

Ces méthodes renvoie true si v est une valeur infinie ou si c'est une valeur non significative.

public static String toString (float f)

Renvoie la chaîne de caractères correspondant au nombre f.

public static Float valueOf (String s) throws NumberFormatException

Renvoie une instance de la classe Float mémorisant le nombre contenu dans la chaîne de caractères
s. Si s contient des caractères invalides une exception NumberFormatException est déclenchée.

public static int floatToIntBits (float value)


public static float intBitsToFloat (int bits)

Ces méthodes convertissent la valeur value en son équivalent en int, ou inversement la valeur bits
de type int en float. Elles sont utilisées par les méthodes writeFloat () et readFloat () de
certaines des classes d'entrée-sortie qui manipulent un nombre de type float sous forme de valeur
32 bits.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe Float à un objet ou renvoyer une chaîne de caractères décrivant la valeur flottante.

Exemple

Applet CalculetteSimple .

La classe java.lang.Double

Cette classe final qui dérive de la classe Number est la classe d'emballage qui correspond au type
double.

Champs

public final static double POSITIVE_INFINITY


public final static double NEGATIVE_INFINITY
public final static double NaN

Ces constantes permettent d'obtenir les valeurs correspondantes à l'infini positif, négatif ou
représentant une valeur non significative (par exemple, Math.sqrt (-1) renvoie Double.NaN).

public final static double MAX_VALUE


public final static double MIN_VALUE

Valeurs minimum (2.2250738585072014e-308) et maximum (1.79769313486231570e+308) d'un


nombre flottant de type double.

Constructeurs
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 9

public Double (double value)


public Double (String s) throws NumberFormatException

Ces constructeurs permettent de créer une instance de la classe Double à partir de la valeur flottante
value ou du nombre contenu dans la chaîne de caractères s.

Méthodes

public int intValue ()


public long longValue ()
public float floatValue ()
public double doubleValue ()

Implémentation des méthodes de la classe Number.

public boolean isInfinite ()


public boolean isNaN ()

Ces méthodes renvoie true si le nombre mémorisé est infini ou si c'est une valeur non significative.

public static boolean isInfinite (double v)


public static boolean isNaN (double v)

Ces méthodes renvoie true si v est une valeur infinie ou si c'est une valeur non significative.

public static String toString (double d)

Renvoie la chaîne de caractères correspondant au nombre d.

public static Double valueOf (String s) throws NumberFormatException

Renvoie une instance de la classe Double mémorisant le nombre contenu dans la chaîne de
caractères s. Si s contient des caractères invalides une exception NumberFormatException est
déclenchée.

public static long doubleToLongBits (double value)


public static double longBitsToDouble (long bits)

Ces méthodes convertissent la valeur value en son équivalent en long, ou inversement la valeur
bits de type long en double. Elles sont utilisées par les méthodes writeDouble () et readDouble ()
de certaines des classes d'entrée-sortie qui manipulent un nombre de type double sous forme de
valeur 64 bits.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe Double à un objet ou renvoyer une chaîne de caractères décrivant la valeur flottante.

Exemple

Application TestExpression .

La classe java.lang.String

Cette classe final permet de manipuler les chaînes de caractères constantes. Elle est détaillée au
chapitre sur les chaînes de caractères.

La classe java.lang.StringBuffer

Cette classe permet de manipuler et modifier des chaînes de caractères. Elle est détaillée au chapitre
sur les chaînes de caractères.

Calcul mathématique : la classe java.lang.Math


vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 10

Cette classe final rassemble l'ensemble des méthodes de calcul mathématique Java. Toutes ses
méthodes sont static.

Champs

public final static double E


public final static double PI

Ces champs permettent d'obtenir les nombres e (= 2.71828...) et Pi (π = 3.14159...).

Méthodes

public static double sin (double a)


public static double cos (double a)
public static double tan (double a)

Ces méthodes permettent de calculer le sinus, le cosinus du nombre a exprimé en radian.

public static double asin (double a)

Renvoie l'arc sinus (sin-1) du nombre a, exprimé en radian et compris entre -PI/2 et PI/2.

public static double acos (double a)

Renvoie l'arc cosinus (cos-1) du nombre a, exprimé en radian et compris entre 0 et PI.

public static double atan (double a)

Renvoie l'arc tangente (tan-1) du nombre a, exprimé en radian et compris entre -PI/2 et PI/2.

public static double atan2 (double a, double b)

Renvoie l'arc tangente (tan-1) de b/a, exprimé en radian et compris entre -PI et PI.

public static double exp (double a)


public static double log (double a)

Ces méthodes permettent de calculer l'exponentielle ou le logarithme népérien du nombre a.

public static double IEEEremainder (double f1, double f2)

Renvoie le reste de la division de f1 par f2 (par exemple Math.IEEEremainder (4.5, 2) = 0.5,


Math.IEEEremainder (4.5, 2.2) = 0.1) .

public static double floor (double a)

Renvoie la plus grande valeur entière plus petite ou égale à a.

public static double ceil (double a)

Renvoie la plus petite valeur entière plus grande ou égale à a.

public static double sqrt (double a)

Renvoie la racine carrée du nombre a.

public static double pow (double a, double b)

Renvoie le nombre a à la puissance b (ab).

public static double rint (double a)


public static int round (float a)
public static long round (double a)

Ces méthodes renvoient la valeur arrondie au plus proche entier du nombre décimal a.
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 11

public static synchronized double random ()

Renvoie un nombre aléatoire compris entre 0. inclus et 1. exclu. Voir aussi la classe
java.util.Random.

public static int abs (int a)


public static long abs (long a)
public static float abs (float a)
public static double abs (double a)

Ces méthodes renvoient la valeur absolue du nombre a.

public static int max (int a, int b)


public static long max (long a, long b)
public static float max (float a, float b)
public static double max (double a, double b)
public static int min (int a, int b)
public static long min (long a, long b)
public static float min (float a, float b)
public static double min (double a, double b)

Ces méthodes renvoient la valeur maximum ou minimum des nombres a et b.

Les méthodes log (), sqrt (), pow (),... auxquelles sont envoyés des arguments interdits en
mathématique (comme le logarithme népérien d'un nombre négatif) renvoie la valeur non
significative Double.NaN sans déclencher d'exception.

Exemples

Applets AfficheurDeCalcul , ObservateurCalcul, DrawIt , ImageNoirEtBlanc , Compteur


et Horloge .

Gestion des threads


L'interface java.lang.Runnable

Cette interface déclare uniquement la méthode run () :

public void run ()

Toute classe Classe1 peut implémenter l'interface Runnable, et implémenter une méthode run () qui
sera exécutée par les threads créés à partir de la classe Classe1.

Exemples

Application PaperBoardServer .
Applets Chrono , AfficheurDeCalcul , ObservateurCalcul , PaperBoardClient ,
AnimationFleche , ScrollText et Horloge .

La classe java.lang.Thread

Cette classe permet de créer et de manipuler les threads. Elle est détaillée au chapitre sur les
threads.

La classe java.lang.ThreadGroup

Cette classe permet de consulter ou créer des groupes de threads, pour rassembler vos threads par
exemple par type. Voir aussi la classe Thread.

Constructeurs

public ThreadGroup (String name) throws SecurityException


public ThreadGroup (ThreadGroup parent, String name)
throws SecurityException, IllegalThreadStateException
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 12

Méthodes

public String toString ()


public final void checkAccess ()
public final String getName ()
public final ThreadGroup getParent ()
public final boolean parentOf (ThreadGroup g)
public final synchronized void stop () throws SecurityException
public final synchronized void suspend () throws SecurityException
public final synchronized void resume () throws SecurityException
public final synchronized void destroy ()
throws SecurityException, IllegalThreadStateException
public final int getMaxPriority ()
public final synchronized void setMaxPriority (int newMaxPriority)
throws SecurityException, IllegalArgumentException
public final boolean isDaemon ()
public final void setDaemon (boolean daemon) throws SecurityException
public synchronized int activeCount ()
public synchronized int activeGroupCount ()
public int enumerate (Thread list [ ])
public int enumerate (Thread list [ ], boolean recurse)
public int enumerate (ThreadGroup list [ ])
public int enumerate (ThreadGroup list [ ], boolean recurse)
public synchronized void list ()
public void uncaughtException (Thread t, Throwable e)

Gestion du système
La classe java.lang.System

Cette classe final permet d'accéder à différentes fonctionnalités du système de la Machine Virtuelle
Java. Elle est détaillée au chapitre sur les objets.

La classe java.lang.Runtime

Cette classe permet de manipuler le Runtime Java, renvoyé par la méthode getRuntime (). Les
méthodes exit (), exec (), load () et loadLibrary () sont susceptibles de déclencher une
exception SecurityException si le gestionnaire de sécurité interdit ces opérations.

Méthodes

public static Runtime getRuntime ()

Renvoie le Runtime Java.

public synchronized void load (String filename)


throws SecurityException, UnsatisfiedLinkError
public synchronized void loadLibrary (String libname)
throws SecurityException, UnsatisfiedLinkError
public void exit (int status) throws SecurityException
public void gc ()
public void runFinalization ()

Ces méthodes ont le même effet que celles que de la classe java.lang.System.

public Process exec (String command)


throws IOException, SecurityException
public Process exec (String command, String envp [ ])
throws IOException, SecurityException
public Process exec (String cmdarray [ ])
throws IOException, SecurityException
public Process exec (String cmdarray [ ], String envp [ ])
throws IOException, SecurityException

Ces méthodes permettent d'exécuter la commande du système command ou cmdarray [0]. envp [] est
un tableau contenant les paramètres à passer à la commande au format param=valeur (pour passer
par exemple des paramètres à java ). exec () renvoie une instance de la classe Process qui permet
de contrôler la commande exécutée.
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 13

public long freeMemory ()


public long totalMemory ()

Ces méthodes renvoient la taille de la mémoire libre et la taille totale de la mémoire.

public void traceInstructions (boolean on)


public void traceMethodCalls (boolean on)
public InputStream getLocalizedInputStream (InputStream in)
public OutputStream getLocalizedOutputStream (OutputStream out)

La classe java.lang.Process

Les objets dérivant de cette classe abstract sont retournés par les méthodes exec () de l a classe
Runtime. Ces méthodes permettent de contrôler l'état d'un process.

Constructeurs

public Process ()

Méthodes

public abstract OutputStream getOutputStream ()


public abstract InputStream getInputStream ()
public abstract InputStream getErrorStream ()

Ces méthodes renvoient les flux de sortie, d'entrée et d'erreur d'un process.

public abstract int waitFor () throws InterruptedException


public abstract void destroy ()

Ces méthodes permettent d'attendre la fin d'un process ou de le détruire.

public abstract int exitValue () throws IllegalThreadStateException

Renvoie la valeur renvoyée par le process.

La classe java.lang.SecurityManager

Une classe dérivée de cette classe abstract permet de définir un gestionnaire de sécurité dont
l'instance est passée en paramètre à la méthode setSecurityManager () de la classe System. Ce
gestionnaire est utilisé par les navigateurs pour interdire aux applets 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

protected boolean inCheck

Constructeurs

protected SecurityManager () throws SecurityException

Méthodes

protected Class [ ] getClassContext ()


protected int classDepth (String name)
protected boolean inClass (String name)
protected ClassLoader currentClassLoader ()
protected int classLoaderDepth ()
protected boolean inClassLoader ()
public boolean getInCheck ()
public Object getSecurityContext ()
public void checkAccept (String host, int port) throws SecurityException
public void checkAccess (Thread t) throws SecurityException
public void checkAccess (ThreadGroup g) throws SecurityException
public void checkConnect (String host, int port) throws SecurityException
vendredi 13 octobre 2000 Du C/C++ à Java : Les classes de bases Page: 14

public void checkConnect (String host, int port, Object context)


throws SecurityException
public void checkCreateClassLoader () throws SecurityException
public void checkDelete (String file) throws SecurityException
public void checkExec (String cmd) throws SecurityException
public void checkExit (int status) throws SecurityException
public void checkLink (String libname) throws SecurityException
public void checkListen (int port) throws SecurityException
public void checkPackageAccess (String packageName) throws SecurityException
public void checkPackageDefinition (String packageName) throws SecurityException
public void checkPropertiesAccess () throws SecurityException
public void checkPropertyAccess (String key) throws SecurityException
public void checkRead (FileDescriptor fd) throws SecurityException
public void checkRead (String file) throws SecurityException
public void checkRead (String file, Object context) throws SecurityException
public void checkSetFactory () throws SecurityException
public boolean checkTopLevelWindow () throws SecurityException
public void checkWrite (FileDescriptor fd) throws SecurityException
public void checkWrite (String file) throws SecurityException
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 1

Les outils Java

Gestion de collections de données


Gestion des dates
Génération de nombres aléatoires
Autres classes d'outils

Gestion de collections de données


Le package java.util rassemble des classes d'utilitaires dont les plus intéressantes permettent de
gérer les collections de données (classes Vector, Stack, Dictionary, Hashtable, BitSet et interface
Enumeration).

L'interface java.util.Enumeration

Cette interface est implémentée par les classes désirant pouvoir faire une énumération des objets
mémorisés par une autre classe, comme par exemple la classe Vector. Les méthodes de cette
interface sont généralement utilisées dans une boucle while, comme dans l'exemple suivant (à
recopier dans un fichier EssaiEnumeration.java que vous compilez avec l'instruction javac
EssaiEnumeration.java pour ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction
java EssaiEnumeration) :

import java.util.*;

// Classe mémorisant des chaînes de caractères


class CollectionChaines
{
String [ ] tabChaines;
int nbreChaines = 0;

CollectionChaines (int max)


{
tabChaines = new String [max];
}

public void ajouterChaine (String chaine)


{
tabChaines [nbreChaines++] = chaine;
}

public Enumeration chaînes ()


{
return new EnumerationChaines (this);
}
}

// Classe permettant d'énumérer les chaînes de CollectionChaines


class EnumerationChaines implements Enumeration
{
CollectionChaines collection;
int indice = 0;

public EnumerationChaines (CollectionChaines collection)


{
this.collection = collection;
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 2

public boolean hasMoreElements ()


{
return indice < collection.nbreChaines;
}

public Object nextElement ()


{
if (indice < collection.nbreChaines)
return collection.tabChaines [indice++];
else
throw new NoSuchElementException ();
}
}

// Classe d'essai
public class EssaiEnumeration
{
public static void main (String [ ] args)
{
// Création d'une collection de chaînes et ajout de 3 chaînes
CollectionChaines donnees = new CollectionChaines (10);
donnees.ajouterChaine ("Toto");
donnees.ajouterChaine ("Titi");
donnees.ajouterChaine ("Tutu");

// Enumération des éléments de la collection


Enumeration enum = donnees.chaînes ();
while (enum.hasMoreElements ())
System.out.println (enum.nextElement ());
}
}

Méthodes

public boolean hasMoreElements ()

Cette méthode doit renvoyer true s'il reste encore un ou plusieurs éléments à énumérer.

public Object nextElement () throws NoSuchElementException

Cette méthode doit retourner l'élément suivant à énumérer ou déclencher une exception de classe
NoSuchElementException si le dernier élément a déjà été énuméré.

La classe java.util.Vector

Cette classe qui implémente l'interface Cloneable, permet de créer un vecteur. Ce type d'ensemble
permet de mémoriser un ensemble d'objets de classe quelconque dans un tableau de taille variable
(ces éléments peuvent être éventuellement égal à null). Comme pour les tableaux, l'accès aux
éléments se fait par un indice. La classe Vector comporte de nombreuses méthodes qui permettent
d'ajouter, d'insérer, de supprimer ou de rechercher des éléments. Toutes les méthodes de Vector sont
final sauf clone ().

Champs

protected Object [ ] elementData


protected int elementCount
protected int capacityIncrement

Ces champs correspondent aux éléments mémorisés, au nombre d'éléments mémorisés et à


l'incrément à appliquer au tableau elementData quand il est rempli. Ces classes étant protected ne
sont accessibles que dans les classes dérivées de Vector.

Constructeurs

public Vector ()
public Vector (int initialCapacity)
public Vector (int initialCapacity, int capacityIncrement)
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

public final synchronized void addElement (Object obj)

Ajoute l'élément obj en fin de vecteur. Si le tableau du vecteur est trop petit, il est automatiquement
agrandi.

public final synchronized void insertElementAt (Object obj, int index)


throws IndexOutOfBoundsException

Insère l'élément obj à l'indice index. Si le tableau du vecteur est trop petit, il est automatiquement
agrandi.

public final synchronized Object elementAt (int index)


throws IndexOutOfBoundsException
public final synchronized void setElementAt (Object obj, int index)
throws IndexOutOfBoundsException

Ces méthodes permettent d'interroger ou de modifier l'élément mémorisé à l'indice index. Si index
est plus grand que le nombre d'éléments du vecteur, une exception IndexOutOfBoundsException est
déclenchée.

public final synchronized boolean removeElement (Object obj)


public final synchronized void removeElementAt (int index)
throws IndexOutOfBoundsException
public final synchronized void removeAllElements ()

Ces méthodes permettent de retirer du vecteur, soit l'élément obj, soit l'élément mémorisé à l'indice
index, soit tous les éléments mémorisés. Les deux premières méthodes décalent les éléments qui
suivent l'élément retiré du tableau du vecteur à un indice inférieur.

public final boolean isEmpty ()

Renvoie true si le vecteur est vide.

public final int size ()

Renvoie le nombre d'éléments mémorisés dans le vecteur.

public final synchronized void setSize (int newSize)

Modifie la taille du vecteur. Si newSize est plus petit que le nombre d'éléments courant du vecteur,
les derniers éléments sont perdus, sinon un ensemble d'éléments égaux à null sont ajoutés pour
atteindre la taille newSize.

public final int capacity ()


public final synchronized void ensureCapacity (int minCapacity)

Ces méthodes permettent d'interroger ou de modifier la capacité courante du vecteur. La capacité du


vecteur est le nombre maximum que son tableau peut contenir avant que celui-ci est besoin d'être
agrandi.

public final synchronized void trimToSize ()

Réduit la capacité du tableau du vecteur aux nombres d'éléments mémorisés par le vecteur.

public final synchronized Object firstElement ()


throws NoSuchElementException
public final synchronized Object lastElement ()
throws NoSuchElementException

Ces méthodes renvoient le premier ou le dernier élément mémorisé par le vecteur.


vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 4

public final synchronized void copyInto (Object array [ ])


throws IndexOutOfBoundsException

Recopie dans le tableau array l'ensemble des éléments mémorisés par le vecteur. Si array est top
petit, une exception IndexOutOfBoundsException est déclenchée.

public final synchronized Enumeration elements ()

Permet d'énumérer les éléments du vecteur.

public final boolean contains (Object elem)

Renvoie true si le vecteur contient un élément égal à elem. La méthode equals () de la classe de
l'objet elem est utilisée pour comparer les objets.

public final int indexOf (Object elem)


public final synchronized int indexOf (Object elem, int index)
throws IndexOutOfBoundsException
public final int lastIndexOf (Object elem)
public final synchronized int lastIndexOf (Object elem, int index)
throws IndexOutOfBoundsException

Ces méthodes renvoient l'indice du premier ou du dernier élément égal à elem, ou -1 si elem n'est
pas trouvé. La méthode equals () de la classe de l'objet elem est utilisée pour comparer les objets.
index permet d'éventuellement spécifier le premier indice à partir duquel commencer la recherche.

public synchronized Object clone ()

Renvoie un clone du vecteur. Les éléments du vecteur ne sont pas clonés eux-mêmes. Cette
méthode outrepasse la méthode clone () de la classe Object.

public final synchronized String toString ()

Cette méthode outrepasse la méthode toString () de la classe Object, pour renvoyer une chaîne de
caractères décrivant la liste des éléments mémorisés par le vecteur.

Exemples

Application ConcatenationFichiers .
Applet PaperBoardClient .

La classe java.util.Stack

Cette classe qui dérive de Vector permet de créer des piles, où vous pouvez empiler un objet avec la
méthode push (), retirer l'élément en haut de la pile avec pop (), ou consulter sans le retirer
l'élément en haut de la pile avec peek ().

Méthodes

public Object push (Object item)


public Object pop () throws EmptyStackException
public Object peek () throws EmptyStackException
public boolean empty ()
public int search (Object o)

La classe java.util.Dictionary

Cette classe abstract permet de créer un dictionnaire représenté par un ensemble d'entrées
associant un élément et une clé. Chaque clé du dictionnaire est unique et est associé au plus à un
élément, mais un même élément peut avoir plusieurs clés d'accès. Les éléments et les clés devant
être de la classe Object, le cast de références permet donc d'utiliser n'importe quel type de classe
pour les clés et les éléments (chaînes de caractères, classes d'emballage des nombres ou d'autres
classes).
Un dictionnaire peut être comparé à un tableau : Dans un tableau tab, vous mémorisez un ensemble
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

abstract public int size ()

size ()doit renvoyer le nombre d'entrées dans le dictionnaire. Chaque clé devant être unique ce
nombre est égal au nombre de clés.

abstract public boolean isEmpty ()

isEmpty () renvoie true si le dictionnaire ne contient aucune entrée.

abstract public Object get (Object key)

Cette méthode doit renvoyer l'élément associé à la clé key, ou null si la clé key n'existe pas.

abstract public Object put (Object key, Object element)

Cette méthode doit ajouter dans le dictionnaire une entrée associant la clé key avec l'élément
element. Si une entrée avec une clé égale à key existe déjà (par la méthode equals ()), l'élément de
cette entrée est renvoyé par put () après avoir été remplacé par element. Sinon put () renvoie null,
après avoir ajouté une nouvelle entrée.

abstract public Object remove (Object key)

Cette méthode doit supprimer du dictionnaire l'entrée ayant comme clé key. Si une entrée avec une
clé égale à key existe déjà (par la méthode equals ()), l'élément de cette entrée est renvoyé par
remove () après que cette entrée ait été supprimée. Sinon, remove () renvoie null.

abstract public Enumeration keys ()


abstract public Enumeration elements ()

Ces méthodes doivent renvoyer un objet d'une classe implémentant Enumeration permettant
d'énumérer les clés et les éléments du dictionnaire respectivement.

La classe java.util.Hashtable

Cette classe implémente l'interface Cloneable et dérive de la classe Dictionary dont elle implémente
toutes les méthodes, en y ajoutant certaines méthodes. Les tables de hash utilise la valeur que
retourne la méthode hashCode () des clés, pour organiser le rangement des entrées de la table afin
que get () fonctionne avec efficacité. Les objets utilisés comme clés dans une table de hash
devraient avoir leur classe qui outrepassent les méthodes equals () et hashCode () pour un
fonctionnement correct (voir la classe Object).

Constructeurs

public Hashtable (int initialCapacity, float loadFactor)

Crée une table de hash avec une capacité initiale de initialCapacity entrées. La table est
réorganisée avec la méthode rehash (), à chaque fois que le nombre d'entrées atteint loadFactor
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.

public Hashtable (int initialCapacity)


public Hashtable ()

Méthodes

public int size ()


public boolean isEmpty ()
public synchronized Object get (Object key)
public synchronized Object put (Object key, Object element)
public synchronized Object remove (Object key)
public synchronized Enumeration keys ()
public synchronized Enumeration elements ()

Ces méthodes outrepassent celles de la classe Dictionary. Pour plus d'informations, consultez la
classe Dictionary.

public synchronized boolean contains (Object element)


public synchronized boolean containsKey (Object key)

Ces méthodes renvoient true si respectivement un élément ou une clé existe dans la table.

public synchronized void clear ()

clear () supprime toutes les entrées (clés et éléments) de la table.

protected void rehash ()

Cette méthode agrandit et réorganise la table pour plus d'efficacité.

public synchronized Object clone ()

Renvoie un clone de la table de hash. Cette méthode outrepasse la méthode clone () de la classe
Object.

public synchronized final String toString ()

Cette méthode outrepasse la méthode toString () de la classe Object, pour renvoyer une chaîne de
caractères décrivant l'ensemble des entrées (clé + élément) de la table.

La classe java.util.Properties

Cette classe dérivant de Hashtable désigne une table de hash ne pouvant mémoriser que des chaînes
de caractères et comportant des fonctionnalités de sauvegarde/lecture dans un fichier.
Cette classe ressemble un peu à la gestion des fichiers profile Windows (.INI ), et la classe System
l'utilise pour mémoriser les propriétés associés à un programme Java.

Champ

protected Properties defaults

Constructeurs

public Properties ()
public Properties (Properties defaults)

Méthodes

public String getProperty (String property)


public String getProperty (String property, String defaultValue)

Ces méthodes renvoient la propriété de nom property, ou defaultValue si cette propriété n'est pas
définie.
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 7

public Enumeration propertyNames ()

Permet d'énumérer l'ensemble des noms de propriétés.

public void list (PrintStream out)

Ecrit sur le flux de données out la liste des propriétés et de leur valeur.

public synchronized void load (InputStream in) throws IOException


public synchronized void save (OutputStream out, String header)

Ces méthodes permettent d'écrire ou de lire un ensemble de propriétés à patir des flux de données
in ou out. Le format des données est ensemble de couples property=value.

La classe java.util.BitSet

Cette classe final qui implémente l'interface Cloneable permet de manipuler aisément un ensemble
de bits. Les objets de cette classe ont leur nombre de bits mémorisés qui croit automatiquement à
l'appel des méthodes set () et clear (). Les méthodes and (), or () et xor () permettent
d'effectuer les opérations ET, OU et OU Exclusif bit à bit sur les ensembles de bits des objets de
cette classe.

Constructeurs

public BitSet ()
public BitSet (int nbits)

Les nbits bits qu'une instance de la classe BitSet mémorise sont stockés dans un tableau dont les
éléments sont de type long (64 bits).

Méthodes

public boolean get (int bitIndex)


public void set (int bitIndex)
public void clear (int bitIndex)
public void and (BitSet set)
public void or (BitSet set)
public void xor (BitSet set)
public int size ()

public boolean equals (Object obj)


public int hashCode ()
public Object clone ()
public String toString ()

Gestion des dates


La classe java.util.Date

Cette classe permet de manipuler les dates. Les objets de cette classe représente un moment donné à
la seconde près. La classe Date fournit des méthodes permettant d'obtenir ou de modifier l'année, le
mois, le jour, l'heure, la minute ou la seconde d'une date et de comparer des dates.

L'année est un int égal à 0 pour l'année 1900.


Le mois est un int allant de 0 pour le mois de Janvier à 11 pour le mois de décembre.
Le jour du mois est un int allant de 1 à 31.
L'heure est un int allant de 0 à 23.
La minute est un int allant de 0 à 59.
La seconde est un int allant de 0 à 59.

Les objets de la classe Date ne sont pas des horloges. A chaque fois que vous voulez obtenir le
temps courant, utilisez la méthode currentTimeMillis () de la classe System, ou créez un nouvel
objet de classe Date avec le constructeur par défaut.
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

public int getYear ()


public void setYear (int year)
public int getMonth ()
public void setMonth (int month)
public int getDate ()
public void setDate (int date)
public int getHours ()
public void setHours (int hours)
public int getMinutes ()
public void setMinutes (int minutes)
public int getSeconds ()
public void setSeconds (int seconds)

Ces méthodes permettent d'interroger ou de modifier l'année, le mois, le jour du mois, l'heure, la
minute ou la seconde d'une date.

public int getDay ()

Renvoie le jour de la semaine sachant que le nombre 0 correspond à dimanche.

public long getTime ()


public void setTime (long time)

Ces méthodes permettent d'interroger ou de modifier le temps en millisecondes

public boolean before (Date when)


public boolean after (Date when)

Ces méthodes renvoient true si une date est inférieure ou supérieure à la date when.

public int getTimezoneOffset ()


public static long UTC (int year, int month, int date,
int hours, int minutes, int seconds)

public static long parse (String s) throws IllegalArgumentException

Renvoie le temps à partir d'une chaîne de caractère respectant le format IETF.

public String toLocaleString ()

Renvoie une chaîne de caractères décrivant la date courante respectant la notation locale.

public String toGMTString ()

Renvoie une chaîne de caractères décrivant la date courante respectant la notation GMT.

public boolean equals (Object obj)


public int hashCode ()
public String toString ()

Exemples

Applets TraitementTexte , MiseAJourHorloge et Horloge .

Génération de nombres aléatoires


La classe java.util.Random
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 9

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

public synchronized void setSeed (long seed)


public int nextInt ()
public long nextLong ()
public float nextFloat ()
public double nextDouble ()
public synchronized double nextGaussian ()

Exemples

Applets ObservateurCalcul , DrawIt et ImageNoirEtBlanc .

Autres classes d'outils


La classe java.util.StringTokenizer

Cette classe permet d'énumérer à partir d'une chaîne de caractères str un ensemble de sous-chaînes
séparées par des délimiteurs (voir aussi la classe java.io.StreamTokenizer).

Constructeurs

public StringTokenizer (String str, String delim, boolean returnTokens)


public StringTokenizer (String str, String delim)
public StringTokenizer (String str)

Permet de spécifier la chaîne str dans laquelle on recherche des sous-chaînes. Les sous-chaînes
sont séparées par des délimiteurs qui peuvent être n'importe quel caractère de delim. Si
returnTokens est true, l'énumération rendra les sous-chaînes et les délimiteurs. Par défaut, le
délimiteur est un espace et returnTokens est égal à false.

Méthodes

public boolean hasMoreElements ()


public boolean hasMoreTokens ()

Ces deux méthodes renvoient vraies si la chaîne str a encore des sous-chaînes à énumérer.

public Object nextElement () throws NoSuchElementException


public String nextToken () throws NoSuchElementException
public String nextToken (String delim) throws NoSuchElementException

Ces trois méthodes renvoient la sous-chaîne suivante de str (ou le délimiteur si returnTokens est
true), et avance la position de recherche dans str au caractère suivant la sous-chaîne renvoyée. La
troisième méthode permet de remplacer les délimiteurs recherchés.

public int countTokens ()

Renvoie le nombre de sous-chaînes restant à énumérer.


vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 10

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

public void update (Observable o, Object arg)

Exemple

Applet ObservateurCalcul .

La classe java.util.Observable

Cette classe permet de créer des objets observés par des observateurs , ces derniers étant des
objets dont la classe implémente l'interface Observer. Quand, à la suite d'un changement, un objet
observé appelle la méthode notifyObservers (), chacun de ses observateurs voit sa méthode update
() appelée.

Constructeur

public Observable ()

Méthodes

public synchronized void addObserver (Observer o)


public synchronized void deleteObserver (Observer o)
public synchronized void deleteObservers ()
public synchronized int countObservers ()

Ces méthodes permettent d'ajouter, de supprimer ou de compter les observateurs d'un objet.

public void notifyObservers ()


public synchronized void notifyObservers (Object arg)

Ces méthodes provoqueront l'appel de la méthode update () de chacun des observateurs, si l'objet a
appelé setChanged () pour spécifier que l'objet a été modifié.

protected synchronized void setChanged ()


protected synchronized void clearChanged ()
public synchronized boolean hasChanged ()

Ces méthodes permettent de valider, d'annuler ou d'interroger un flag indiquant si l'objet a changé.

Voici un exemple illustrant l'utilisation de la classe Observable et de l'interface Observer, avec une
applet affichant des sinusoïdes, dérivée de l'exemple du chapitre sur les threads :

et le programme Java correspondant (à copier dans un fichier dénommé ObservateurCalcul.java et


invoqué à partir d'un fichier HTML) :

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

public class ObservateurCalcul extends Applet implements Observer


{
CalculateurObservable calculateur;
vendredi 13 octobre 2000 Du C/C++ à Java : Les outils Java Page: 11

public void start ()


{
// Création d'un calculateur que l'applet observe
(calculateur = new CalculateurObservable (this)).addObserver (this);
}

public void stop ()


{
calculateur.stop ();
}

public void update (Observable calculateur, Object arg)


{
((CalculateurObservable)calculateur).dessinerCourbe (getGraphics ());
}
}

class CalculateurObservable extends Observable implements Runnable


{
private int [ ] courbe;
private Component fenetre;
private Random generateur = new Random ();
private Thread threadCalculateur;

public CalculateurObservable (Component fenetre)


{
this.fenetre = fenetre;
// Démarrage d'un thread de calcul
(threadCalculateur = new Thread (this)).start ();
}

public void stop ()


{
threadCalculateur.stop ();
}

public void calculerCourbe ()


{
courbe = new int [fenetre.size ().width];
// Calcul d'une sinusoïde avec fréquence aléatoire
double pasCalcul = 2 * Math.PI / courbe.length / Math.random ();
for (int i = 0; i < courbe.length; i++)
courbe [i] = (int)( (Math.sin (i * pasCalcul) + 1)
* fenetre.size ().height / 2);
// Nouveau calcul
setChanged();
}

public void dessinerCourbe (Graphics gc)


{
gc.setColor (Color.white);
gc.fillRect (0, 0, fenetre.size ().width, fenetre.size ().height);
// Création d'une couleur aléatoire
gc.setColor (new Color (generateur.nextFloat (),
generateur.nextFloat (),
generateur.nextFloat ()));
if (courbe != null)
// Dessin de la courbe en reliant les points un à un
for (int i = 1; i < courbe.length; i++)
gc.drawLine (i - 1, courbe [i - 1], i, courbe [i]);
}

public void run ()


{
try
{
while (threadCalculateur.isAlive ())
{
// Calculer une courbe puis prévenir d'un nouveau calcul
calculerCourbe ();
notifyObservers ();

Thread.sleep (200);
}
}
catch (InterruptedException e) { }
vendredi 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

La gestion des fichiers


et des flux de données

Gestion des entrées-sorties


Manipulation des fichiers
Gestion des flux de données
Accès à un flux de données en lecture
Accès à un flux de données en écriture
Gestion de l'accès aléatoire aux fichiers

Ce chapitre décrit le package java.io de Java 1.0 qui rassemble les classes permettant de manipuler
les fichiers et de gérer les entrées-sorties.

Gestion des entrées-sorties


En observant la hiérarchie des classes de Java, vous serez peut-être étonné par le nombre de classes
dévolues à la gestion des entrées-sorties (package java.io), et particulièrement celles de gestion des
flux de données (stream ). Ne vous inquiétez pas : une fois que vous aurez cerner le principe de
fonctionnement de la gestion des flux de données, vous verrez que toutes les classes dérivant des
classes InputStream et OutputStream s'utilisent de manière intuitive.

Mode d'accès aux données

Comme dans la plupart des langages informatiques, Java permet de manipuler les fichiers soit en
accès par flux de données (Stream ), soit en accès aléatoire (Random access ) :

Le mode d'accès par flux de données permet d'accéder aux données sous la forme d'un flux séquentiel.
Pour faire un parallèle, imaginez un lecteur de CD sans les touches d'avance et de retour rapides >> et << :
Vous pouvez accéder à un morceau de musique grâce aux touches >>| et |<< et l'écouter, mais quand le
morceau est en train d'être lu, vous ne pouvez que l'écouter de manière continu sans revenir en arrière ni
sauter une partie.
Similairement quand vous accédez à un flux de données, une fois que vous commencez à accéder aux
données, vous devez traiter les données au fur et à mesure qu'elles vous sont délivrées.

Ce système permet d'accéder à toute sorte de sources de données comme :


un fichier (classes FileInputStream et FileOutputStream)
une chaîne de caractères (classe StringBufferInputStream)
un tableau d'octets (classes ByteArrayInputStream et ByteArrayOutputStream)
un pipeline (classes PipedInputStream et PipedOutputStream)
l'entrée et les sorties standards (champs System.in, System.out et System.err ou FileDescriptor.in,
FileDescriptor.out et FileDescriptor.err)
une URL
un socket.

Quel est l'intérêt d'utiliser un flux de données pour accéder à une chaîne de caractères ou à un tableau
d'octets, alors qu'il sera impossible d'accéder aux données dans un ordre quelconque ?
La programmation orientée objet utilisée par Java permet de manipuler les données entrantes ou sortantes
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.

Contrairement au C où il existe des fonctions différentes suivant le type de source de données


(comme scanf (), fscanf (), sscanf ()), Java permet d'accéder aux différents types de flux de
données par les mêmes méthodes.
Le mode d'accès aléatoire permet d'accéder aux données dans n'importe quel ordre. Vous pouvez à tout
moment aller en avant ou en arrière pour lire ou écrire une partie des données. Ce mode d'accès est
comparable à celui utilisé pour la mémoire vive (Random Access Memory elle aussi !). Du fait que
certains types de source de données, comme les pipelines ou les URL ne peuvent supporter ce mode
d'accès, son usage est plus spécifique et Java fournit uniquement la classe RandomAccessFile qui permet
d'accéder à une position quelconque dans un fichier.

En C, la différence entre les deux types d'accès par flux de données (fonctions f... () de
stdio.h ) et à accès aléatoire (open (), read (), ...) n'apparaît pas toujours de manière évidente. En
effet, les méthodes ftell () et fseek () permettent de se déplacer à une position quelconque dans
un fichier, ce qui rend possible l'accès aléatoire !
En Java, la différence est franche : Les classes InputStream et OutputStream et leurs classes
dérivées ne permettent d'accéder que sous forme de flux de données continus (avec éventuellement
un retour en arrière quand c'est possible), au contenu d'un fichier ou d'une source de donnée d'un
autre type, et la classe RandomAccessFile permet d'accéder aléatoirement au contenu d'un fichier.

Gestion de l'accès aux données avec les exceptions

Les exceptions en Java permettent de programmer de manière plus simple et surtout plus maintenable une
séquence d'instructions devant effectuer des entrées/sorties. Ce type de programme effectue
habituellement un grand nombre de contrôles puisqu'il faut vérifier à chaque accès à un flux de données
ou un fichier si tout s'est bien déroulé. Une programmation n'utilisant pas les exceptions est donc truffée
de contrôles après chaque instruction, ce qui en réduit la lisibilité.
En Java, la plupart des méthodes d'entrée/sortie déclenche une exception de classe IOException ou de ses
dérivées EOFException, FileNotFoundException, InterruptedIOException ou UTFDataFormatException. Par
exemple, une méthode qui va effectuer une lecture à l'intérieur d'un fichier respectera généralement le
schéma suivant :

import java.io.*;

class Classe1
{
void lireFichier (String fichier)
{
try
{
// Suite d'instructions accédant au fichier et
// ne s'occupant pas de la gestion des erreurs

// Tentative d'ouvrir un fichier

// Lecture dans le fichier


}
catch (FileNotFoundException e)
{
// Exception déclenchée si le fichier n'existe pas
}
catch (EOFException e)
{
// Exception déclenchée si la fin du fichier est atteinte
}
catch (IOException e)
{
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 3

// Exception déclenchée si un autre problème survient


// pendant l'accès au fichier
}
finally
{
// Le bloc finally est toujours exécuté ce qui permet d'être sûr
// que la fermeture du fichier sera effectuée
try
{
// Fermeture du fichier si le fichier a été ouvert
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant la fermeture
}
}
}
}

Manipulation des fichiers


Java permet de manipuler les fichiers situés sur le système local (par opposition à ceux auquel on
accède via une URL), grâce à l'interface FilenameFilter et les classes File et FileDescriptor. On
peut ajouter à cette liste la classe java.awt.FileDialog, qui permet de choisir interactivement un
fichier avec une boite de dialogue.

L'interface java.io.FilenameFilter

Cette interface est utilisée pour permettre d'accepter ou de rejeter un fichier issu d'une liste. Une
instance d'une classe implémentant cette interface est requise par les méthodes list () de la classe
File et setFilenameFilter () de la classe FileDialog.

Méthode

public boolean accept (File dir, String name)

Cette méthode doit renvoyer true si le fichier name se trouvant dans le répertoire dir est accepté.

La classe java.io.File

Cette classe est utilisée pour représenter un chemin d'accès au système de fichiers local et effectuer
des opérations sur celui-ci (autorisation d'accès en lecture/écriture, liste des fichiers d'un
répertoire,...). Ce chemin d'accès peut désigner un fichier ou un répertoire.
Les méthodes de cette classe peuvent éventuellement déclenchée une exception SecurityException,
si le gestionnaire de sécurité actif leur interdit d'être exécutée. Les applets fonctionnant dans un
navigateur empêchent généralement notamment tout accès au système de fichiers local donc
n'imaginez pas une applet qui puisse accéder aux fichiers du système dans ce contexte.
La description des chemins d'accès (séparateurs, description des disques,...) utilise les conventions
du système sur lequel est exécutée la Machine Virtuelle.

Champs

public static final String separator


public static final char separatorChar

La chaîne ou le caractère utilisé comme séparateur de fichier (égal généralement à / ou \).

public static final String pathSeparator


public static final char pathSeparatorChar

La chaîne ou le caractère utilisé comme séparateur de plusieurs chemins d'accès (égal généralement
à ;).

Constructeurs

public File (String path)


vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 4

Construit une instance de la classe File, à partir d'un chemin d'accès path pouvant être absolu ou
relatif au répertoire courant. path peut représenter un répertoire ou un fichier suivant l'utilisation
que vous voulez faire de cette nouvelle instance.

public File (String dir, String name)


public File (File dir, String name)

Ces constructeurs permettent de créer une instance de la classe File, à partir d'un répertoire dir
(chemin d'accès absolu ou relatif au répertoire courant), et d'un nom de fichier ou de répertoire name
accessible à partir du répertoire dir.

Méthodes

public String getName ()

Renvoie le nom du fichier ou du répertoire d'un chemin d'accès.

public String getPath ()

Renvoie le chemin d'accès d'un objet de classe File.

public String getAbsolutePath ()

Renvoie le chemin d'accès absolu.

public String getParent ()

Renvoie le chemin d'accès du répertoire parent au chemin d'accès de l'objet de classe File, ou null
s'il n'existe pas.

public boolean exists () throws SecurityException


public boolean isFile () throws SecurityException
public boolean isDirectory () throws SecurityException
public boolean isAbsolute ()

Ces méthodes renvoient true, si le chemin d'accès existe, s'il correspond à un fichier ou à un
répertoire, ou si le chemin d'accès est absolu.

public boolean canRead () throws SecurityException


public boolean canWrite () throws SecurityException

Ces méthodes renvoient true, si le chemin d'accès est accessible en lecture ou en écriture.

public long lastModified () throws SecurityException

Renvoie la date de dernière modification du fichier. Cette date n'étant pas exprimé dans la même
échelle que celle utilisée par la classe Date, elle ne peut être utilisée que pour comparer des dates de
différents fichiers.

public long length () throws SecurityException

Renvoie la taille du chemin d'accès représentant un fichier.

public boolean delete () throws SecurityException


public boolean renameTo (File dest) throws SecurityException

Ces méthodes permettent de supprimer ou de renommer un chemin d'accès, et renvoient true si


l'opération s'est effectuée correctement.

public boolean mkdir () throws SecurityException


public boolean mkdirs () throws SecurityException

Ces méthodes permettent de créer le répertoire ou les répertoires représentés par le chemin d'accès
et renvoient true si l'opération s'est effectuée correctement.

public String [ ] list () throws SecurityException


public String [ ] list (FilenameFilter filter) throws SecurityException
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 5

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.

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
chemin d'accès à un objet ou renvoyer une chaîne de caractères égale au chemin d'accès.

La classe java.io.FileDescriptor

Cette classe final représente un descripteur de fichier. Une instance de cette classe permet de
manipuler un fichier ouvert ou l'entrée et les sorties standards.
Les méthodes getFD () des classes FileInputStream, FileOutputStream et RandomAccessFile
permettent d'obtenir une instance de la classe FileDescriptor et les 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

public static final FileDescriptor in


public static final FileDescriptor out
public static final FileDescriptor err

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

public boolean valid ()

Gestion des flux de données


En Java, les classes dévolues aux flux de données permettent d'accéder aux données soit en lecture
grâce aux classes qui dérivent de la classe abstract InputStream, soit en écriture grâce aux classes
qui dérivent de la classe abstract OutputStream.

A chaque type de flux de données (fichier, chaîne de caractères, tableau d'octets, pipeline)
correspond une classe permettant de le manipuler, mais il existe aussi un ensemble de classes de
filtres dérivées des classes abstract FilterInputStream et FilterOutputStream qui peuvent s'utiliser
ensemble pour simplifier ou optimiser l'accès aux flux.

Accès à un flux de données en lecture


L'accès en lecture à un flux de données s'effectue grâce aux classes qui dérivent de la classe
InputStream. Ces classes se divisent en deux catégories :

Les classes qui permettent de se lier à un type de source de données spécifique :


la classe FileInputStream pour accéder en lecture à un fichier
la classe StringBufferInputStream pour parcourir une chaîne de caractères sous forme de flux de
données
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 6

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 :

Classes Paramètres des constructeurs


Chemin d'accès à un fichier (classe String)
FileInputStream Fichier décrit par une instance de la classe File
Descripteur de fichier (classe FileDescriptor)
StringBufferInputStream Chaîne de caractères (classe String)
ByteArrayInputStream Tableau d'octets (de type byte [])

Aucun pour créer un pipeline d'entrée


PipedInputStream
Sortie d'un pipeline (classe PipedOutputStream)
Une énumération de flux de données de classe InputStream
SequenceInputStream
Deux flux de données de classe InputStream

Une fois que vous avez créé une instance d'une de ces classes, vous pouvez appeler les méthodes de la
classe InputStream, puisque toutes ces classes en héritent, comme dans l'application suivante qui permet
d'écrire sur la sortie standard les n premiers caractères d'un fichier. Recopiez-la dans un fichier
LectureFichier.java , que vous compilez avec l'instruction javac LectureFichier.java pour ensuite
l'exécuter avec java ou Java Runner , grâce à l'instruction java LectureFichier nomFichier
nombreCaracteres (par exemple java LectureFichier LectureFichier.java 100) :

import java.io.*;

public class LectureFichier


{
// Méthode lancée à l'appel de l'instruction :
// java LectureFichier nomFichier nombreCaracteres
public static void main (String [ ] args)
{
try
{
// Ouverture du fichier passé en paramètre dans la ligne de commande
InputStream fluxFichier = new FileInputStream (args [0]);

// Lecture des n premiers octets du fichier. n est passé en paramètre


byte contenuFichier [ ] = new byte [Integer.parseInt (args [1])];
fluxFichier.read (contenuFichier);

// Ecriture sur la sortie standard des octets lus convertis


// en une chaîne de caractères
System.out.println (new String (contenuFichier, 0));

// Fermeture du fichier
fluxFichier.close ();
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant l'accès au fichier
System.out.println (e);
}
}
}

L'accès aux trois autres types de sources de données restantes (l'entrée standard, les sockets et un fichier
via une URL) s'effectue différemment :
L'entrée standard est accessible soit directement par 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 :

figure 11. Utilisation cumulée de filtres

L'application suivante utilise ce schéma de filtres pour numéroter les lignes lues dans un fichier.
Recopiez la dans un fichier NumerotationLigne.java , que vous compilez avec l'instruction javac
NumerotationLigne.java pour ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction java
NumerotationLigne nomFichier (par exemple java NumerotationLigne NumerotationLigne.java) :

import java.io.*;

public class NumerotationLigne


{
// Méthode lancée à l'appel de l'instruction :
// java LectureFichier nomFichier
public static void main (String [ ] args)
{
try
{
// Ouverture du fichier passé en paramètre dans la ligne de commande
// avec un filtre utilisant un buffer
InputStream fluxFichier = new BufferedInputStream (
new FileInputStream (args [0]));

// Numérotation des lignes


numeroterLigne (fluxFichier);

fluxFichier.close ();
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);
}
}

public static void numeroterLigne (InputStream flux) throws IOException


{
// Création d'un filtre de décompte des lignes
LineNumberInputStream fluxLignes = new LineNumberInputStream (flux);
// Ajout d'un filtre pour lire de manière plus pratique les caractères
DataInputStream fluxLecture = new DataInputStream (fluxLignes);

// Lecture des lignes du flux jusqu'à la fin du flux


String ligne;
for (ligne = fluxLecture.readLine ();
ligne != null;
ligne = fluxLecture.readLine ())
// Ecriture sur la sortie standard de la ligne lue avec son numéro de ligne
System.out.println (String.valueOf (fluxLignes.getLineNumber ())
+ " : " + ligne);
}
}

En poursuivant dans cette logique, il est simple de créer et d'utiliser vos propres filtres sur des flux de
données en dérivant vos nouvelles classes de la classe FilterInputStream ou d'une de ses classes
dérivées.

La classe java.io.InputStream

Cette classe abstract est la super classe de toutes les classes qui permettent d'accéder à d'un flux de
données en lecture (voir aussi le paragraphe précédent).

Méthodes

public abstract int read () throws IOException

Lit un octet dans le flux de données. Renvoie -1 si la fin est atteinte.

public int read (byte [ ] tab) throws IOException

Lit tab.length octets dans le tableau tab. Si la fin est atteinte pendant la lecture, tab est remplit avec les
octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est atteinte.

public int read (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Lit length octets dans le tableau tab, en le remplissant à partir de l'indice offset. Si la fin est atteinte
pendant la lecture, tab est remplit avec les octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est
atteinte.

public long skip (long n) throws IOException

Saute n octets plus loin dans la lecture du flux de données. Renvoie le nombre d'octets effectivement
sautés.

public int available () throws IOException

Renvoie le nombre d'octets que les méthodes read () peuvent actuellement lire sans bloquer le thread
courant.

public void close () throws IOException

Ferme l'accès au flux de données.

public synchronized void mark (int readlimit)


vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 9

Marque la position courante dans le flux de données, pour qu'un appel à la méthode reset () provoque
un retour en arrière à cette position. readlimit permet de préciser le nombre d'octets maximum que l'on
peut lire avant que la marque soit invalide, c'est-à-dire la taille maximum du retour en arrière qu'il est
possible de faire.

public synchronized void reset () throws IOException

Repositionne la position courante dans le flux de données sur la dernière marque positionnée par la
méthode mark (). Si aucune marque n'a été positionnée ou si la marque est invalide, une exception
IOException est déclenchée.

public boolean markSupported ()

Renvoie true si le flux de données autorise l'utilisation des méthodes mark () et reset ().

Exemples

Applications LectureFichier , NumerotationLigne , ConcatenationFichiers et EchoServeur .

La classe java.io.FileInputStream

Cette classe qui dérive de la classe InputStream permet d'ouvrir et d'accéder à un fichier en lecture, sous
forme de flux de données.

Constructeurs

public FileInputStream (String path)


throws SecurityException, FileNotFoundException
public FileInputStream (File file)
throws SecurityException, FileNotFoundException

Ces constructeurs permettent de créer une instance de classe FileInputStream à partir du chemin d'accès à
un fichier path ou d'une instance de classe File.

public FileInputStream (FileDescriptor fdObj) throws SecurityException

Construit une instance de classe FileInputStream à partir d'un descripteur de fichier fdObj.

Méthodes

public int read () throws IOException


public int read (byte [ ] tab) throws IOException
public int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public long skip (long n) throws IOException
public int available () throws IOException
public void close () throws IOException

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à la lecture d'un fichier.

public final FileDescriptor getFD () throws IOException

Permet d'obtenir un descripteur de fichier.

protected void finalize () throws IOException

Méthode de la classe Object outrepassée pour que le fichier ouvert à la création d'une instance de la
classe FileInputStream soit fermé avant que le Garbage Collector n'intervienne pour détruire cet objet. Le
moment où intervient le Garbage Collector n'étant généralement pas prévisible, il vaut mieux fermer
soi-même le fichier avec la méthode close (), pour éviter d'en bloquer l'accès à d'autres programmes.

Exemples

Applications LectureFichier , NumerotationLigne et ConcatenationFichiers .

La classe java.io.StringBufferInputStream
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

protected String buffer


protected int pos
protected int count

Constructeur

public StringBufferInputStream (String s)

Construit une instance de classe StringBufferInputStream à partir de la chaîne de caractères s.

Méthodes

public synchronized int read ()


public synchronized int read (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException
public synchronized long skip (long n)
public synchronized int available ()
public synchronized void reset ()

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à une chaîne de
caractères.

Exemple

Applet PaperBoardClient .

La classe java.io.ByteArrayInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder à un tableau d'octets en lecture, sous
forme de flux de données.

Champs

protected byte [ ] buf


protected int pos
protected int count

Constructeurs

public ByteArrayInputStream (byte [ ] buf)

Construit une instance de classe ByteArrayInputStream à partir du tableau d'octets buf.

public ByteArrayInputStream (byte [ ] buf, int offset, int length)

Construit une instance de classe ByteArrayInputStream à partir du tableau d'octets buf, où length octets
seront lus à partir de l'indice offset.

Méthodes

public synchronized int read () throws IndexOutOfBoundsException


public synchronized int read (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException
public synchronized long skip (long n)
public synchronized int available ()
public synchronized void reset ()

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un tableau d'octets.

La classe java.io.PipedInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder à un pipeline en lecture, sous forme de
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

public void connect (PipedOutputStream src) throws IOException

Crée une connexion avec la sortie du pipeline src.

public synchronized int read () throws IOException


public synchronized int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un pipeline.

La classe java.io.SequenceInputStream

Cette classe qui dérive de la classe InputStream permet d'accéder en lecture à un ensemble de flux de
données les uns après les autres.

Constructeurs

public SequenceInputStream (InputStream s1, InputStream s2)

Construit une instance de classe SequenceInputStream à partir des flux de données s1 et s2. Les méthodes
read () de cette classe liront le contenu des flux s1 et s2 l'un à la suite de l'autre, comme s'il forme un
seul flux.

public SequenceInputStream (Enumeration e)

Construit une instance de classe SequenceInputStream à partir d'une énumération de flux de données e.
Les méthodes read () de cette classe liront le contenu de tous les flux énumérés par e, comme s'il forme
un seul flux. Chacun des objets énumérés par e doit donc être de classe InputStream ou d'une de ses
classes dérivées.

Méthodes

public int read () throws IOException


public int read (byte [ ] buf, int pos, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un ensemble de flux
de données.

Exemple

Application ConcatenationFichiers .

La classe java.io.FilterInputStream

Cette classe qui dérive de la classe InputStream est la super classe de toutes les classes utilisées pour
filtrer la lecture d'un flux de données. Vous pouvez utiliser les classes BufferedInputStream,
DataInputStream, LineNumberInputStream ou PushBackInputStream qui en dérivent mais aussi créer vos
propres classes de filtre dérivant de cette classe.

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

des données sur le flux à filtrer.

Constructeur

protected FilterInputStream (InputStream in)

Le constructeur de cette classe est protected ce qui vous empêche de l'instancier (cette classe n'a de toute
façon aucun effet sur le flux de données ). Il est utilisé par les classes dérivées.

Méthodes

public int read () throws IOException


public int read (byte [ ] tab) throws IOException
public int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public long skip (long n) throws IOException
public int available () throws IOException
public void close () throws IOException
public synchronized void mark (int readlimit)
public synchronized void reset () throws IOException
public boolean markSupported ()

Méthodes de la classe InputStream outrepassées pour qu'elles s'appliquent à l'accès à un flux de données
filtré en lecture.

Voici un exemple d'une classe de filtre dérivée de FilterInputStream qui a pour effet de mettre en
majuscule toutes les lettres qui sont lus sur un flux de données :

class ToUpperCaseInputStream extends FilterInputStream


{
// Constructeur : appel du constructeur de la super classe
public ToUpperCaseInputStream (InputStream flux)
{
super (flux);
}

public int read () throws IOException


{
// Lecture sur le flux d'un byte
int car = (char)in.read ();

// Si la fin du fichier n'est pas atteinte et si le caractère


// lu est une minuscule renvoyer la majuscule correspondante
if ( car != -1
&& Character.isLowerCase ((char)car))
return Character.toUpperCase ((char)car);
return car;
}

public int read (byte tab [], int offset, int length) throws IOException
{
// Lecture sur le flux : read () renvoie le nombre d'octets lus
int nbreOctetsLus = in.read (tab, offset, length);

// Conversion de toutes les minuscules lues en majuscules


for (int i = 0; i < nbreOctetsLus; i++)
if (Character.isLowerCase ((char)tab [offset + i]))
tab [offset + i] = (byte)Character.toUpperCase ((char)tab [offset + i]);
return nbreOctetsLus;
}
}

Si dans l'application NumerotationLigne vous remplacez la ligne :

DataInputStream fluxLecture = new DataInputStream (fluxLignes);

par les lignes :

DataInputStream fluxLecture = new DataInputStream (


new ToUpperCaseInputStream (fluxLignes));
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 13

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

protected byte [ ] buf


protected int count
protected int pos
protected int markpos
protected int marklimit

Constructeurs

public BufferedInputStream (InputStream in)


public BufferedInputStream (InputStream in, int size)

Ces constructeurs permettent de créer une instance de la classe BufferedInputStream dont la lecture sera
effectuée sur le flux de données in. size est la taille utilisée pour créer le buffer (égal à 2048 par défaut).

Méthodes

public synchronized int read () throws IOException


public synchronized int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public synchronized long skip (long n) throws IOException
public synchronized int available () throws IOException
public synchronized void mark (int readlimit)
public synchronized void reset () throws IOException
public boolean markSupported ()

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

Exemples

Applications NumerotationLigne , ConcatenationFichiers et PaperBoardServer .


Applet PaperBoardClient .

L'interface java.io.DataInput

Cette interface implémentée par les classes DataInputStream et RandomAccessFile déclare un ensemble de
méthodes qui permettent de lire à partir d'un flux de données ou d'un fichier des données de différents
types : Soit un ensemble d'octets grâce aux méthodes readFully (), soit une donnée binaire représentant
un type de base Java, soit une chaîne de caractères.

Méthodes

public void readFully (byte [ ] tab) throws IOException

Cette méthode doit permettre de lire tab.length octets dans le tableau tab. Une exception EOFException
est déclenchée si la fin du fichier est atteinte, et une exception IOException est déclenchée en cas d'autre
erreur.

public void readFully (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Cette méthode doit permettre de lire length octets dans le tableau tab, en le remplissant à partir de
l'indice offset. Une exception EOFException est déclenchée si la fin du fichier est atteinte, et une
exception IOException est déclenchée en cas d'autre erreur.
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 14

public boolean readBoolean () throws IOException


public byte readByte () throws IOException
public int readUnsignedByte () throws IOException
public short readShort () throws IOException
public int readUnsignedShort () throws IOException
public char readChar () throws IOException
public int readInt () throws IOException
public long readLong () throws IOException
public float readFloat () throws IOException
public double readDouble () throws IOException

Ces méthodes doivent permettre de lire une valeur booléenne, un octet, un octet non signé (codé sur 8
bits et compris en 0 et 255), un short, un short non signé, un int, un long, un float ou un double. Une
exception EOFException est déclenchée si la fin du fichier est atteinte, et une exception IOException est
déclenchée en cas d'autre erreur.

public String readLine () throws IOException

Cette méthode doit permettre de lire tous les caractères jusqu'au premier symbole de retour à la ligne.
Elle manipule des caractères codés sur 8 bits et non sur 16 bits (Unicode). Renvoie la chaîne de
caractères lue sans le caractère de retour à la ligne à la fin, ou null si la fin du fichier est atteinte.

public String readUTF () throws IOException

Cette méthode doit permettre de lire une chaîne de caractères qui est codée au format UTF. Renvoie la
chaîne de caractères lue ou null si la fin du fichier est atteinte.

public int skipBytes (int n) throws IOException

Cette méthode doit permettre de sauter n octets. Une exception EOFException est déclenchée si la fin du
fichier est atteinte, et une exception IOException est déclenchée en cas d'autre erreur.

La classe java.io.DataInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream implémente l'interface DataInput, ce
qui permet de lire à partir d'un flux de données autres chose que des octets.

Constructeur

public DataInputStream (InputStream in)

Construit une instance de la classe DataInputStream dont la lecture sera effectuée sur le flux de données
in.

Méthodes

public final void read (byte [ ] tab) throws IOException


public final void read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public final void readFully (byte [ ] tab)


throws IOException
public final void readFully (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final boolean readBoolean () throws IOException
public final byte readByte () throws IOException
public final int readUnsignedByte () throws IOException
public final short readShort () throws IOException
public final int readUnsignedShort () throws IOException
public final char readChar () throws IOException
public final int readInt () throws IOException
public final long readLong () throws IOException
public final float readFloat () throws IOException
public final double readDouble () throws IOException
public final String readLine () throws IOException
public final String readUTF () throws IOException
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 15

public final static String readUTF (DataInput in) throws IOException


public final int skipBytes (int n) throws IOException

Implémentation des méthodes de l'interface DataInput.

Exemple

Applications NumerotationLigne et PaperBoardServer .


Applet PaperBoardClient .

La classe java.io.LineNumberInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream permet de compter le nombre de
lignes lors de la lecture d'un flux de données. Une ligne se termine par le caractère '\n'. L'application
NumerotationLigne utilise cette classe de filtre.

Constructeur

public LineNumberInputStream (InputStream in)

Construit une instance de la classe LineNumberInputStream dont la lecture sera effectuée sur le flux de
données in. Le numéro de ligne est initialisé à 0.

Méthodes

public int read () throws IOException


public int read (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public long skip (long n) throws IOException
public int available () throws IOException
public void mark (int readlimit)
public void reset () throws IOException

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public int getLineNumber ()


public void setLineNumber (int lineNumber)

Ces méthodes permettent d'interroger ou de modifier le numéro de ligne courant.

Exemple

Application NumerotationLigne .

La classe java.io.PushBackInputStream

Cette classe qui dérive des classes FilterInputStream et InputStream permet de simuler un retour en
arrière d'un octet pendant la lecture d'un flux de données. La méthode unread () qui est utilisée pour
revenir en arrière d'un octet prend en paramètre l'octet à renvoyer à la lecture suivante. Cette classe est
surtout utilisée pour les analyseurs lexicaux et syntaxiques.

Champ

protected int pushBack

Constructeur

public PushbackInputStream (InputStream in)

Méthodes

public int read () throws IOException


public int read (byte [ ] bytes, int offset, int length)
throws IOException, IndexOutOfBoundsException
public int available () throws IOException
public boolean markSupported ()
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 16

Méthodes de la classe FilterInputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public void unread (int ch) throws IOException

Permet de simuler un retour en arrière d'un octet. L'octet ch est le prochain octet qui sera renvoyé par un
appel à une méthode read (). Cette méthode déclenche une exception de classe IOException si deux
appels à unread () sont effectués à la suite sans appel à une des méthodes read () entre temps.

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

public static final int TT_EOF


public static final int TT_EOL
public static final int TT_NUMBER
public static final int TT_WORD

Ces constantes représentent le type d'une sous-chaîne lue : la fin du flux de données, la fin d'une ligne, un
nombre ou un mot.

public int ttype

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.

public String sval


public double nval

Ces champs mémorisent le dernier nombre (si ttype = TT_NUMBER) ou le dernier mot lu (si ttype =
TT_WORD).

Constructeur

public StreamTokenizer (InputStream in)

Construit une instance de la classe StreamTokenizer avec comme flux de données d'entrée in.

Méthodes

public void wordChars (int low, int high)

Permet d'ajouter tous les caractères dont le code est compris entre low et high à l'ensemble des caractères
utilisés pour construire un mot. La lecture d'un mot s'arrête quand un des caractères utilisés comme
espace ou un caractère ordinaire sont lus. Quand un mot est lu, le champ ttype est égal à TT_WORD et le
champ sval contient le mot lu.

public void whitespaceChars (int low, int high)

Permet d'ajouter tous les caractères dont le code est compris entre low et high à l'ensemble des caractères
utilisés comme espace.

public void commentChar (int ch)

Permet d'ajouter le caractère de code ch à l'ensemble des caractères délimitant le début d'un commentaire
de fin de ligne.

public void ordinaryChar (int ch)


public void ordinaryChars (int low, int high)

Permet d'ajouter le caractère de code ch ou tous les caractères dont le code est compris entre low et high à
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.

public void quoteChar (int ch)

Permet d'ajouter le caractère de code ch à l'ensemble des caractères utilisés pour entourer une chaîne de
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.

public void parseNumbers ()

Permet de spécifier que les nombres doivent être lus en tant que tels, c'est-à-dire que l'appel de cette
méthode mémorise que les chiffres 0 à 9, les caractères '.' et '-' appartiennent à l'ensemble des
caractères utilisés pour construire un nombre. Quand un nombre est lu, le champ ttype est égal à
TT_NUMBER et le champ nval contient le nombre lu.

public void resetSyntax ()

Remet à zéro la syntaxe définie avec les méthodes précédentes.

public void eolIsSignificant (boolean flag)

Permet de spécifier que les caractères de fin de ligne sont significatifs ou non. Si flag est égal à true, 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.

public void slashStarComments (boolean flag)

Permet de spécifier si les commentaires Java /* ... */ sont significatifs ou non.

public void slashSlashComments (boolean flag)

Permet de spécifier si les commentaires Java de fin de ligne // ... sont significatifs ou non.

public void lowerCaseMode (boolean flag)

Permet de spécifier si tous les mots lus sont convertis en minuscules ou non.

public int nextToken () throws IOException

Lit dans le flux d'entrée la sous-chaîne suivante en respectant la syntaxe définie grâce aux méthodes
précédentes. Renvoie la valeur du champ ttype.

public void pushBack ()

Permet de revenir d'une sous-chaîne en arrière une fois.

public int lineno ()

Renvoie le numéro de ligne courant.

public String toString ()

Méthode de la classe Object, outrepassée pour qu'elle renvoie une description de la dernière sous-chaîne
lue.

Exemple

Applet PaperBoardClient .

Accès à un flux de données en écriture


De manière similaire à l'accès à un flux de données en lecture, l'accès à un flux de données en écriture
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 18

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 :

Classes Paramètres des constructeurs


Chemin d'accès à un fichier (classe String)
FileOutputStream Fichier décrit par une instance de la classe File
Descripteur de fichier (classe FileDescriptor)
Aucun pour créer un tableau d'octets par défaut
ByteArrayOutputStream
Taille minimum du tableau d'octets
Aucun pour créer un pipeline de sortie
PipedOutputStream
Entrée d'un pipeline (classe PipedInputStream)

Une fois que vous avez créé une instance d'une de ces classes, vous pouvez appeler les méthodes de la
classe OutputStream, puisque toutes ces classes en héritent.
L'accès aux trois autres types de sources de données restantes (les sorties standards, les sockets et un
fichier via une URL) s'effectue différemment :
Les sorties standard et d'erreur sont accessibles soit directement par les 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.

L'application suivante utilise les classes FileInputStream, SequenceInputStream, FileOutputStream et


BufferedOutputStream pour effectuer la concaténation de plusieurs fichiers dans un fichier destination.
Recopiez la dans un fichier ConcatenationFichiers.java , que vous compilez avec l'instruction javac
ConcatenationFichiers.java pour ensuite l'exécuter avec java ou Java Runner , grâce à l'instruction
java ConcatenationFichiers nomFichier1 ... nomFichierN fichierDest (par exemple java
ConcatenationFichiers ConcatenationFichiers.java NumerotationLigne.java resultat.java) :
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 19

import java.io.*;
import java.util.Vector;

public class ConcatenationFichiers


{
// Méthode lancée à l'appel de l'instruction :
// java ConcatenationFichiers nomFichier1 ... nomFichierN nomFichierDest
public static void main (String [ ] args)
{
try
{
Vector ensembleFichiers = new Vector ();
// Ajout à ensembleFichiers de tous les flux de données avec buffer
// correspondant à chaque fichier d'entrée passé en paramètres
// (sauf le dernier paramètre)
for (int i = 0; i < args.length - 1; i++)
ensembleFichiers.addElement (new BufferedInputStream (
new FileInputStream (args [i])));

// Création d'un ensemble de flux d'entrée


InputStream fluxEntree =
new SequenceInputStream (ensembleFichiers.elements ());

// Ouverture en écriture avec un buffer du fichier


// passé en dernier paramètre dans la ligne de commande
OutputStream fluxDestination =
new BufferedOutputStream (
new FileOutputStream (args [args.length -1]));

byte donnees [ ] = new byte [1000];


int nbreOctetsLus;
// Lecture puis écriture des données
while ((nbreOctetsLus = fluxEntree.read (donnees)) != -1)
fluxDestination.write (donnees, 0, nbreOctetsLus);

// Fermeture des flux


fluxDestination.close ();
fluxEntree.close ();
}
catch (IOException e)
{
// Exception déclenchée si un problème survient pendant l'accès aux fichiers
System.out.println (e);
}
}
}

La classe java.io.OutputStream

Cette classe abstract est la super classe de toutes les classes qui permettent d'écrire sur un flux de
données en lecture (voir aussi le paragraphe précédent).

Méthodes

public abstract void write (int b) throws IOException

Ecrit un octet sur le flux de données.

public void write (byte [ ] tab) throws IOException

Ecrit les octets du tableau tab sur le flux de données.

public void write (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Ecrit length octets du tableau tab sur le flux de données, à partir de l'indice offset.

public void flush () throws IOException

Ecrit physiquement sur le flux de données quand une zone tampon (buffer ) est utilisée.
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 20

public void close () throws IOException

Ferme l'accès au flux de données.

Exemple

Application ConcatenationFichiers et EchoServeur .

La classe java.io.FileOutputStream

Cette classe qui dérive de la classe OutputStream permet d'ouvrir un fichier et d'y écrire, sous forme de
flux de données.

Constructeurs

public FileOutputStream (String path)


throws SecurityException, FileNotFoundException
public FileOutputStream (File file)
throws SecurityException, FileNotFoundException

Ces constructeurs permettent de créer une instance de classe FileOutputStream à partir du chemin d'accès
à un fichier path ou d'une instance de classe File. Si le fichier n'existe pas il est créé.

public FileOutputStream (FileDescriptor fdObj) throws SecurityException

Construit une instance de classe FileInputStream à partir d'un descripteur de fichier fdObj.

Méthodes

public void write (int b) throws IOException


public void write (byte [ ] tab) throws IOException
public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture dans un fichier.

public final FileDescriptor getFD () throws IOException

Permet d'obtenir un descripteur de fichier.

protected void finalize () throws IOException

Méthode de la classe Object outrepassée pour que le fichier ouvert à la création d'une instance de la
classe FileOutputStream soit fermé avant que le Garbage Collector n'intervienne pour détruire cet objet.
Le moment où intervient le Garbage Collector n'étant généralement pas prévisible, il vaut mieux fermer
soi-même le fichier avec la méthode close (), pour éviter d'en bloquer l'accès à d'autres programmes.

Exemple

Application ConcatenationFichiers .

La classe java.io.PipedOutputStream

Cette classe qui dérive de la classe OutputStream permet d'accéder à un pipeline en écriture, sous forme
de flux de données.

Constructeurs

public PipedOutputStream (PipedInputStream snk) throws IOException


public PipedOutputStream ()

Méthodes

public void connect (PipedInputStream snk) throws IOException

Crée une connexion avec l'entrée du pipeline snk.


vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 21

public void write (int b) throws IOException


public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void close () throws IOException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture sur un pipeline.

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

protected byte [ ] buf


protected int count

Constructeurs

public ByteArrayOutputStream ()
public ByteArrayOutputStream (int size)

Ces constructeurs permettent de créer une instance de classe ByteArrayOutputStream, dont le tableau sera
au minimum de size octets (égal à 32 par défaut).

Méthodes

public synchronized void write (int b)


public synchronized void write (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'écriture dans un tableau
d'octets.

public int size ()

Renvoie le nombre courant d'octets écrits dans le tableau.

public synchronized void reset ()

Remet l'index courant dans le tableau à 0 sans allouer un nouveau tableau.

public synchronized byte [ ] toByteArray ()

Renvoie un tableau d'octets copie des octets écrits dans le tableau interne.

public String toString ()

Méthode de la classe Object, outrepassée pour qu'elle renvoie les octets écrits dans le tableau sous forme
de chaîne de caractères.

public String toString (int hibyte)

Renvoie les octets écrits dans le tableau sous forme de chaîne de caractères, dont chaque caractère
Unicode a hibyte pour partie haute.

public synchronized void writeTo (OutputStream out) throws IOException

Ecrit sur le flux de données out les octets écrits dans le tableau.

La classe java.io.FilterOutputStream

Cette classe qui dérive de la classe OutputStream est la super classe de toutes les classes utilisées pour
filtrer l'écriture sur un flux de données. Vous pouvez utiliser les classes BufferedOutputStream,
DataOutputStream ou PrintStream qui en dérivent mais aussi créer vos propres classes de filtre dérivant
de cette classe (voir aussi la classe ToUpperCaseInputStream comme exemple de filtre).
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 22

Champ

protected OutputStream out

Constructeur

public FilterOutputStream (OutputStream out)

Méthodes

public void write (int b) throws IOException


public void write (byte [ ] tab) throws IOException
public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public void flush () throws IOException
public void close () throws IOException

Méthodes de la classe OutputStream outrepassées pour qu'elles s'appliquent à l'accès à un flux de données
filtré en écriture.

La classe java.io.BufferedOutputStream

Cette classe qui dérive des classes FilterOutputStream et OutputStream permet d'écrire sur un flux de
données en utilisant une zone mémoire tampon (buffer ). Lors d'un appel à la méthodes write (), les
octets sont stockés dans le buffer. Le buffer est écrit finalement sur le flux de données une fois qu'il est
plein ou lors d'un appel à la méthode flush (). Ceci évite de faire une écriture physique sur le flux de
données à chaque appel de la méthode write () c'est pourquoi ce filtre est très souvent utilisé.

Champs

protected byte [ ] buf


protected int count

Constructeurs

public BufferedOutputStream (OutputStream out)


public BufferedOutputStream (OutputStream out, int size)

Ces constructeurs permettent de créer une instance de la classe BufferedOutputStream dont l'écriture sera
effectuée sur le flux de données out. size est la taille utilisée pour créer le buffer (égal à 512 par défaut).

Méthodes

public synchronized void write (int b) throws IOException


public synchronized void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public synchronized void flush () throws IOException

Méthodes de la classe FilterOutputStream outrepassées pour qu'elles s'appliquent à ce filtre.

Exemple

Application ConcatenationFichiers et PaperBoardServer .


Applet PaperBoardClient .

L'interface java.io.DataOutput

Cette interface implémentée par les classes DataOutputStream et RandomAccessFile déclare un ensemble
de méthodes qui permettent d'écrire dans un flux de données ou dans un fichier des données de différents
types : Soit un ensemble d'octets grâce aux méthodes write (), soit une donnée binaire représentant un
type de base Java, soit une chaîne de caractères.
Les données écrites grâce à ces méthodes sont lisibles grâce aux méthodes de l'interface DataInput,
quelque soit le système sur lequel les données ont été écrites. Le codage binaire des nombres étant
souvent différemment d'un système à l'autre, ceci permet de créer des flux de données portables.

Méthodes
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 23

public void write (int b) throws IOException

Cette méthode doit permettre d'écrire un octet.

public void write (byte [ ] tab) throws IOException

Cette méthode doit permettre d'écrire les octets du tableau tab.

public void write (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Cette méthode doit permettre d'écrire length octets du tableau tab, à partir de l'indice offset.

public void writeBoolean (boolean v) throws IOException


public void writeByte (int v) throws IOException
public void writeShort (int v) throws IOException
public void writeChar (int v) throws IOException
public void writeInt (int v) throws IOException
public void writeLong (long v) throws IOException
public void writeFloat (float v) throws IOException
public void writeDouble (double v) throws IOException

Ces méthodes doivent permettre d'écrire une valeur booléenne, un octet, un short, un caractère Unicode,
un int, un long, un float ou un double.

public void writeBytes (String s) throws IOException

Cette méthode doit permettre d'écrire la chaîne de caractères s. Elle écrit les caractères en les codant sur
8 bits et non sur 16 bits (Unicode).

public void writeChars (String s) throws IOException

Cette méthode doit permettre d'écrire la chaîne de caractères s, comme une suite de caractères Unicode.

public void writeUTF (String s) throws IOException

Cette méthode doit permettre d'écrire au format UTF la chaîne de caractères s.

La classe java.io.DataOutputStream

Cette classe qui dérive des classes FilterOutputStream et OutputStream implémente l'interface
DataOutput, ce qui permet d'écrire sur un flux de données autres chose que des octets.

Champ

protected int written

Constructeur

public DataOutputStream (OutputStream out)

Construit une instance de la classe DataOutputStream dont l'écriture sera effectuée sur le flux de données
out.

Méthodes

public synchronized void write (int b) throws IOException


public synchronized void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final void writeBoolean (boolean v) throws IOException
public final void writeByte (int v) throws IOException
public final void writeShort (int v) throws IOException
public final void writeChar (int v) throws IOException
public final void writeInt (int v) throws IOException
public final void writeLong (long v) throws IOException
public final void writeFloat (float v) throws IOException
public final void writeDouble (double v) throws IOException
public final void writeBytes (String s) throws IOException
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 24

public final void writeChars (String s) throws IOException


public final void writeUTF (String str) throws IOException

Implémentation des méthodes de l'interface DataOutput. La méthode write (byte [ ] tab) est
implémentée par la classe FilterOutputStream.

public void flush () throws IOException

Méthodes de la classe FilterOutputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public final int size ()

Renvoie le nombre d'octets écrits sur le flux de données depuis la création du filtre.

La classe java.io.PrintStream

Cette classe qui dérive des classes FilterOutputStream et OutputStream permet d'écrire au format texte les
types de base Java. Elle est très souvent utilisée via 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

public PrintStream (OutputStream out)


public PrintStream (OutputStream out, boolean autoflush)

Ces constructeurs permettent de créer une instance de la classe PrintStream dont l'écriture sera effectuée
sur le flux de données out. autoflush permet de préciser si un appel à la méthode flush () doit être
effectué à chaque retour à la ligne (égal à false par défaut).

Méthodes

public void write (int b)


public void write (byte [ ] tab, int offset, int length)
throws IndexOutOfBoundsException
public void flush ()
public void close ()

Méthodes de la classe FilterOutputStream outrepassées pour qu'elles s'appliquent à ce filtre.

public boolean checkError ()

Appelle la méthode flush () et renvoie true si une erreur est survenue pendant un appel aux autres
méthodes de cette classe.

public void print (Object obj)


public synchronized void print (String s)
public synchronized void print (char [ ] tab)
public void print (boolean b)
public void print (char c)
public void print (int i)
public void print (long l)
public void print (float f)
public void print (double d)

Ces méthodes permettent d'écrire au format texte un objet, une chaîne de caractère, un tableau de
caractères, une valeur booléenne, un caractère, un int, un long, un float ou un double, sur le flux de
données. Le texte écrit pour l'objet obj est la chaîne de caractère renvoyée par l'appel obj.toString ().

public synchronized void println (Object obj)


public synchronized void println (String s)
vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 25

public synchronized void println (char [ ] tab)


public synchronized void println (boolean b)
public synchronized void println (char c)
public synchronized void println (int i)
public synchronized void println (long l)
public synchronized void println (float f)
public synchronized void println (double d)

Ces méthodes effectuent les mêmes opérations que le groupe précédent, puis écrivent un retour à la ligne
sur le flux de données.

public void println ()

Ecrit un retour à la ligne sur le flux de données.

Cette classe est l'équivalent des fonctions printf () du C, mais ne comporte pas toute la richesse
des formats de données disponibles avec printf ().

Exemples

Les applications Banque , LectureFichier , NumerotationLigne , ConcatenationFichiers ,


TestExpression et PaperBoardServer utilisent le champ System.out et la méthode println ().
Applet PaperBoardClient .

Gestion de l'accès aléatoire aux fichiers


Java fournit la classe RandomAccessFile pour accéder aléatoirement au contenu d'un fichier en
lecture ou en écriture (voir aussi le début de ce chapitre).

La classe java.io.RandomAccessFile

Cette classe qui implémente les interfaces DataInput et DataOuput, permet d'accéder aléatoirement à un
fichier en mode lecture/écriture, ou uniquement en mode lecture.

Constructeurs

public RandomAccessFile (String path, String mode)


throws SecurityException, IOException, IllegalArgumentException
public RandomAccessFile (File file, String mode)
throws SecurityException, IOException, IllegalArgumentException

Ces constructeurs permettent d'ouvrir le fichier représenté par son chemin d'accès path ou par une
instance de la classe File. Si mode est égal à "rw" (read/write ) il est possible d'écrire ou de lire dans le
fichier ouvert et le fichier est créé s'il n'existe pas.
Si mode est égal à "r" (read ), le fichier est accessible uniquement en lecture et par conséquent
l'utilisation des méthodes d'écriture write...() de cette classe déclenchera une exception de classe
IOException.

Méthodes

public final FileDescriptor getFD () throws IOException

Permet d'obtenir un descripteur de fichier.

public long getFilePointer () throws IOException

Renvoie la position courante dans le fichier.

public void seek (long pos) throws IOException

Déplace la position courante en pos dans le fichier.

public long length () throws IOException

Renvoie la taille courante du fichier.


vendredi 13 octobre 2000 Du C/C++ à Java : La gestion des fichiers et des flux de données Page: 26

public void close () throws IOException

Ferme l'accès au fichier.

public abstract int read () throws IOException

Lit un octet dans le flux de données. Renvoie -1 si la fin est atteinte.

public int read (byte [ ] tab) throws IOException

Lit tab.length octets dans le tableau tab. Si la fin est atteinte pendant la lecture, tab est remplit avec les
octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est atteinte.

public int read (byte [ ] tab, int offset, int length)


throws IOException, IndexOutOfBoundsException

Lit length octets dans le tableau tab, en le remplissant à partir de l'indice offset. Si la fin est atteinte
pendant la lecture, tab est remplit avec les octets lus. Renvoie le nombre d'octets lus ou -1 si la fin est
atteinte.

public int skipBytes (int n) throws IOException

Méthode de l'interface DataInput implémentée pour qu'elle déplace de n octets la position courante dans
le fichier. n peut être positif ou négatif (pour revenir en arrière). Renvoie n.

public final void readFully (byte [ ] tab) throws IOException


public final void readFully (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final boolean readBoolean () throws IOException
public final byte readByte () throws IOException
public final int readUnsignedByte () throws IOException
public final short readShort () throws IOException
public final int readUnsignedShort () throws IOException
public final char readChar () throws IOException
public final int readInt () throws IOException
public final long readLong () throws IOException
public final float readFloat () throws IOException
public final double readDouble () throws IOException
public final String readLine () throws IOException
public final String readUTF () throws IOException

Implémentation des autres méthodes de l'interface DataInput.

public void write (int b) throws IOException


public void write (byte [ ] tab) throws IOException
public void write (byte [ ] tab, int offset, int length)
throws IOException, IndexOutOfBoundsException
public final void writeBoolean (boolean v) throws IOException
public final void writeByte (int v) throws IOException
public final void writeShort (int v) throws IOException
public final void writeChar (int v) throws IOException
public final void writeInt (int v) throws IOException
public final void writeLong (long v) throws IOException
public final void writeFloat (float v) throws IOException
public final void writeDouble (double v) throws IOException
public final void writeBytes (String s) throws IOException
public final void writeChars (String s) throws IOException
public final void writeUTF (String str) throws IOException

Implémentation des méthodes de l'interface DataOutput.


vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 1

Les accès au réseau

Accès via une URL


L'architecture client-serveur
Accès via les sockets
Accès via les datagrams

Ce chapitre décrit le package java.net de Java 1.0 qui rassemble les classes permettant de gérer les
accès réseau via une URL ou par les sockets.

Accès via une URL


Les accès au réseau peuvent se faire à plusieurs niveaux en Java.
Le niveau le plus simple permet d'accéder à un fichier sur le réseau grâce à son URL (Uniformed
Resource Locator ) : Contrairement au chemin d'accès classique à un fichier dont la notation varie
d'un système à l'autre (c:\fichier.txt, /usr/unCompte/fichier.txt), une URL permet de désigner
un fichier de manière uniforme quelque que soit le système qui héberge ce fichier.
Même si vous ne la connaissiez pas sous ce nom, l'adresse d'un site Internet est une URL :
http://www.javasoft.com/index.html, http://www.eteks.com/coursjava/net10.html#AccesURL sont
des exemples d'URL.
De quoi est composée une URL ?

Une chaîne représentant le protocole à utiliser pour accéder au fichier (http, ftp, file,
mailto,...), suivi du symbole :.
Le nom de l'hôte qui fournit le service recherché (www.yahoo.fr). Cet hôte correspond à une
machine.
Eventuellement un numéro de port sur la machine de l'hôte précédé du symbole :.
Le chemin d'accès au fichier recherché sur l'hôte. Les répertoires éventuels de ce chemin
d'accès sont séparés par le symbole /.
Pour les fichiers HTML, le nom du fichier peut être suivi d'une référence à une ancre à
l'intérieur du fichier précédé du symbole #.

Une URL peut représenter un fichier mais aussi de manière plus générale une ressource. Par
exemple, une ressource peut être un programme renvoyant une image ou le résultat d'un accès à une
base de données. La plupart des programmes auxquels on accède sur un site Internet via une URL
utilise le modèle CGI (Common Gateway Interface ). Ce 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.

public URL (String spec) throws MalformedURLException

Construit une instance de classe URL à partir de la chaîne de caractères spec représentant une URL.

public URL (URL context, String spec) throws MalformedURLException

Construit une instance de classe URL à partir d'une URL existante context et de la chaîne de
caractères spec. Si spec décrit entièrement une URL (avec protocole, hôte, fichier,...) , l'URL
context est ignorée et l'objet créé correspond à l'URL spec. Sinon, context est utilisé comme base
complétée par les informations fournies dans spec, pour construire le nouvel objet de classe URL
(spec peut par exemple désigné un fichier dans un sous répertoire différent).

Méthodes

public String getProtocol ()


public String getHost ()
public int getPort ()
public String getFile ()
public String getRef ()

Ces méthodes permettent d'interroger le protocole, l'hôte, le port, le chemin d'accès au fichier et la
référence mémorisée dans une instance de la classe URL. Si le port n'a pas été précisé en paramètre
du constructeur, la valeur -1 est renvoyée.

public boolean sameFile (URL other)

Renvoie true si other désigne la même URL que l'objet sur lequel est invoquée cette méthode. Les
références mémorisées par les objets ne sont pas prises en compte pour la comparaison.

public String toExternalForm ()

Renvoie une chaîne de caractères correspondant à l'URL mémorisée par un objet de classe URL.

public final InputStream openStream () throws IOException

Ouvre en lecture la connexion avec une URL, et renvoie une instance de la classe InputStream pour
accéder aux données sous forme de flux de données.

public final Object getContent () throws IOException

Renvoie un objet correspondant au contenu du fichier représenté par un objet de classe URL. Par
exemple, si le fichier est une image, cette méthode renverra une instance d'une classe dérivée de la
classe Image.

public URLConnection openConnection () throws IOException

Ouvre une connexion avec une URL, et renvoie une instance de la classe URLConnection qui permet
d'obtenir toute sorte de renseignements (dates, en-tête,...) sur le fichier correspondant à l'URL.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 3

public static synchronized void setURLStreamHandlerFactory


(URLStreamHandlerFactory fac)
throws SecurityException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de


création de flux de données pour chacun des protocoles reconnus. Cette méthode static ne peut
être appelée qu'une seule fois. Par défaut, la Machine Virtuelle Java reconnaît un certain nombre de
protocoles (voir l'exemple qui suit).

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe URL à un objet ou renvoyer une chaîne de caractères correspondant à l'URL.

L'application suivante permet de tester si la Machine Virtuelle Java accepte ou non les 4 protocoles
les plus communément utilisés : http, ftp, file et mailto. Recopiez-la dans un fichier
TestProtocole.java , que vous compilez avec l'instruction javac TestProtocole.java pour ensuite
l'exécuter avec java ou Java Runner , grâce à l'instruction java TestProtocole :

import java.net.*;

public class TestProtocole


{
public static void main (String [] args)
{
// Tableau avec 4 URL à tester
String urlTest [] = {"http://wwwusers.imaginet.fr/~puybaret/learnjava/index.html",
"ftp://wwwusers.imaginet.fr/~puybaret/learnjava/index.html",
"file://C//java/bin/java.exe",
"mailto:puybaret@imaginet.fr"};

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


try
{
// Création d'une URL
URL url = new URL (urlTest [i]);
System.out.println ("URL : " + url + " correctement form\u00e9e\n"
+ " et protocole " + url.getProtocol () + " reconnu.");
}
catch (MalformedURLException e)
{
// URL incorrect ou protocole inconnu
System.out.println ("URL : " + urlTest [i] + " incorrect\n"
+ " ou protocole non reconnu\n"
+ "(Exception " + e + ").");
}
}
}

Autre exemple

Applets HelloFromNet et BoutonsNavigation .

La classe java.net.URLConnection

Cette classe abstract gère la connexion avec une URL. Une instance d'une classe dérivant de cette
classe peut être obtenue grâce à la méthode openConnection () de la classe URL.
Les méthodes de cette classe permettent de créer un flux de données pour lire et/ou d'écrire dans le
fichier désigné par une URL, mais aussi d'obtenir toute sorte d'information sur ce fichier (sa date de
dernière modification, sa taille, son type,...).

Champs

protected URL url


protected boolean doInput
protected boolean doOutput
protected boolean allowUserInteraction
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 4

protected boolean useCaches


protected long ifModifiedSince
protected boolean connected

Ces champs protected sont utilisés par les classes dérivées de la classe URLConnection.

Constructeur

protected URLConnection (URL url)

Méthodes

public URL getURL ()

Renvoie l'URL avec laquelle la connexion est faite.

public InputStream getInputStream () throws IOException


public OutputStream getOutputStream () throws IOException

Ces méthodes renvoient un flux de données en lecture ou en écriture à partir d'une connexion à une
URL. Une exception de classe UnknownServiceException (dérivant de la classe IOException) est
déclenchée si le protocole interdit l'accès en lecture ou en écriture à cette URL.

public int getContentLength ()

Renvoie la taille du fichier avec lequel la connexion est faite ou 0 si cette taille n'est pas connue.

public String getContentType ()


public String getContentEncoding ()

Renvoie le type du contenu et son codage ou null si ces renseignements ne sont pas connus

public Object getContent () throws IOException

Renvoie un objet correspondant au contenu du fichier.

public long getDate ()


public long getLastModified ()
public long getExpiration ()

Renvoie la date à laquelle le fichier a été envoyé, sa date de dernière modification et sa date
d'expiration ou 0 si ces renseignements ne sont pas connus.

public String getHeaderField (String name)


public int getHeaderFieldInt (String name, int default)
public long getHeaderFieldDate (String name, long default)
public String getHeaderFieldKey (int n)
public String getHeaderField (int n)

Ces méthodes renvoient les renseignements correspondant aux clés contenues dans l'entête d'un
fichier

public boolean getDoInput ()


public boolean getDoOutput ()
public boolean getUseCaches ()
public boolean getAllowUserInteraction ()

Ces méthodes renvoient true, s'il est possible de lire ou d'écrire sur une connexion, si la connexion
utilise le cache ou si elle autorise d'interagir avec l'utilisateur (pour lui demander par exemple un
mot de passe). Par défaut, une connexion est ouverte en lecture.

public long getIfModifiedSince ()

Renvoie la durée de validité du fichier sur la connexion est faite.

public String getRequestProperty (String key)

Renvoie la valeur associée au mot-clé key par lequel une requête est reconnue.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 5

public abstract void connect () throws IOException

Les classes dérivées de la classe URLConnection doivent implémenter cette méthode pour réaliser la
connexion avec une URL.

public void setDoInput (boolean doinput)


public void setDoOutput (boolean dooutput)
public void setUseCaches (boolean usecaches)
public void setAllowUserInteraction (boolean allowuserinteraction)
public void setIfModifiedSince (long ifmodifiedsince)
public void setRequestProperty (String key, String value)

Ces méthodes permettent d'autoriser l'accès en lecture ou en écriture à un fichier, l'utilisation du


cache,... Elles ne peuvent pas être appelées quand la connexion est déjà ouverte.

public boolean getDefaultUseCaches ()


public void setDefaultUseCaches (boolean defaultusecaches)
public static boolean getDefaultAllowUserInteraction ()
public static void setDefaultAllowUserInteraction
(boolean defaultallowuserinteraction)
public static String getDefaultRequestProperty (String key)
public static void setDefaultRequestProperty (String key, String value)
protected static String guessContentTypeFromName (String fname)
protected static String guessContentTypeFromStream (InputStream is)
throws IOException

public static synchronized void setContentHandlerFactory


(ContentHandlerFactory fac) throws SecurityException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de


création d'objets représentés dans un fichier suivant leur type mime, différent de celui fourni par
défaut. Cette méthode static ne peut être appelée qu'une seule fois.

public String toString ()

Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant la
connexion.

Voici un exemple simple d'applet qui lit le texte contenu dans le fichier accessible à l'URL
http://www.eteks.com/classes/hello.txt pour l'afficher à l'écran :

et le programme Java correspondant (à copier dans un fichier dénommé HelloFromNet.java et


invoqué à partir d'un fichier HTML) :

import java.applet.Applet;
import java.awt.Graphics;
import java.net.*;
import java.io.*;

public class HelloFromNet extends Applet


{
String texteLu;

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


public void init ()
{
try
{
// Création de l'URL http://www.eteks.com/classes/hello.txt
URL urlFichierHello = new URL ("http",
"www.eteks.com",
"/classes/hello.txt");
// Ouverture d'une connexion et récupération d'un flux d'entrée
URLConnection connexion = urlFichierHello.openConnection ();
InputStream fluxFichier = connexion.getInputStream ();

// Lecture du contenu du flux d'entrée


vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 6

byte contenuFichier [ ] = new byte [connexion.getContentLength ()];


int octetsLus = fluxFichier.read (contenuFichier);

texteLu = new String (contenuFichier, 0, 0, octetsLus);

// Fermeture de la connexion
fluxFichier.close ();
}
catch (Exception e)
{
texteLu = "Probleme...";
}
}

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


public void paint (Graphics gc)
{
if (texteLu != null)
// Affichage du texte lu
gc.drawString(texteLu, 10, 20);
}
}

La classe java.net.URLEncoder

L'unique méthode encode () de cette classe est utile si vous voulez créer un formulaire en Java et
en envoyer les résultats au serveur pour être traité par exemple par un programme CGI. Cette
méthode transforme une chaîne de caractères au format x-www-form-urlencoded, c'est-à-dire que tous
les espaces sont remplacés par le signe + et tous les caractères différents des lettres de l'alphabet,
des chiffres et du caractère _ par leur code hexadécimal précédé du symbole %.

Méthode

public static String encode (String s)

Vous n'aurez normalement pas à utiliser les interfaces et les classes qui suivent, car la Machine
Virtuelle Java fournit et gère automatiquement un certain nombre de protocoles et la création d'objet
en fonction du contenu d'un fichier. Ces classes sont utilisées indirectement par les méthodes de la
classes URLConnection.

L'interface java.net.URLStreamHandlerFactory

Cette interface est implémentée par les classes désirant gérer l'accès sous forme de flux de données
à un ou plusieurs protocoles. Elle est utilisée comme paramètre de la méthode
setURLStreamHandlerFactory () de la classe URL, pour modifier le gestionnaire par défaut.

Méthode

public URLStreamHandler createURLStreamHandler (String protocol)

Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract URLStreamHandler
ou null si le protocole protocol est refusé ou n'est pas reconnu par ce gestionnaire. L'objet renvoyé
doit pouvoir créer un flux de données à partir du protocole protocol et d'une URL.

La classe java.net.URLStreamHandler

Cette classe abstract est la super classe de toutes les classes qui gère la création d'un flux de
données à partir d'un protocole donné et d'une URL.
La Machine Virtuelle Java fournit un ensemble de classes pour un certain nombre de protocoles.
Ces classes sont utilisées par défaut si la méthode setURLStreamHandlerFactory () de la classe URL
n'a pas été appelée pour changer de gestionnaire de création de flux de données.

Constructeur
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 7

public URLStreamHandler ()

Méthodes

protected abstract URLConnection openConnection (URL url) throws IOException

Cette méthode doit ouvrir un connexion avec l'URL url et renvoyer une instance d'une classe
dérivant de la classe abstract URLConnection pour permettre d'accéder à l'URL sous forme de flux
de données.

protected void parseURL (URL url, String spec, int start, int limit)

Analyse la chaîne de caractères spec représentant une URL entre les caractères start et limit
(exclu), pour compléter l'instance url de la classe URL. Cette méthode analyse la chaîne spec en
respectant la syntaxe du protocole http, et donc s'attend à ce que start désigne le caractère suivant
le symbole : du protocole, et limit le caractère # précédant la référence à une ancre.
Outrepassez cette méthode si vous voulez qu'elle analyse différemment la chaîne spec.

protected String toExternalForm (URL url)

Renvoie une chaîne de caractères correspondant à l'URL mémorisée par url.

protected void setURL (URL url, String protocol, String host,


int port, String file, String ref)

Permet de modifier le protocole protocol, l'hôte host, le port port, le chemin d'accès au fichier file
et la référence ref mémorisés par l'objet url.

L'interface java.net.ContentHandlerFactory

Cette interface est implémentée par les classes désirant gérer le contenu d'un fichier suivant leur
type mime (par exemple image/gif ou text/plain). Elle est utilisée comme paramètre de la
méthode setContentHandlerFactory () de la classe URLConnection, pour modifier le gestionnaire par
défaut.

Méthode

public ContentHandler createContentHandler (String mimetype)

Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract ContentHandler.
Cet objet doit pouvoir créer un objet correspondant au contenu d'un fichier de type mime mimetype.

La classe java.net.ContentHandler

Cette classe abstract est la super classe de toutes les classes qui gère la création d'un objet
correspondant au contenu d'un fichier d'un type mime donné.
La Machine Virtuelle Java fournit un ensemble de classes pour certains types mime. Ces classes
sont utilisées par défaut si la méthode setContentHandlerFactory () de la classe URLConnection n'a
pas été appelée pour changer de gestionnaire de création d'objet.

Constructeur

public ContentHandler ()

Méthode

public abstract Object getContent (URLConnection urlc) throws IOException

Cette méthode doit renvoyer un objet correspondant au contenu du fichier avec lequel la connexion
urlc a été établie.

L'architecture client-serveur
Grâce à une URL, l'accès à un fichier ou une ressource est géré de manière simple : que le fichier
soit sur un disque local ou sur Internet, il vous suffit de respecter les règles de construction d'une
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 8

URL et le tour est joué.


Mais Java permet aussi de gérer des accès réseau plus complexe, pour réaliser par exemple un
dialogue entre deux ordinateurs d'un réseau. Ce dialogue est basé sur l'architecture client-serveur,
dont voici un rappel des grands principes.

Principe

Comme la programmation orientée objet, l'architecture client-serveur est une technologie


informatique très utilisée actuellement.
Elle est surtout connue dans le monde des SGBD (Système de Gestion de Base de Données ), mais
elle s'applique aussi à d'autres domaines comme Internet, les terminaux X utilisés sous UNIX (que
l'on appelle aussi serveurs X).
Son principe est assez simple mais ce sont des exemples qui vous permettront de mieux comprendre
comment cette architecture s'applique concrètement.

Comme son nom l'indique, le client-serveur implique qu'il y ait au moins deux acteurs en jeu, un
client et un serveur , qui communiquent entre eux, souvent à travers un réseau.
Un serveur est un programme ou par extension une machine programmée pour rendre toujours le
même service , suite à une requête qui lui est adressée.
Un client est un programme ou par extension une machine qui demande à un serveur un service ,
en lui adressant une requête .
Gardez-bien toujours en tête ce mot service , car c'est souvent en pensant à qui rend service à
l'autre que vous arriverez à déterminer qui est le client du serveur dans une architecture
client-serveur.

Exemples d'utilisation

figure 12. Architecture client-serveur sur Internet

Internet que vous utilisez en ce moment, est un bon exemple d'architecture client-serveur : Les sites
que vous consultez sont souvent appelés aussi des serveurs, car les programmes qui les gèrent
rendent toujours le même service au client qu'est votre navigateur.
Quel service ? Le programme d'un serveur est en attente que vous lui demandiez une page du site
que vous voulez visualiser. Quand votre requête lui parvient, il vous renvoie cette page. Votre
navigateur de son côté est un client du serveur car il lui demande une page qu'il visualise une fois
téléchargée.

Le client-serveur est souvent utilisé pour accéder à de grandes bases de données utilisées par
plusieurs utilisateurs. La base de données est stockée sur une machine utilisée comme serveur. Le
programme sur ce serveur a pour mission de répondre à des requêtes SQL que lui adressent des
clients. Les requêtes SQL que le serveur reçoit lui permettent de modifier ou d'interroger la base de
données qu'il gère, et de renvoyer au client la réponse attendue. Le client est généralement une
machine de type PC dont le programme est utilisé pour mettre en page les réponses reçues du
serveur.

Le principale avantage de cette architecture est qu'elle permet de séparer complètement le serveur
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 9

qui généralement gère le stockage de données, du client qui cherche à y accéder.


Ainsi, dans le cas d'Internet, il est possible d'avoir des navigateurs qui tournent sous différents
systèmes et même des logiciels de navigation différents sur un même système. Tous ces navigateurs
sont des clients du serveur d'un site Internet auquel ils peuvent accéder simultanément.
A l'opposé, le serveur d'un site Internet peut fonctionner sur n'importe quel système, que vous
n'avez d'ailleurs pas à connaître. Ce qu'il compte, c'est qu'il réponde correctement à vos requêtes,
quand vous voulez voir une page hébergée sur ce site.

La clé de voûte qui permet à ce système de fonctionner sans que le client et le serveur n'aient à
savoir comment ils fonctionnent l'un l'autre est l'utilisation d'un protocole commun. Ce protocole
établit la norme que doit respecter le client et le serveur pour communiquer entre eux : comment
établir une communication, comment un client doit envoyer une requête à un serveur et comment
un serveur doit répondre à la requête du client.

Protocoles

Deux types de protocoles peuvent être utilisés sur Internet :

TCP (Transport Control Protocol ) : Sans que vous ayez à le savoir, ce protocole est utilisé
par d'autres protocoles de plus haut niveau comme HTTP (HyperText Transfer Protocol ) et
FTP (File Transfer Protocol ).
Ce protocole permet d'établir une connexion entre deux machines (un client et un serveur) :
Une fois la connexion établie, les programmes client et serveur peuvent s'échanger des
données, et ce protocole garantit que les données émises d'une machine arriveront sur l'autre,
et dans l'ordre où elles ont été émises. Ainsi suite à une requête, le transfert d'un fichier peut
être effectué en étant assuré de son intégrité à l'arrivée.
Les classes URL et URLConnection utilisent TCP implicitement et permettent à un client
d'accéder ne manière simple à un fichier ou une ressource d'un serveur Internet. Ces classes
sont généralement suffisantes pour les communications utilisant les protocoles comme HTTP
ou FTP. Mais Java permet aussi de programmer des communications quelconques entre un
client et un serveur en utilisant TCP, grâce aux classes Socket et ServerSocket.
UDP (User Datagram Protocol ) : A l'opposé de TCP, ce protocole est utilisé pour
transmettre des données entre deux machines sans garantir que ces données arriveront à
destination et dans l'ordre où elles ont été émises. Ces données sont envoyées par paquets
appelés aussi des datagrammes.
Ce protocole évite la contrainte d'avoir à établir et maintenir une connexion entre les deux
machines.
Il peut être utile par exemple pour un serveur envoyant les tics d'une horloge sur requête d'un
client. Si un datagramme contenant une heure donnée ne parvient pas au client ou son
acheminement est retardé, il est inutile de s'en inquiéter car au tic suivant le serveur renverra
un nouveau datagramme contenant l'heure actualisée. Si ces datagrammes arrivent dans le
désordre, il suffira au client de ne pas tenir compte des datagrammes contenant une heure
obsolète.
Les classes DatagramSocket et DatagramPacket permet d'utiliser UDP pour programmer ce type
de communication entre un client et un serveur.

Port

Les serveurs des sites auxquels vous vous connectez sur Internet sont accessibles grâce à un
mnémonique qui est le nom de la machine hôte (par exemple java.sun.com, www.yahoo.fr). Chaque
mnémonique est associé à un identifiant numérique représentant la machine hôte qui héberge un
serveur. Cet identifiant est un nombre 32 bits appelé adresse IP que l'on note généralement sous la
forme d'une suite de 4 nombres séparés par des points (par exemple 194.51.83.2) : certains
navigateurs affichent dans la barre d'état l'adresse IP de l'hôte d'un site, quand vous essayez de vous
connectez à ce site.
Chaque machine reliée à Internet possède une adresse IP différente qui permet de la désigner de
manière unique sur le réseau Internet.

Tous les systèmes d'exploitation permettent de faire fonctionner plusieurs programmes en même
temps sur une machine. Si plusieurs serveurs sont hébergés sur une même machine, ils vont devoir
partager l'accès physique au réseau sur cette machine. Un deuxième niveau d'identification est donc
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é.

Accès via les sockets


Chaque extrémité d'une connexion entre un client et un serveur est appelée un socket. Un socket est
une représentation logique du point de branchement d'un programme au réseau.

Pour programmer une architecture client serveur utilisant le protocole TCP (Transport Control
Protocol ), il vous faut réaliser, grâce aux classes ServerSocket et Socket qui suivent, un
programme faisant office de serveur sur une machine hôte et un autre utilisé comme client sur une
autre machine.
Pour vous permettre de tester les programmes serveur et client sur la même machine, le client peut
essayer de se connecter à la machine localhost qui représente la machine locale.

figure 13. Utilisation des sockets en Java

Comme le montre la figure précédente, le programme serveur doit créer une instance de la classe
ServerSocket en précisant le numéro de port sur lequel il attend les demandes de connexion des
programmes clients. Le serveur appelle ensuite la méthode accept () de cette classe pour se mettre
en attente de demande de connexion.
Une fois qu'un client s'est connecté, une instance de la classe Socket est renvoyée. Le serveur peut
alors grâce au méthodes de cette classe obtenir des flux de données en lecture et en écriture pour
communiquer avec le programme client.

Le programme client de son côté se connecte à un programme serveur en créant une instance de la
classe Socket, en précisant l'adresse Internet de la machine hôte et le numéro de port sur lequel le
serveur attend les demandes de connexions.
Comme pour le programme serveur, une fois qu'il a obtenu une instance 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

public static synchronized InetAddress getByName (String host)


vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 11

throws UnknownHostException

Renvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine host.

public static synchronized InetAddress [ ] getAllByName (String host)


throws UnknownHostException

Renvoie toutes les adresses Internet de la machine host.

public static InetAddress getLocalHost () throws UnknownHostException

Renvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine locale.
Très pratique pour tester sur une même machine les programmes client et serveur. Equivalent à
getByName (null) ou getByName ("localhost").

public String getHostName ()

Renvoie le nom de la machine hôte.

public byte [ ] getAddress ()

Renvoie l'adresse IP mémorisée par une instance de la classe InetAddress. Le tableau renvoyé
contient les 4 nombres d'une adresse IP qui sont séparées par des points (par exemple 194.51.83.2).

public int hashCode ()


public boolean equals (Object obj)
public String toString ()

Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un
objet de classe InetAddress à un objet ou renvoyer une chaîne de caractères décrivant une adresse
Internet.

Exemples

Applets EchoClient et PaperBoardClient .

La classe java.net.Socket

Cette classe final permet de manipuler un socket. Vous pouvez en obtenir une instance de deux
manières différentes, suivant que vous réalisez le programme client ou le serveur :

Quand un programme client essaye de se connecter à un serveur, il doit créer une instance de
la classe Socket grâce à l'un des constructeurs de cette classe (par exemple new Socket
("www.eteks.com", 26197)). Chacun de ses constructeurs prend comme paramètres l'adresse
Internet de la machine hôte du serveur et le numéro de port sur lequel le serveur est en attente
de connexion avec un client.
Quand un programme serveur en attente sur la méthode accept () reçoit une demande de
connexion avec un programme client, cette connexion est créée puis accept () renvoie une
instance de la classe Socket.

Une fois obtenue une instance de la classe Socket, il est possible d'obtenir côté client comme côté
serveur un flux de données d'entrée et un flux de données de sortie, grâce aux méthodes
getInputStream () et getOutputStream (). Ces méthodes renvoient des instances de classes dérivant
des classes InputStream et OutputStream dont les méthodes permettent de de lire et d'envoyer des
données entre le client et le serveur.

Constructeurs

public Socket (String host, int port)


throws UnknownHostException, IOException
public Socket (String host, int port, boolean stream)
throws IOException

Ces constructeurs permettent de créer une instance de la classe Socket et d'obtenir pour un
programme client une connexion avec le programme serveur de la machine host en attente sur le
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éé.

public Socket (InetAddress address, int port)


throws IOException
public Socket (InetAddress address, int port, boolean stream)
throws IOException

Ces constructeurs fonctionnent comme les deux premiers, mais prennent comme premier paramètre
une instance de la classe InetAddress.

Méthodes

public InputStream getInputStream () throws IOException


public OutputStream getOutputStream () throws IOException

Ces méthodes permettent d'obtenir un flux de données en lecture ou un flux de données en écriture,
pour communiquer avec le socket distant.

public synchronized void close () throws IOException

Ferme la connexion avec le socket distant.

public InetAddress getInetAddress ()


public int getPort ()

Ces méthodes renvoient l'adresse Internet et le port du socket distant auquel le socket est connecté.

public int getLocalPort ()

Renvoie le port local sur lequel le socket est connecté.

public static synchronized void setSocketImplFactory (SocketImplFactory fac)


throws IOException, SecurityException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de


création de sockets, différent de celui fourni par défaut. Cette méthode static ne peut être appelée
qu'une seule fois.

public String toString ()

Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le socket.

Exemples

Applications EchoServer et PaperBoardServer .


Applets EchoClient et PaperBoardClient .

La classe java.net.ServerSocket

Cette classe final est utilisée par un programme serveur pour gérer l'attente de demande de
connexion d'un programme client sur un port de la machine hôte du serveur.

Constructeurs

public ServerSocket (int port) throws IOException


public ServerSocket (int port, int count) throws IOException

Ces constructeurs permettent de créer un socket de serveur en attente sur le port port. Pour autoriser
la connexion anonyme d'un client, utilisez un port est égal à 0. count (égal à 50 par défaut) permet
de spécifier le nombre maximum de demande de connexions en attente que le serveur termine
d'exécuter la méthode accept ().

Méthodes

public Socket accept () throws IOException


vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 13

Attend la demande de connexion d'un programme client, et renvoie un socket une fois la connexion
établie.

public void close () throws IOException

Ferme le socket du serveur.

public InetAddress getInetAddress ()


public int getLocalPort ()

Ces méthodes renvoient l'adresse Internet et le port local sur lequel le socket du serveur est
connecté.

public static synchronized void setSocketFactory


(SocketImplFactory fac) throws IOException

Si le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de


création de socket de serveur, différent de celui fourni par défaut. Cette méthode static ne peut être
appelée qu'une seule fois.

public String toString ()

Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le
serveur.

Exemples

Applications EchoServer et PaperBoardServer .

Le client serveur d'écho

Ce premier exemple de client serveur se décompose en deux programmes : une application pour le
serveur et une applet pour le client. Vous pouvez tester cet exemple sur votre ordinateur car l'applet
se connecte sur le serveur de la machine locale, où vous pouvez lancer l'application serveur.
Cet exemple réalise une opération très simple d'écho : le serveur ne fait que renvoyer en l'état les
données que le client lui envoie, jusqu'à ce qu'il rencontre un retour chariot.
Il faut lancer l'application du serveur avant de lancer l'applet du client.

Voici le programme du serveur. Recopiez-le dans un fichier EchoServer.java , que vous compilez
avec l'instruction javac EchoServer.java pour ensuite l'exécuter avec java ou Java Runner , grâce
à l'instruction java EchoServer : :

import java.net.*;
import java.io.*;

public class EchoServer


{
public static void main (String args [])
{
try
{
// Création d'un serveur sur le port 13267
ServerSocket server = new ServerSocket (13267);
// Attente de la connexion d'un client
Socket socket = server.accept ();

System.out.println ("Connexion sur le socket : " + socket);

// Récupération des flux d'entrés et de sortie


InputStream fluxEntree = socket.getInputStream ();
OutputStream fluxSortie = socket.getOutputStream ();

char caractereLu;
// Renvoie au client de chaque caractère lu
// jusqu'à ce que ce caractère soit un retour chariot
while ((caractereLu = (char)fluxEntree.read ()) != '\n')
fluxSortie.write (caractereLu);
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.*;

public class EchoClient extends Applet


{
String texteLu = "";
InputStream fluxEntree;
OutputStream fluxSortie;

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


public void init ()
{
try
{
// Récupération de l'adresse de l'hôte local
InetAddress adresse = InetAddress.getLocalHost ();
// Ouverture d'une connexion sur le port 13267 de cet hôte
Socket socket = new Socket (adresse, 13267);

// Récupération des flux d'entrés et de sortie


fluxEntree = socket.getInputStream ();
fluxSortie = socket.getOutputStream ();
}
catch (IOException e)
{
texteLu = "Probleme de connexion";
}
}

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


public void paint (Graphics gc)
{
gc.drawString (texteLu, 10, 20);
}

// Méthode appelée le système quand une touche du clavier est enfoncée


public boolean keyDown (Event evt, int key)
{
if (fluxSortie != null)
try
{
// Envoie au serveur du caractère lu puis relecture
fluxSortie.write(key);
char caractereLu = (char)fluxEntree.read ();

// Ajout du caractère et redessin de l'applet


texteLu += caractereLu;
repaint();
}
catch (IOException e)
{ }
return true;
}
}

Voici un exemple de code HTML qui vous permettre d'appeler cette applet (à mettre par exemple
dans un fichier EchoClient.html et à exécuter avec appletviewer ou Applet Runner , grâce à
l'instruction appletviewer EchoClient.html) :
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 15

<applet code="EchoClient" width=200 height=30> </applet>

Le paper board Internet

Ce deuxième exemple de client serveur beaucoup plus élaboré se décompose toujours en deux
programmes : une application pour le serveur et une applet pour le client.
Chaque utilisateur visualisant cette applet peut partager un dessin qu'il dessine avec la souris, avec
toutes les autres personnes connectées au serveur. Pour saisir un élément de dessin, il suffit de
déplacer la souris en maintenant son bouton enfoncé.
Pour permettre plusieurs connexions simultanées de clients au serveur, ce dernier lance un thread
gérant chaque connexion. Chacun de ces threads a pour mission d'attendre les requêtes de chacun
des clients.
Quatre types différents de requêtes sont gérées :

AJOUT : Le client envoie cette requête pour indiquer au serveur que l'utilisateur de l'applet
client a saisi une nouvelle suite de points (polyline ), avec la souris. Cette requête est suivie
d'une liste de valeurs entières représentant les couples x y de chacun des points de la polyline.
Le serveur ajoute cette liste à l'ensemble des polylines qu'il a déjà reçues.
LISTE : Le client envoie cette requête pour demander au serveur l'ensemble des polylines
actuellement enregistrées sur le serveur. Le serveur lui renvoie chacune des listes de points
séparée par des tabulations (caractère '\t'). Envoyée à intervalle régulier, cette requête
permet à chacun des clients du serveur de mettre à jour sa zone d'affichage en affichant toutes
les polylines qu'ont saisies l'ensemble des utilisateurs de l'applet client connectée au serveur.
CONNEXION : Le client envoie cette requête pour demander le nombre actuelle de clients
connectés au serveur. Ce dernier renvoie ce nombre.
FIN : Le client envoie cette requête pour indiquer au client son intention de se déconnecter du
serveur.

Pour tester cette applet, il faut une connexion Internet active. Cette version du manuel étant prévue
pour fonctionner off-line, vous pouvez éventuellement la tester sur
http://www.eteks.com/coursjava/net10.html#PaperBoard

Voici le programme du serveur qui tourne sur www.eteks.com (A mettre dans un fichier
PaperBoardServer.java , et exécuté avec java ou Java Runner ) :

import java.net.*;
import java.io.*;
import java.util.*;

public class PaperBoardServer implements Runnable


{
public static int port = 26197;
public static String requeteFin = "FIN";
public static String requeteListe = "LISTE";
public static String requeteAjout = "AJOUT";
public static String requeteConnexion = "CONNEXION";

private static ServerSocket serveur;

public static void main (String args [])


{
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 16

if (lancerServeur () == null)
System.exit (-1);
}

// lancerServeur () peut être appelée par une servlet


// pour éviter de lancer une Machine Virtuelle supplémentaire
// sur le serveur où est hébergé le site
public static ServerSocket lancerServeur ()
{
if (serveur == null)
try
{
// Création d'un serveur sur le port 26197
serveur = new ServerSocket (port);
// Lancement d'un thread d'attente de connexion
new Thread (new PaperBoardServer ()).start ();
}
catch (IOException e)
{
System.out.println (e);
return null;
}

return serveur;
}

public void run ()


{
try
{
// Boucle sans fin d'attente de connexion
while (true)
{
// Attente de la connexion d'un client
Socket socket = serveur.accept ();
// Démarrage d'un nouveau thread pour gérer la connexion
new ThreadSocket (socket).start ();
}
}
catch (IOException e)
{
System.out.println (e);
}
finally
{
try
{
// Fermeture du serveur
serveur.close ();
serveur = null;
}
catch (IOException e)
{
System.out.println (e);
}
}
}
}

class ThreadSocket extends Thread


{
static private Vector listePolylines = new Vector (100);
static private int connexions;
private Socket socket;

public ThreadSocket (Socket socket)


{
this.socket = socket;
}

public void run ()


{
try
{
// Récupération des flux d'entrés et de sortie
DataInputStream fluxEntree = new DataInputStream (
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 17

new BufferedInputStream (socket.getInputStream ()));


PrintStream fluxSortie = new PrintStream (
new BufferedOutputStream (socket.getOutputStream ()), true);

// Augmentation du nombre de connexions au serveur


connexions++;

String chaineLue;
// Lecture sur le flux d'entrée tant que la requête FIN n'est pas arrivée
while ( (chaineLue = fluxEntree.readLine ()) != null
&& !chaineLue.equals (PaperBoardServer.requeteFin))
{
if (chaineLue.startsWith (PaperBoardServer.requeteListe))
{
// Si la liste des polylines est demandée, elles sont
// renvoyées séparées par des tabulations
for (Enumeration e = listePolylines.elements ();
e.hasMoreElements (); )
fluxSortie.print (e.nextElement ().toString () + '\t');
fluxSortie.write ('\n');
}
else if (chaineLue.equals (PaperBoardServer.requeteConnexion))
{
// Renvoi du nombre de connexions au serveur
fluxSortie.print (String.valueOf (connexions));
fluxSortie.write ('\n');
}
else if (chaineLue.startsWith (PaperBoardServer.requeteAjout))
{
// Si le vecteur arrive à saturation, il est vidée
// (ceci pour éviter que le dessin ne devienne trop dense)
if (listePolylines.size () == listePolylines.capacity ())
listePolylines.removeAllElements ();
// La nouvelle polyline reçue est ajoutée à la liste
listePolylines.addElement (
chaineLue.substring (PaperBoardServer.requeteAjout.length ()));
}
}

fluxEntree.close ();
fluxSortie.close ();
socket.close ();
}
catch (IOException e)
{
System.out.println (e);
}

connexions--;
}
}

Une fois programmé le serveur, il faut réaliser un programme client. Celui-ci est l'applet ci-dessus
qui envoie au serveur les polylines saisies par l'utilisateur, et demande au serveur toutes les
secondes la liste courante de toutes les polylines que celui-ci mémorise (à mettre dans un fichier
PaperBoardClient.java ) :

import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;

public class PaperBoardClient extends Applet implements Runnable


{
private String message;
private Socket socket;
private DataInputStream fluxEntree;
private PrintStream fluxSortie;

private Vector listePolylines = new Vector (100);


private Polygon polylineCourante;

private int connexions;


vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 18

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


public void start ()
{
setBackground (Color.white);
try
{
// Récupération de l'adresse de l'hôte www.eteks.com
InetAddress adresse = InetAddress.getByName ("www.eteks.com");
// Ouverture d'une connexion sur le port 26197 de cet hôte
socket = new Socket (adresse, PaperBoardServer.port);

// Récupération des flux d'entrés et de sortie


fluxEntree = new DataInputStream (
new BufferedInputStream (socket.getInputStream ()));
fluxSortie = new PrintStream (
new BufferedOutputStream (socket.getOutputStream ()), true);

// Lancement d'un thread qui interroge à intervalle régulier


// la liste des polylines enregistrées sur le serveur
new Thread (this).start ();
}
catch (IOException e)
{
message = "Probleme de connexion avec le serveur";
}
}

// Méthode appelée par le système à la disparition de l'applet


public void stop ()
{
try
{
// Envoi d'une requête FIN et fermeture des flux
fluxSortie.println (PaperBoardServer.requeteFin);
fluxSortie.close ();
fluxEntree.close ();
socket.close ();
}
catch (IOException e)
{ }

fluxSortie = null;
}

public void run ()


{
try
{
while (fluxSortie != null)
{
// Envoi d'une requête CONNEXION pour récupérer le
// nombre de clients connectés au serveur
fluxSortie.print (PaperBoardServer.requeteConnexion);
fluxSortie.write ('\n');

message = fluxEntree.readLine () + " connexions";

// Envoi d'une requête LISTE pour récupérer


// la liste de toutes les polylines du serveur
fluxSortie.print (PaperBoardServer.requeteListe);
fluxSortie.write ('\n');

String liste = fluxEntree.readLine ();

// Vidange de la liste pour la remettre à jour


listePolylines.removeAllElements ();
StreamTokenizer tokens = new StreamTokenizer (
new StringBufferInputStream (liste));
tokens.parseNumbers ();
tokens.ordinaryChar ('\t');
tokens.whitespaceChars (' ', ' ');

// Décodage de la liste de points


while (tokens.nextToken () != StreamTokenizer.TT_EOF)
{
Polygon polyline = new Polygon ();
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 19

// Récupération des couples de valeurs (x,y)


// d'une polyline jusqu'à la prochaine tabulation
while (tokens.ttype != '\t')
{
int x = (int)tokens.nval;
tokens.nextToken ();
int y = (int)tokens.nval;
tokens.nextToken ();
polyline.addPoint (x, y);
}
// Ajout de la polyline à la liste
listePolylines.addElement (polyline);
}

repaint ();
// Arrête le thread pendant 1 s avant de lancer
// une nouvelle demande de mise à jour
Thread.sleep (1000);
}
}
catch (InterruptedException e)
{ }
catch (IOException e)
{ }
}

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


public void paint (Graphics gc)
{
if (message != null)
gc.drawString (message, 10, 20);

// Dessin de toutes les polylines


// et de la polyline courante si elle existe
for (Enumeration e = listePolylines.elements ();
e.hasMoreElements (); )
drawPolyline (gc, (Polygon)e.nextElement ());

if (polylineCourante != null)
drawPolyline (gc, polylineCourante);
}

private void drawPolyline (Graphics gc, Polygon polyline)


{
for (int i = 1; i < polyline.npoints; i++)
gc.drawLine (polyline.xpoints [i - 1], polyline.ypoints [i - 1],
polyline.xpoints [i], polyline.ypoints [i]);
}

// Les méthodes mouseDown (), mouseDrag (), mouseUp () sont


// appelées par le système à l'enfoncement du bouton de la souris,
// au déplacement du pointeur, et au relâchement du bouton
public boolean mouseDown (Event evt, int x, int y)
{
polylineCourante = new Polygon ();
polylineCourante.addPoint (x, y);
return true;
}

public boolean mouseDrag (Event evt, int x, int y)


{
polylineCourante.addPoint (x, y);
paint (getGraphics ());
return true;
}

public boolean mouseUp (Event evt, int x, int y)


{
polylineCourante.addPoint (x, y);

// Construction d'une requête AJOUT avec la liste des points


// de la nouvelle polyline
fluxSortie.print (PaperBoardServer.requeteAjout);
for (int i = 0; i < polylineCourante.npoints; i++)
fluxSortie.print (String.valueOf (polylineCourante.xpoints [i])
+ ' ' + polylineCourante.ypoints [i] + ' ');
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 20

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

protected abstract void create (boolean stream) throws IOException


protected abstract void connect (String host, int port)
throws IOException
protected abstract void connect (InetAddress address, int port)
throws IOException
protected abstract void bind (InetAddress host, int port)
throws IOException
protected abstract void listen (int count) throws IOException
protected abstract void accept (SocketImpl s) throws IOException
protected abstract InputStream getInputStream () throws IOException
protected abstract OutputStream getOutputStream () throws IOException
protected abstract int available () throws IOException
protected abstract void close () throws IOException
protected FileDescriptor getFileDescriptor ()
protected InetAddress getInetAddress ()
protected int getPort ()
protected int getLocalPort ()

public String toString ()

L'interface java.net.SocketImplFactory

Cette interface est implémentée par les classes désirant gérer la création de sockets. Elle est utilisée
comme paramètre des méthodes setSocketImplFactory () de la classe Socket et setSocketFactory
() de la classe ServerSocket, pour modifier le gestionnaire par défaut.

Méthode

public SocketImpl createSocketImpl ()


vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 21

Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract SocketImpl.

Accès via les datagrammes


Pour programmer une architecture client serveur utilisant le protocole UDP (User Datagram
Protocol ), il faut utiliser les classes DatagramPacket représentant un paquet de données (ou
datagramme) à recevoir ou envoyer, et DatagramSocket utilisé pour recevoir et envoyer des
datagrammes.

Contrairement au protocole TCP, le protocole UDP ne maintient pas de connexion permanente entre
deux machines. Chaque datagramme est indépendant l'un de l'autre : il comporte un tableau de
données, l'adresse Internet et le port de la machine à laquelle il est destiné.

La classe java.net.DatagramPacket

Cette classe final représente un datagramme. Le premier constructeur est utilisé pour créer un
datagramme à recevoir, le second pour créer un datagramme à envoyer à une machine.

Constructeurs

public DatagramPacket (byte ibuf [ ], int ilength)

Construit une instance de la classe DatagramPacket utilisé pour recevoir un datagramme dont les
paquets de données de longueur ilength seront stockées dans le tableau ibuf. Si le datagramme
reçu est plus long que ilength, les données restantes ne sont pas recopiées dans ibuf.

public DatagramPacket (byte ibuf [ ], int ilength,


InetAddress iaddr, int iport)

Construit une instance de la classe DatagramPacket utilisé pour envoyer un paquet de données ibuf
de longueur ilength, au port iport d'une machine d'adresse Internet iaddr.

Méthodes

public InetAddress getAddress ()

Renvoie l'adresse Internet de la machine dont provient ou auquel est destiné ce datagramme.

public int getPort ()

Renvoie le port de la machine dont provient ou auquel est destiné ce datagramme.

public byte[] getData ()


public int getLength ()

Ces méthodes renvoient le tableau où sont stockées les données d'un datagramme et la longueur des
données à recevoir (ou reçues) ou à envoyer.

La classe java.net.DatagramSocket

Cette classe permet de recevoir et envoyer des datagrammes, grâce aux méthodes receive () et
send ().

Constructeurs

public DatagramSocket () throws SocketException

Construit une instance de la classe DatagramSocket. Le port utilisé pour recevoir ou envoyer des
datagrammes est le premier disponible sur la machine locale.

public DatagramSocket (int port) throws SocketException

Construit une instance de la classe DatagramSocket. Le port port sur la machine locale est utilisé
pour recevoir ou envoyer des datagrammes.
vendredi 13 octobre 2000 Du C/C++ à Java : Les accès au réseau Page: 22

Méthodes

public int getLocalPort ()

Renvoie le port de la machine locale sur lequel l'envoi ou la réception de datagrammes s'effectue.

public void synchronized receive (DatagramPacket packet) throws IOException

Permet de recevoir un paquet de données dans le datagramme packet. Cette méthode met en attente
le thread courant jusqu'à réception d'un datagramme.

public void send (DatagramPacket packet) throws IOException

Permet d'envoyer le datagramme packet.

public synchronized void close ()

Ferme le socket.

protected synchronized void finalize ()


vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 1

Les applications et les applets

Les applications Java


Les applets
L'intégration des applets dans les navigateurs
Transformer une applet en application

Ce chapitre décrit les applications Java et le package java.applet de Java 1.0 qui rassemble les
classes permettant de programmer une applet Java et de l'intégrer dans un navigateur.

Les applications Java


Comme il est décrit au premier chapitre, l'environnement Java vous permet de développer
principalement deux types de programmes : des applications Java ou des applets Java.

Les applications sont comparables à tout programme écrit dans un autre langage. Le point d'entrée
d'une application Java est la méthode main () de la classe donnée en argument à la commande
java et respectant la déclaration suivante :

public static void main (String [ ] args)

Comme n'importe quelle classe public ClasseExec qui définit cette méthode peut être exécutée par
la Machine Virtuelle Java, vous pouvez ajouter une méthode main () à une ou plusieurs classes
utilisées dans un même programme. Ceci permet de tester individuellement chacune de ces classes.
Attention, cette méthode est static et la Machine Virtuelle Java ne crée donc aucune instance de la
classe ClasseExec.
Les arguments passés après le nom de la classe, comme dans la ligne de commande java
ClasseExec arg0 arg1 sont mémorisés dans le tableau args (args [0] mémorise la chaîne arg0).

La méthode main () peut être suivie d'une clause throws pour citer toutes les classes d'exceptions
qu'elle n'intercepte pas. Ceci est pratique quand vous voulez écrire de petits programmes de test
sans vous soucier du traitement de certaines exceptions.

De manière comparable au C, le point d'entrée d'un programme Java, est la méthode static main
() définie par la classe passée en argument à l'interpréteur Java.
La Machine Virtuelle Java rend la main au système une fois terminée l'exécution de la méthode
main (), si et seulement si plus aucun thread n'est vivant, excepté pour les threads en tâche de
fond (daemon ).
Vous pouvez aussi utiliser la méthode exit () de la classes System à tout moment pour forcer la
Machine Virtuelle à rendre la main.
Pour sa propre gestion, la bibliothèque AWT crée un ensemble de threads aussitôt qu'elle est
utilisé. Comme ces thread ne sont pas des daemons , il faut donc se servir de la méthode exit ()
pour forcer la fin d'un programme utilisant AWT.

Les applets
Caractéristiques
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.

appletviewer et certains navigateurs permettent de modifier les paramètres du gestionnaire de


sécurité, pour autoriser certaines opérations. Mais si vous voulez réaliser des applets
accessibles publiquement sur Internet, concevez les en considérant que le niveau de sécurité
des navigateurs utilisés pour exécuter vos applets sera à son maximum.

Certaines caractéristiques qui précédent s'adressent à l'utilisation d'applet dans des pages HTML
affichées par un navigateur. Si vous utilisez appletviewer (ou Applet Runner sous MacOS), la
commande appletviewer pageweb.html créera une fenêtre isolée pour chaque applet définie par la
balise <APPLET ...> du fichier pageweb.html.

On rencontre habituellement deux types d'applet :

Des applets dans lesquelles on dessine directement un dessin ou un texte en outrepassant la


méthode paint () de la classe Component, comme l'applet HelloWorld décrite au premier
chapitre, dont le résultat donne ceci :
Des applets qui sont utilisées comme container pour y inclure différents composants, en
appelant la méthode add () de la classe Container, comme l'applet AppletBouton décrite au
chapitre suivant, dont le résultat donne ceci :

La classe java.applet.Applet
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

public void init ()

Cette méthode est appelée automatiquement une fois au chargement du fichier HTML dans lequel
une applet est appelée. C'est le point d'entrée principal de l'applet (comparable à la méthode main ()
des applications). Outrepassez cette méthode dans votre classe d'applet, pour initialiser votre applet.

public void start ()

Cette méthode est appelée à chaque fois qu'une applet est visualisée dans un navigateur ou dans
appletviewer . Si vous devez effectuer des opérations à chaque visualisation de votre applet,
outrepassez cette méthode dans votre classe d'applet.

public void stop ()

Cette méthode est appelée à chaque fois qu'une applet est effacée dans un navigateur ou dans
appletviewer (la page HTML où est visualisée l'applet n'est plus la page courante). Si par exemple
votre applet utilise un thread qui tourne continuellement, c'est l'endroit idéal pour l'arrêter, en
outrepassant cette méthode dans votre classe d'applet, comme dans l'exemple du chronomètre. En
effet, n'oubliez pas que si le thread n'est pas arrêté, il effectuera des opérations dont le résultat peut
être inutile, puisque la page HTML n'est plus affichée dans le navigateur.

public void destroy ()

Cette méthode est appelée quand le fichier HTML dans lequel une applet est appelée est supprimé
de la mémoire du navigateur. Si vous devez libérer certaines ressources utilisées par votre applet,
outrepassez cette méthode dans votre classe d'applet.

public String getAppletInfo ()

Cette méthode renvoie une chaîne de caractères d'information sur une applet. Outrepasser cette
méthode pour renvoyer une chaîne indiquant les renseignements concernant votre applet
(Copyright, version,...) .

public String [ ][ ] getParameterInfo ()

Cette méthode renvoie un tableau de chaînes de caractères correspondant aux paramètres qu'il est
possible d'utiliser sur une applet. Outrepasser cette méthode pour renvoyer la liste des paramètres
accepter par votre applet. Le tableau doit être un ensemble de tableaux de 3 chaînes fournissant
pour chaque paramètre son nom, le type de valeur attendue et sa description, comme par exemple :
{"param", "0-10", "Description de param"}.

public void resize (int width, int height)


public void resize (Dimension d)

Permet de changer les dimensions d'une applet.

public URL getDocumentBase ()

Renvoie l'URL du fichier HTML dans lequel l'applet est incluse.

public URL getCodeBase ()

Renvoie l'URL du répertoire du fichier .class à partir duquel l'applet a été chargée (cette URL est
construite grâce à l'attribut CODEBASE du tag APPLET).
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 4

public String getParameter (String name)

Cette méthode permet de récupérer la valeur ValueParam du paramètre name de l'applet donné par la
balise <PARAM NAME="name" VALUE="ValueParam">. null est renvoyé si le paramètre name n'existe pas.

public AppletContext getAppletContext ()

Renvoie l'instance de la classe AppletContext, représentant le contexte dans lequel l'applet tourne
(fourni par le navigateur ou par appletviewer ).

public boolean isActive ()

Renvoie true si une applet est active. Une applet devient active à l'appel de sa méthode start ().

public void showStatus (String msg)

Cette méthode affiche le message msg dans la fenêtre d'état du navigateur (en général, cette fenêtre
est une barre d'état située en bas des fenêtres des navigateurs).

public Image getImage (URL url)


public Image getImage (URL url, String name)

Ces méthodes permettent d'obtenir l'image à l'URL url ou l'image name relative à url. L'image n'est
effectivement chargée qu'à sa première utilisation (voir le chargement des images). Les images
peuvent être aux formats GIF animé ou non, ou JPEG.

public AudioClip getAudioClip (URL url)


public AudioClip getAudioClip (URL url, String name)
public void play (URL url)
public void play (URL url, String name)

Ces méthodes permettent d'obtenir ou de jouer directement le fichier son à l'URL url ou le fichier
son name relatif à url.

public final void setStub (AppletStub stub)

Permet de donner à l'applet une instance stub de la classe implémentant AppletStub. Cette méthode
est appelée par le navigateur ou par appletviewer pour lier l'applet aux fonctionnalités fournies par
les interfaces AppletContext, AppletStub et AudioClip.

Les méthodes start () et stop () des classes Applet et Thread n'ont aucun lien entre elles. Une
applet n'est pas un thread, mais par contre, il est vrai que la plupart des navigateurs crée un thread
pour chaque applet d'une page HTML.

Exemples

Vous avez l'embarras du choix dans la liste des applets fournie dans la table des matières !

L'intégration des applets dans les navigateurs


La plupart des méthodes de la classe Applet font appel aux méthodes des interfaces suivantes. Les
classes implémentant ces interfaces sont fournies avec les navigateurs ou avec appletviewer .

L'interface java.awt.AppletContext

L'interface AppletContext représente le contexte dans lequel l'applet tourne.

Méthodes

public AudioClip getAudioClip (URL url)


public Image getImage (URL url)
public void showStatus (String status)

Voir la classe Applet.


vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 5

public Applet getApplet (String name)


public Enumeration getApplets ()

Ces méthodes renvoient soit une applet en fonction de son nom (donné par la valeur de NAME dans la
balise <APPLET ...>), soit une énumération de toutes les applets du contexte dans lequel une applet
tourne. Par exemple, une fois que vous avez trouvé une applet, il est possible de communiquer avec
elle (voir l'exemple qui suit).

public void showDocument (URL url)


public void showDocument (URL url, String target)

Ces méthodes permettent de charger dans le navigateur ou dans appletviewer un nouveau


document à l'adresse url. target peut être égal à une des valeurs suivantes :

"_self" : le document est chargé dans le frame courant.


"_parent" : le document est chargé dans le frame parent.
"_top" : le document est chargé dans le frame de niveau le plus élevé.
"_blank" : le document est chargé dans une nouvelle fenêtre sans nom.
Une autre chaîne "Titre" : le document est chargé dans le cadre (frame ) dénommé Titre.

showDocument () est utilisé typiquement pour charger un document quand un utilisateur clique sur
un bouton défini dans une applet.

Exemples

Applets PlayApplet et BoutonsNavigation .

L'interface java.applet.AppletStub

L'interface AppletStub est utilisée pour intégrer une applet dans un navigateur ou dans
appletviewer .

Méthodes

public boolean isActive ()


public URL getDocumentBase ()
public URL getCodeBase ()
public String getParameter (String name)
public AppletContext getAppletContext ()

Voir la classe Applet pour la définition de ces méthodes.

public void appletResize (int width, int height)

Cette méthode est appelée par les méthodes resize () de la classe Applet pour redimensionner une
applet dans la fenêtre d'un navigateur.

Voici un exemple d'utilisation des méthodes getParameter () et getApplet (), qui permet de
retrouver une applet appletControlee dont le nom est donné en paramètre. Une fois trouvée cette
applet, on lui associe un bouton qui appelle alternativement les méthodes start () ou stop () de
l'applet appletControlee pour la démarrer ou l'arrêter. Ici, l'applet de la classe Observable est reprise
pour illustrer cet exemple :

et le programme Java correspondant (à copier dans un fichier dénommé PlayApplet.java et


invoqué à partir d'un fichier HTML) :

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

public class PlayApplet extends Applet


{
private Applet appletControlee = null;
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 6

private Button boutonDemarrerArreter;

public void init ()


{
String nomApplet = getParameter ("Applet");
String action = getParameter ("Action");

if (nomApplet != null)
try
{
Applet applet;
do
// Recherche de l'applet dont le nom égale nomApplet
if ( (applet = getAppletContext ()
.getApplet (nomApplet)) == null
|| !applet.isActive ())
// Si l'applet recherchée n'existe pas, attendre 1 seconde
// (l'applet n'est peut-être pas encore chargée)
Thread.sleep (1000);
else
appletControlee = applet;
while (appletControlee == null);

// L'applet a été trouvée, ajouter un bouton pour la contrôler


add (boutonDemarrerArreter = new Button (action));
// Si le paramètre action indique de démarrer l'applet au départ,
// arrêter l'applet pour l'utilisateur puisse la démarrer
if (action.equals ("D\u00e9marrer"))
appletControlee.stop ();
}
catch (InterruptedException e)
{ }
}

// Méthode appelée quand on clique sur le bouton


public boolean action (Event event, Object arg)
{
if (event.target == boutonDemarrerArreter)
{
// Arrêter ou démarrer l'applet suivant le nom du bouton
if ("Arr\u00eater".equals (arg))
appletControlee.stop ();
else
{
appletControlee.start ();
appletControlee.validate ();
}

// Changer le nom du bouton et le réafficher


boutonDemarrerArreter.setLabel ("Arr\u00eater".equals (arg)
? "D\u00e9marrer" : "Arr\u00eater");
boutonDemarrerArreter.invalidate ();
validate ();
return true;
}
return super.action (event, arg);
}
}

Si vous consultez le source de ce fichier HTML , vous pourrez voir comment utiliser l'applet
PlayApplet avec ses paramètres. Le paramètre de nom Applet vous permettant d'indiquer quel est
le nom de la classe de l'applet que vous voulez contrôler :

<APPLET CODE="ObservateurCalcul" CODEBASE="../classes"


NAME="calcul"
ALT="ObservateurCalcul" WIDTH=250 HEIGHT=30 ALIGN=middle>
</APPLET>
<APPLET CODE="PlayApplet" CODEBASE="../classes"
ALT="PlayApplet" WIDTH=80 HEIGHT=30 ALIGN=middle>
<PARAM NAME="Applet" VALUE="calcul">
<PARAM NAME="Action" VALUE="D&eacute;marrer">
</APPLET>

L'applet PlayApplet n'a pas qu'un intérêt didactique : elle permet de contrôler sans les modifier, des
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

public void play ()

Cette méthode démarre le son de l'objet AudioClip. A chaque fois, que cette méthode est appelée, le
son redémarre.

public void loop ()

Démarre le son et le joue en boucle.

public void stop ()

Arrête le son. N'oubliez pas d'arrêter de jouer un son dans la méthode stop () d'une applet, si ce
son ne doit être joué qu'avec cette applet et surtout s'il est joué en boucle.

Voici un exemple d'utilisation de l'interface AudioClip, qui joue le son correspondant à la note d'un
octave de piano sur lequel on clique (il faut bien sûr une carte son sur votre ordinateur !):

et le programme Java correspondant (à copier dans un fichier dénommé Piano.java et invoqué à


partir d'un fichier HTML) :

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

public class Piano extends Applet


{
AudioClip [ ] octave = new AudioClip [12];
String [ ] notes = {"do.au", "dodiese.au", "re.au", "rediese.au", "mi.au",
"fa.au", "fadiese.au", "sol.au", "soldiese.au",
"la.au", "ladiese.au", "si.au"};
Rectangle [ ] rectNotes = new Rectangle [12];
boolean [ ] notesBlanches = {true, false, true, false, true,
true, false, true, false, true, false, true};

public void init ()


{
// Chargement des 12 fichiers de notes
for (int i = 0; i < notes.length; i++)
octave [i] = getAudioClip (getCodeBase (), notes [i]);

// Changement de la couleur de fond


setBackground (Color.white);
}

// Méthode appelée par la Machine Virtuelle quand l'applet change de taille


public void reshape (int newx, int newy, int largeur, int hauteur)
{
int largeurBlanche = largeur / 7;
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 8

int largeurNoire = largeurBlanche / 2;

// Calcul des rectangles représentant les 12 notes (noires ou blanches)


for (int x = 0, i = 0; i < notes.length;
x += notesBlanches [i++] ? largeurBlanche : 0)
if (notesBlanches [i])
rectNotes [i] = new Rectangle (x, 0, largeurBlanche - 1, hauteur - 1);
else
rectNotes [i] = new Rectangle (x - largeurNoire / 2, 0,
largeurNoire - 1, 2 * hauteur / 3);
// Rappel de la méthode de la super classe
super.reshape (newx, newy, largeur, hauteur);
}

// Méthode appelée par la Machine Virtuelle quand l'applet doit être dessinée
public void paint (Graphics gc)
{
gc.setColor (Color.black);
// Dessin des blanches
for (int i = 0; i < notes.length; i++)
if (notesBlanches [i])
gc.drawRect (rectNotes [i].x, rectNotes [i].y,
rectNotes [i].width, rectNotes [i].height);
// Dessin des noires
for (int i = 0; i < notes.length; i++)
if (!notesBlanches [i])
gc.fillRect (rectNotes [i].x, rectNotes [i].y,
rectNotes [i].width, rectNotes [i].height);
}

// Méthode recherchant la note à la position (x,y)


private AudioClip rechercherNote (int x, int y)
{
// Recherche d'abord parmi les noires puis les blanches car les
// rectangles des noires sont au-dessus de ceux des blanches
for (int i = 0; i < notes.length; i++)
if ( !notesBlanches [i]
&& rectNotes [i].inside (x, y))
return octave [i];
for (int i = 0; i < notes.length; i++)
if (rectNotes [i].inside (x, y))
return octave [i];
return null;
}

// Méthode appelée par la Machine Virtuelle


// quand le bouton de la souris est enfoncé
public boolean mouseDown (Event evt, int x, int y)
{
AudioClip note = rechercherNote (x, y);
if (note != null)
note.play ();
return true;
}
}

Les méthodes paint () et reshape () sont décrites dans le chapitre suivant. Les méthodes utilisées
dans la méthode paint () ainsi que la méthode outrepassée mouseDown () sont décrites dans le
chapitre sur la gestion de l'interface utilisateur.

Bien que le seul format reconnu actuellement réduise la taille (et la qualité) des fichiers sons,
ceux-ci sont très gourmands en octets. Rien que l'applet Piano qui utilise douze (petits) fichiers
sons de 6 K en moyenne, doit charger 72 K pour fonctionner. Donc faites attention à la taille de
vos sons, si vous voulez utiliser vos applets sur Internet.

Transformer une applet en application


Il est partiellement possible de créer une classe d'applet qui puisse aussi fonctionner comme
application isolée, en définissant une méthode main () qui crée une fenêtre dans laquelle l'applet est
ajoutée (voir l'exemple de la classe Frame).
Les méthodes suivantes de la classe Applet sont en fait des appels indirects aux classes qui
vendredi 13 octobre 2000 Du C/C++ à Java : Les applications et les applets Page: 9

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 :

getDocumentBase () interface AppletStub


getCodeBase () interface AppletStub
getParameter () interface AppletStub
getAppletContext () interface AppletStub
isActive () interface AppletStub
getImage () interface AppletContext
getAudioClip () interface AppletContext
showStatus () interface AppletContext
play () interface AudioClip

Si vous utilisez dans votre applet une de ces méthodes, pour éviter le déclenchement d'exception il
vous faut créer la ou les classes qui implémentent les méthodes des interfaces correspondantes,
même si leur implémentation ne fait rien (seule la méthode getImage () est aussi disponible dans la
classe Toolkit).
Une instance de la classe implémentant AppletStub est associée à chaque applet grâce à la méthode
setStub (), une instance de la classe implémentant AppletContext est associée à chaque fenêtre de
navigateur et une instance de la classe implémentant AudioClip est associée à chaque son.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 1

Les composants de l'interface utilisateur

Les composants Java


La classe java.awt.Component
Les composants prédéfinis
Comment ça marche ?

Les composants Java


Le package java.awt (Abstract Window Toolkit) est le plus riche des packages Java. Il définit
toutes les classes permettant de construire et de gérer l'interface utilisateur. Les classes les plus
importantes de ce package sont celles qui permettent de créer les différents composants d'une
interface utilisateur. Les composants se répartissent en trois catégories :

1. Les composants prédéfinis sur lesquels l'utilisateur agit directement pour communiquer avec
le programme (dans d'autres environnements comme Windows, on les appelle des contrôles).
Voici la liste de ceux fournis avec le package java.awt :
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.

Les ascenseurs (de classe Scrollbar) : ces composants sont utilisés en


association avec un autre composant (en général un container), quand la
zone d'affichage de celui-ci est trop petite pour contenir tout ce qu'il peut
afficher. L'ascenseur (horizontal et/ou vertical) permet alors à l'utilisateur de
déplacer la zone d'affichage sur d'autres portions du composant (comme on
bouge des jumelles pour voir différents détails d'un paysage).

2. Les composants dérivés de la classe Canvas : Si les composants prédéfinis ne satisfont pas un
de vos besoins, vous devez utiliser cette classe comme super classe pour créer vos propres
composants que vous dessinez en utilisant les méthodes de la classe Graphics.
3. Les containers : Contrairement aux autres composants, les containers sont utilisés pour
contenir d'autres composants de n'importe quelle catégorie ou pour afficher directement des
dessins fabriqués avec les méthodes de la classe Graphics. Les containers se subdivisent en
deux sous catégories qui héritent toutes deux de la classe abstract Container :
Les containers de type fenêtre (de classe Window ou ses dérivées) : Les fenêtres sont des
zones d'affichage indépendantes les unes des autres. Parmi les fenêtres, on distingue les
boites de dialogue (de classe Dialog) comportant un cadre et un titre, et les fenêtres de
classe Frame comportant un cadre, un titre, un menu éventuel, un pointeur de souris
propre,... (et correspondant aux fenêtres que vous avez l'habitude d'utiliser).
Les containers de classe Panel : Ce type de containers est une zone d'affichage occupant
tout ou partie d'une fenêtre. Notamment, la classe Applet dérive de Panel et permet
d'afficher des composants ou un dessin dans une fenêtre d'un navigateur.

Toutes les classes de composants citées ci-dessus dérivent directement ou indirectement d'une
unique classe : la classe Component. Cette classe décrit tous les comportements communs à tous les
composants.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 3

Voici la hiérarchie des classes qui dérivent de la classe Component :

java.awt.Component
java.awt.Button
java.awt.Canvas
java.awt.Checkbox
java.awt.Choice
java.awt.Container
java.awt.Panel
java.applet.Applet
java.awt.Window
java.awt.Dialog
java.awt.FileDialog
java.awt.Frame
java.awt.Label
java.awt.List
java.awt.Scrollbar
java.awt.TextComponent
java.awt.TextArea
java.awt.TextField

Quand vous recherchez les méthodes disponibles sur une classe dérivée de Component, n'oubliez
pas de consulter toutes les super classes dont elle hérite, pour connaître l'ensemble complet de ses
fonctionnalités.

Tous ces composants prennent l'aspect de ceux du système sur lequel la Machine Virtuelle
fonctionne.
Et les menus dans tout ça ?! Les menus et sous-menus d'une fenêtre sont des instances des classes
dérivant de la classe abstract MenuComponent qui n'a pas de lien d'héritage avec la classe Component,
c'est pourquoi ils sont traités dans le paragraphe sur les menus.

Contrairement à d'autres systèmes (Windows, MacOS,...), il n'existe pas en Java 1.0 de concept de
fichier ressource utilisé pour décrire et créer l'interface utilisateur, et séparé du programme qui s'en
sert. Vous devez créer vous-même tous les composants et les ajouter au container qui les contient.
Par contre, leur disposition (position et taille) peut être gérée automatiquement par un layout
associé à chaque container.

La classe java.awt.Component
Cette classe abstract est la super classe de toutes les classes de composants citées ci-dessus, et
implémente l'interface ImageObserver. Elle comporte un très grand nombre de méthodes, qui sont
souvent outrepassées par les classes dérivées de Component.
Si vous devez créer une applet ou un programme avec une interface graphique, vous utiliserez les
classes de composants ou vous créerez de nouvelles classes qui en dérivent (de la classe Applet
notamment). Comme vous aurez à outrepasser ces méthodes ou vous les invoquerez directement ou
indirectement, il vous faut bien maîtriser l'utilité de la plupart d'entre elles.

Méthodes

public Container getParent ()


public ComponentPeer getPeer ()
public Toolkit getToolkit ()

Ces méthodes permettent d'obtenir le composant parent (par exemple le container auquel appartient
un bouton), le peer d'un composant et le kit d'outils (Toolkit ) gérant la création des composants à
l'écran.

public boolean isValid ()


vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 4

public boolean isVisible ()


public boolean isShowing ()
public boolean isEnabled ()

Ces méthodes permettent de savoir si un composant est valide, s'il est visible, s'il est affiché et s'il
est utilisable.
Un composant est invalide, quand son image à l'écran ne correspond pas aux valeurs de ses 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é.

public Point location ()


public Dimension size ()
public Rectangle bounds ()

Ces méthodes renvoient la position, la taille et le rectangle englobant d'un composant. Les
coordonnées sont exprimées dans le système de coordonnées du parent du composant, sachant que
le point (0,0) est en haut à gauche et que l'axe des y descend vers le bas.

public synchronized void enable ()


public synchronized void disable ()
public void enable (boolean cond)

Vous pouvez rendre utilisable ou inutilisable un composant grâce à ces méthodes. Un composant
inutilisable (disabled ) est généralement grisé.

public synchronized void show ()


public synchronized void hide ()
public void show (boolean cond)

Ces méthodes permettent d'afficher ou d'effacer un composant. Ces méthodes sont surtout utilisées
pour afficher ou effacer une fenêtre à l'écran.

public Color getForeground ()


public synchronized void setForeground (Color c)
public Color getBackground ()
public synchronized void setBackground (Color c)
public Font getFont ()
public synchronized void setFont (Font f)
public synchronized ColorModel getColorModel ()

Utilisez ces méthodes pour obtenir ou modifier la couleur d'un composant, sa couleur de fond et la
police de caractères qu'il utilise pour afficher son texte (voir aussi la classe Graphics).

public void move (int x, int y)


public void resize (int width, int height)
public void resize (Dimension d)
public synchronized void reshape (int x, int y, int width, int height)

Ces méthodes sont utilisées pour déplacer et modifier la taille d'un composant. Généralement, on ne
les appelle pas directement pour placer un composant dans un container : ces composants sont
disposés automatiquement par le layout associé au container. Par contre, vous devez donner une
dimension aux fenêtres que vous créez.
Si vous voulez utiliser les nouvelles dimensions d'un composant comme dans l'applet Piano,
outrepassez la méthode reshape () car les trois autres méthodes appellent cette méthode.

public Dimension preferredSize ()


public Dimension minimumSize ()

Ces méthodes renvoient la taille préférée et la taille minimum d'un composant, pour que son
affichage se fasse correctement. Ces méthodes sont appelées par certains classes de layout, pour
connaître les dimensions correctes d'un composant.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 5

public void layout ()

L'implémentation de cette méthode ne fait rien dans la classe Component. Elle est outrepassée
notamment par la classe Container, pour disposer les composants appartenant à un container.

public void validate ()

Si un composant est invalide (son image à l'écran ne correspond pas aux valeurs de ses champs),
cette méthode appelle layout (). Elle est outrepassée dans la classe Container pour mettre à jour
tous les composants d'un container invalide.

public void invalidate ()

Marque un composant et ses parents comme étant invalide. Ainsi, un appel à validate () pourra
mettre à jour ces composants invalides.

public Graphics getGraphics ()

Permet de d'obtenir un contexte graphique pour un composant, pour y effectuer des dessins. Cette
méthode renvoie null si le composant n'a pas encore été affiché.

public FontMetrics getFontMetrics (Font font)

Permet de récupérer les renseignements concernant une police de caractère (hauteur, largeur,...).

public void paint (Graphics gc)

Cette méthode est appelée quand le dessin d'un composant doit être mis à jour (à la première
visualisation d'un composant, quand il change de taille, quand une portion d'un composant cachée
par une fenêtre, est réaffichée,...). L'implémentation de la méthode paint () de la classe Component
ne fait rien. Quand vous créez de nouvelles classes de composants (dérivées d'une des classes
Canvas, Applet, Frame,...), vous devez outrepasser cette méthode pour dessiner le composant avec
les méthodes de la classe Graphics, si vous ne voulez pas vous en servir comme container.

Les composants Java n'utilisent pas le double-buffering (système utilisant une image cachée,
copie de l'image d'un composant à l'écran). C'est à dire que, par défaut, à chaque fois qu'un
composant a besoin d'être redessiné (même si ce dessin n'a pas changé), la méthode paint () de sa
classe est appelée et donc que toutes les instructions de cette méthode sont exécutées. Ceci
implique deux inconvénients majeurs :

Si le dessin est inchangé (comme par exemple, quand un composant caché par une fenêtre
est réaffichée), toutes les instructions de paint () sont réexécutées et ceci peut prendre un
certain temps.
L'utilisateur voit le dessin se fabriquer à l'écran, ce qui peut être gênant si notamment, vous
voulez faire des animations.

Par contre, l'avantage est que moins de mémoire est utilisée pour l'affichage d'un composant (pas
de besoin de mémoriser une image copie du composant à l'écran).
Le chapitre sur les images, décrit comment simuler un effet de double-buffering , en créant une
image dans laquelle vous dessinez avant de la copier à l'écran.

public void update (Graphics gc)

Cette méthode est appelée indirectement par les méthodes repaint (). La méthode update ()
remplit d'abord le composant avec sa couleur de fond avant d'appeler paint (). Si le dessin réalisé
par les instructions de la méthode paint () occupe la totalité de la surface du composant,
outrepassez la méthode update () en appelant directement paint (), ceci évitera que le fond soit
rempli inutilement.

public void paintAll (Graphics gc)

Redessine un composant et pour un container tous les composants lui appartenant.


vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 6

public void repaint ()


public void repaint (long millisec)
public void repaint (int x, int y,
int width, int height)
public void repaint (long millisec, int x, int y,
int width, int height)

Redessine un composant. Ces méthodes provoque indirectement un appel à update (), soit le plus
rapidement possible, soit après au moins millisec millisecondes. x, y, width et height délimite la
zone à redessiner.

public void print (Graphics gc)

Imprime le composant. L'implémentation de print () de la classe Component appelle paint ().

public void printAll (Graphics gc)

Imprime un composant et pour un container tous les composants lui appartenant.

public synchronized boolean inside (int x, int y)

Vérifie si le point de coordonnées (x,y) appartient à la boite englobante du composant.

public Component locate (int x, int y)

L'implémentation de cette méthode renvoie le composant lui-même si le point de coordonnées (x,y)


appartient à sa boite englobante. Elle est outrepassée dans la classe Container pour rechercher le
composant situé au point de coordonnées (x,y) d'un container.

public Image createImage (int width, int height)


public Image createImage (ImageProducer producer)
public boolean prepareImage (Image image, ImageObserver observer)
public boolean prepareImage (Image image, int width, int height,
ImageObserver observer)
public int checkImage (Image image, ImageObserver observer)
public int checkImage (Image image, int width, int height,
ImageObserver observer)
public boolean imageUpdate (Image img, int flags,
int x, int y,
int width, int height)

Ces méthodes permettent de manipuler les images. Voir le chapitre sur les images pour plus de
détails. La méthode imageUpdate () est l'implémentation de la méthode de l'interface ImageObserver.

public void deliverEvent (Event evt)


public boolean postEvent (Event evt)
public boolean handleEvent (Event evt)
public boolean mouseDown (Event evt, int x, int y)
public boolean mouseDrag (Event evt, int x, int y)
public boolean mouseUp (Event evt, int x, int y)
public boolean mouseMove (Event evt, int x, int y)
public boolean mouseEnter (Event evt, int x, int y)
public boolean mouseExit (Event evt, int x, int y)
public boolean keyDown (Event evt, int key)
public boolean keyUp (Event evt, int key)
public boolean action (Event evt, Object what)
public boolean gotFocus (Event evt, Object what)
public boolean lostFocus (Event evt, Object what)

Ces méthodes sont utilisées pour la gestion événementielle des composants (voir le chapitre sur la
gestion de l'interface utilisateur).

public void requestFocus ()

Permet à un composant de lancer une requête pour obtenir le focus. Si le focus lui est accordé, la
méthode gotFocus () sera appelée.

public void nextFocus ()

Donne le focus au composant suivant parmi ceux de la liste des composants d'un container.
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 7

public void addNotify ()

Cette méthode est outrepassée par les classes dérivées de Component pour créer le peer d'un
composant, qui est sa représentation à l'écran. Elle est appelée indirectement par les méthodes qui
gèrent l'affichage des composants.

public synchronized void removeNotify ()

Cette méthode est appelée pour supprimer le peer d'un composant.

protected String paramString ()


public String toString ()

Ces méthodes sont utilisées pour fabriquer une chaîne de caractères décrivant le composant
(toString () outrepasse la méthode de la classe Object et appelle la méthode paramString () qui
est outrepassée par les classes dérivées de Component).

public void list ()


public void list (PrintStream out)
public void list (PrintStream out, int indent)

Imprime sur System.out ou sur out, la chaîne de caractères décrivant le composant.

Les composants prédéfinis


La classe java.awt.Button

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer
et de manipuler des boutons. Une fois créé un objet de classe Button, vous devez l'ajouter à un
container avec la méthode add () de la classe Container, pour le visualiser.

Constructeurs

public Button ()
public Button (String label)

Ces constructeurs permettent de créer un bouton avec ou sans label (texte affiché dans le bouton).

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'un
bouton.

public String getLabel ()


public void setLabel (String label)

Ces méthodes permettent d'interroger ou de modifier le label d'un bouton.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
bouton.

Exemples

Applets PlayApplet , AppletButton , TraitementTexte , MessageBoxApplet , BorderBuilder ,


ShowMenu et MiseAJourHorloge .

La classe java.awt.Checkbox

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer
et de manipuler des boites à cocher et les boutons radios. Une fois créé un objet de classe Checkbox,
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.

public Checkbox (String label, CheckboxGroup group,


boolean state)

Ce constructeur permet de construire une boite à cocher, ou un bouton radio si group est différent de
null. Si state est égal à true la boite à cocher est cochée, ou le bouton radio est le bouton coché
parmi tous les boutons radios qui appartiennent au groupe group.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
boite à cocher ou d'un bouton radio.

public String getLabel ()


public void setLabel (String label)

Ces méthodes permettent d'interroger ou de modifier le label d'une boite à cocher ou d'un bouton
radio.

public boolean getState ()


public void setState (boolean state)

Ces méthodes permettent d'interroger ou de modifier l'état (coché ou non) d'une boite à cocher ou
d'un bouton radio.

public CheckboxGroup getCheckboxGroup ()


public void setCheckboxGroup (CheckboxGroup group)

Ces méthodes permettent d'interroger ou de modifier le groupe auquel appartient un bouton radio.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
composant.

Exemple

Applet DrawIt .

La classe java.awt.CheckboxGroup

Cette classe est la classe du package java.awt qui permet de gérer un ensemble de boutons radios.
Parmi tous les boutons radios (de classe Checkbox) associés à une instance de CheckboxGroup, au plus
un seul peut être coché.

Constructeur

public CheckboxGroup ()

Méthodes
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 9

public Checkbox getCurrent ()


public synchronized void setCurrent (Checkbox box)

Ces méthodes permettent d'interroger ou de modifier le bouton radio qui est actuellement coché.

public String toString ()

Exemple

Applet DrawIt .

La classe java.awt.Choice

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer
des composants proposant un choix parmi une liste déroulante. Une fois créé un objet de classe
Choice, vous devez l'ajouter à un container avec la méthode add () de la classe Container, pour le
visualiser. A chaque choix de la liste est associé un indice, compris entre 0 et le nombre de choix -
1.

Constructeur

public Choice ()

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
instance de la classe Choice.

public int countItems ()

Renvoie le nombre de choix possibles dans le composant.

public String getItem (int index)

Renvoie la chaîne de caractères correspondante au choix d'indice index.

public synchronized void addItem (String item)

Permet d'ajouter en fin de liste un nouveau choix égal à la chaîne de caractères item.

public String getSelectedItem ()


public int getSelectedIndex ()

Ces méthodes renvoient soit la chaîne de caractères soit l'indice du choix courant.

public synchronized void select (int index)


throws IllegalArgumentException
public void select (String str)

Ces méthodes permettent de désigner le choix courant du composant, soit en donnant son indice
index, soit en donnant sa valeur str.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant le
composant.

Exemple

Applet CalculetteSimple .

La classe java.awt.List

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer
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.

public List (int rows, boolean multipleSelections)

Ce constructeur construit une liste dont le nombre de lignes affichées est égal à rows, et qui autorise
la sélection multiple si multipleSelections est égal à true.

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify () de la classe Component pour créer le peer d'une
liste.

public synchronized void removeNotify ()

Cette méthode outrepasse la méthode removeNotify () de la classe Component pour supprimer le


peer d'une liste.

public int countItems ()

Renvoie le nombre de choix possibles dans la liste.

public String getItem (int index)

Renvoie la chaîne de caractères correspondante au choix d'indice index.

public synchronized void addItem (String item)

Permet d'ajouter en fin de liste un nouveau choix égal à la chaîne de caractères item.

public synchronized void addItem (String item, int index)

Permet d'insérer à l'indice index un nouveau choix égal à la chaîne de caractères item (si index est
égal à -1, item est ajouté en fin de liste).

public synchronized void replaceItem (String newValue, int index)

Remplace la chaîne de caractères à l'indice index par newValue.

public synchronized void clear ()

Supprime toutes les chaînes de caractères de la liste.

public synchronized void delItem (int index)


public synchronized void delItems (int start, int end)

Supprime le choix à l'indice index, ou tous les choix compris entre les indices start et end.

public synchronized String getSelectedItem ()


public synchronized int getSelectedIndex ()

Ces méthodes renvoient soit la chaîne de caractères sélectionnée soit son indice (en cas de sélection
multiple seule la première est renvoyée).
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 11

public synchronized String [ ] getSelectedItems ()


public synchronized int [ ] getSelectedIndexes ()

Ces méthodes renvoient soit un tableau des chaînes de caractères sélectionnées soit un tableau de
leur indice.

public synchronized void select (int index)


public synchronized void deselect (int index)

Ces méthodes permettent de sélectionner ou de désélectionner une chaîne de caractères en donnant


son indice index.

public synchronized boolean isSelected (int index)

Renvoie true si la chaîne de caractères d'indice index est sélectionnée.

public int getRows ()

Renvoie le nombre de lignes visibles dans la liste.

public boolean allowsMultipleSelections ()

Renvoie true si une liste autorise la sélection multiple.

public void setMultipleSelections (boolean v)

Permet d'autoriser ou non la sélection multiple dans une liste.

public int getVisibleIndex ()

Renvoie l'indice de la chaîne de caractères qui a été rendue visible lors d'un précédent appel à la
méthode makeVisible ().

public void makeVisible (int index)

Force la chaîne de caractères d'indice index à être visible à l'écran.

public Dimension preferredSize (int rows)


public Dimension preferredSize ()
public Dimension minimumSize (int rows)
public Dimension minimumSize ()

Méthodes de la classe Component, outrepassées pour renvoyer la taille préférée ou la taille minimum
d'une liste.

protected String paramString ()

Méthode de la classe Component, outrepassée pour renvoyer une chaîne de caractères décrivant la
liste.

Voici un programme Java simple utilisant une liste : c'est celui de l'applet donnant la liste des
caractères Unicode compris entre '\u0080' et '\u00ff'(à copier dans un fichier dénommé
Unicode.java et invoqué à partir d'un fichier HTML)

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

public class Unicode extends Applet


{
public void init ()
{
// Utilisation d'un layout grille 1 x 1 qui permet
// à la liste d'occuper tout l'espace de l'applet
setLayout (new GridLayout (1, 1));

// Changement de police de caractères


setFont (new Font("Courier", Font.PLAIN, 12));
vendredi 13 octobre 2000 Du C/C++ à Java : Les composants de l'interface utilisateur Page: 12

// Création d'une liste affichant les caractères


// Unicode compris entre '\u0080' et '\u00ff'
List listeCaracteres = new List ();
for (short i = 0x0080; i <= 0x00ff; i++)
listeCaracteres.addItem ( "\\u00" + Integer.toHexString (i)
+ " " + new Character ((char)i));
// Ajout de la liste au container
add (listeCaracteres);
}
}

Autre exemple

Applet ListePolices .

La classe java.awt.Label

Cette classe qui dérive de la classe Component, est la classe du package java.awt qui permet de créer
et de manipuler des labels. Une fois créé un objet de classe Label, vous devez l'ajouter à un
container avec la méthode add () de la classe Container, pour le visualiser.

Champs

public final static int LEFT


public final static int CENTER
public final static int RIGHT

Constantes utilisées pour l'alignement du texte dans le composant.

Constructeurs

public Label ()
public Label (String label)
public Label (String label, int alignment)

Ces constructeurs permettent de créer un label avec ou sans texte, et en précisant éventuellement
l'alignement du texte dans le composant (égal à LEFT, CENTER ou RIGHT).

Méthodes

public synchronized void addNotify ()

Cette méthode outrepasse la méthode addNotify ()