Java est un langage objet avec des classes. Les classes ont une double fonction : structurer les programmes et dire comment on construit les objets. Pour ce qui est de la seconde fonction, il n’est pas si facile de définir ce qu’est exactement un objet dans le cas général. Disons qu’un objet possède un état et des méthodes qui sont des espèces de fonctions propres à chaque objet. Par exemple tous les objets possèdent une méthode toString sans argument et qui renvoie une chaîne représentant l’objet et normalement utilisable pour l’affichage. La section B.2 décrit la construction des objets à partir des classes.
Mais décrivons d’abord la structuration des programmes réalisée par les classes. Les classes regroupent des membres qui sont le plus souvent des variables (plus précisément des champs) et des méthodes, mais peuvent aussi être d’autres classes.
Un programme se construit à partir de une ou
plusieurs classes, dont une au moins contient une méthode main
qui est le point d’entrée du programme.
Les variables des classes sont les variables globales du programme et leurs
méthodes sont les fonctions du programme.
Une variable ou une méthode qui existent dès que la classe existe
sont dites statiques.
Commençons par un programme en une seule classe.
Par exemple, la classe simple suivante est un programme qui affiche
Coucou !
sur la console :
Cette classe ne sert pas à fabriquer des objets. Elle se suffit à elle
même.
Par conséquent tout ce qu’elle définit (variable msg
et méthode
main
) est statique. Par re-conséquent, toutes les déclarations
sont précédées du mot-clé static,
car si on ne met rien les membres ne sont pas statiques.
Si le source est contenu dans un fichier Simple.java, il se
compile par javac Simple.java et se lance par java
Simple. C’est une bonne idée de mettre les classes dans des fichiers
homonymes, ça permet de s’y retrouver.
En termes de programmation objet, la méthode main
invoque la méthode
println
de l’objet System.out
, avec l’argument
msg
. System.out
désigne la variable
out de
la classe System, qui fait partie de la bibliothèque
standard de Java. Notons que msg
est en fait Simple.msg
,
mais dans la classe Simple
, on peut se passer de rappeler que
msg
est une variable de la classe Simple
, alors autant en
profiter.
Reste à se demander quel est l’objet rangé dans System.out
. Et
bien, disons que c’est un objet d’une autre classe (la classe
PrintStream) qui a été mis là
par le système Java et ne nous en préoccupons plus pour le moment.
La déclaration de cette méthode doit obligatoirement être de la forme :
En plus d’être statique, la méthode main
doit impérativement
être publique (mot-clé public)
et prendre un tableau de chaîne en argument.
Le sens du mot-clé public est expliqué plus
loin.
Le reste des obligations porte sur le type de l’argument de main (son nom est libre). Le tableau de chaîne est initialisé par le système pour contenir les arguments de la ligne de commande. De sorte que l’on peut facilement écrire une commande echo en Java.1
Ce qui nous donne après compilation :
% java Echo coucou foo bar coucou foo bar
La classe-programme Simple
utilise déjà une autre classe,
la classe System
écrite par les auteurs du système Java.
Pour structurer vos programmes, vous pouvez (devez) vous aussi écrire
plusieurs classes.
Par exemple, récrivons le programme simple à l’aide de deux classes.
Le message est fourni par une classe Message
Tandis que le programme est modifié ainsi :
Si on met la classe Message dans un fichier Message.java, elle sera compilée automatiquement lorsque l’on compile le fichier Simple.java (par javac Simple.java). Encore une bonne raison pour mettre les classes dans des fichiers homonymes.
Lorsque l’on fabrique un programme avec plusieurs classes, l’une
d’entre elles contient la méthode main
. Les autres fournissent
des services, en général sous forme de méthodes accessibles à
partir des autres classes.
Supposons que la classe Hello
doit fournir un message
de bienvenue, en anglais ou en français. On pourra écrire.
Classe utilisée par une nouvelle classe Simple
.
La variable hello
est privée
(mot-clé private) ce qui interdit son accès à partir de code
qui n’est pas dans la classe Hello
. Une méthode
getHello
est donc fournie, pour pouvoir lire le message. Deux
autres méthodes laissent la possibilité aux utilisateurs de la classe
de sélectionner le message anglais ou le message français. Enfin, le
bout de code static { setEnglish() ; }
est exécuté lors de la
création de la classe en machine, ce qui assure le choix initial de la
langue du message. Finalement, la conception de la classe Hello
garantit une propriété : à tout instant, Hello.hello
contient
nécessairement un message de bienvenue en français ou en anglais.
La pratique de restreindre autant que possible la visibilité des variables et méthodes améliore à la fois la sûreté et la structuration des programmes.
On parle d’abstraction, la séparation en classe segmente le programme en unités plus petites, dont on n’a pas besoin de tout savoir.
À l’intérieur de la classe elle-même, la démarche d’abstraction revient à écrire plusieurs méthodes, chaque méthode réalisant une tâche spécifique. Les classes elle-mêmes peuvent être regroupées en packages, qui constituent une nouvelle barrière d’abstraction. La bibliothèque de Java, qui est énorme, est structurée en packages.
Le découpage en packages, puis en classes, puis en méthodes, qui interagissent selon des conventions claires qui disent le quoi et cachent les détails du comment, est un fondement de la bonne programmation, c’est-à-dire de la production de programmes compréhensibles et donc de programmes qui sont (plus) facilement mis au point, puis modifiés.
Il y a en Java quatre niveaux de visibilité, du plus restrictif au plus permissif.
Nous connaissons déjà quelques emplois de public
.
main
soit déclarée public
.
Noter que déclarer public
les autres méthodes de vos classes
n’a aucun sens, à moins d’être train d’écrire un package.
public
(ou très rarement protected
).
La plus grande part de la puissance de la programmation objet
réside dans le mécanisme de l’héritage.
Comme première approche, nous examinons rapidement l’héritage et seulement
du point de vue de la classe.
Soit une classe Foo
qui hérite d’une classe Bar
.
La classe Foo
démarre dans la vie avec toutes les
variables et toutes les méthode de la classe Bar
.
Mais la classe Foo
ne va pas se contenter de démarrer dans la
vie, elle peut effectivement étendre la classe dont elle hérite
en se donnant de nouvelles méthodes et de nouvelles variables.
Elle peut aussi redéfinir les méthodes et variables héritées.
Par exemple, on peut construire une classe HelloGoodBye
qui
offre un message d’adieu en plus du message de bienvenue de
Hello
, et garantit que les deux messages sont dans la même
langue.
On note que deux méthodes sont redéfinies
(setEnglish
et setFrench
) et qu’une méthode est ajoutée
(getGoodBye
).
La méthode setEnglish
ci-dessus doit, pour assurer la
cohérence des deux messages, appeler la méthode setEnglish
de la classe Hello
, d’où l’emploi d’une notation complète
Hello.setEnglish
.
Comme démontré par la nouvelle et dernière classe Simple
,
la classe HelloGoodBye
a bien reçu la méthode getHello
en héritage.
En fait, l’héritage des classes n’a que peu d’intérêt en pratique, l’héritage des objets est bien plus utile.