1 Présentation de Java Après un très bref historique du langage Java montrant dans quel esprit il a été créé, nous en présenterons les principales caractéristiques. Nous verrons tout d’abord que Java est un langage objet et nous exposerons les concepts majeurs de la Programmation Orientée Objets. Puis nous ferons la distinction entre les programmes utilisant une interface console et les programmes utilisant une interface graphique, ce qui nous amènera à parler des possibilités de programmation événementielle qui sont offertes par Java sous la forme de classes standard. Enfin, nous montrerons que Java est le premier langage à offrir une portabilité aussi avancée. 1 Petit historique du langage On peut faire remonter la naissance de Java à 1991. A cette époque, des ingénieurs de chez SUN ont cherché à concevoir un langage applicable à de petits appareils électriques (on parle de code embarqué). Pour ce faire, ils se sont fondés sur une syntaxe très proche de celle de C++, en reprenant le concept de machine virtuelle déjà exploité auparavant par le Pascal UCSD. L’idée consistait à traduire d’abord un programme source, non pas directement en langage machine, mais dans un pseudo langage universel, disposant des fonctionnalités communes à toutes les machines. Ce code intermédiaire, dont on dit qu’il est formé de byte codes1, se trouve ainsi compact et portable sur n’importe quelle machine ; il suffit simplement que cette dernière dispose d’un programme approprié (on parle alors de machine virtuelle) permettant de l’interpréter dans le langage de la machine concernée. 1. Conformément à la tradition, nous n’avons pas cherché à traduire ce terme. 6 Présentation de Java CHAPITRE 1 En fait, ce projet de langage pour code embarqué n’a pas abouti en tant que tel. Mais ces concepts ont été repris en 1995 dans la réalisation du logiciel HotJava, un navigateur Web écrit par SUN en Java, et capable d’exécuter des applets écrits précisément en byte codes. Les autres navigateurs Web ont suivi, ce qui a contribué à l’essor du langage qui a beaucoup évolué depuis cette date, sous forme de versions successives : 1.01 et 1.02 en 1996, 1.1 en 98 et 1.2 (finalement rebaptisée Java 2) en 1999, 1.3 en 2000, 1.4 en 2002, 1.5 en 2004 (toujours appelées Java 2). 2 Java et la Programmation Orientée Objets La P.O.O. (Programmation Orientée Objets) possède de nombreuses vertus universellement reconnues désormais. Notamment, elle ne renie pas la programmation structurée (elle se fonde sur elle), elle contribue à la fiabilité des logiciels et elle facilite la réutilisation de code existant. Elle introduit de nouveaux concepts, en particulier ceux d’objets, d’encapsulation, de classe et d’héritage. 2.1 Les concepts d’objet et d’encapsulation En programmation structurée, un programme est formé de la réunion de différentes procédures et de différentes structures de données généralement indépendantes de ces procédures. En P.O.O., un programme met en oeuvre différents objets. Chaque objet associe des données et des méthodes agissant exclusivement sur les données de l’objet. Notez que le vocabulaire évolue quelque peu : on parlera de méthodes plutôt que de procédures ; en revanche, on pourra utiliser indifféremment le mot données ou le mot champ. Mais cette association est plus qu’une simple juxtaposition. En effet, dans ce que l’on pourrait qualifier de P.O.O. "pure", on réalise ce que l’on nomme une encapsulation des données. Cela signifie qu’il n’est pas possible d’agir directement sur les données d’un objet ; il est nécessaire de passer par ses méthodes, qui jouent ainsi le rôle d’interface obligatoire. On traduit parfois cela en disant que l’appel d’une méthode est en fait l’envoi d’un message à l’objet. Le grand mérite de l’encapsulation est que, vu de l’extérieur, un objet se caractérise uniquement par les spécifications de ses méthodes, la manière dont sont réellement implantées les données étant sans importance. On décrit souvent une telle situation en disant qu’elle réalise une abstraction des données (ce qui exprime bien que les détails concrets d’implémentation sont cachés). A ce propos, on peut remarquer qu’en programmation structurée, une procédure pouvait également être caractérisée (de l’extérieur) par ses spécifications, mais que, faute d’encapsulation, l’abstraction des données n’était pas réalisée. L’encapsulation des données présente un intérêt manifeste en matière de qualité de logiciel. Elle facilite considérablement la maintenance : une modification éventuelle de la structure des données d’un objet n’a d’incidence que sur l’objet lui-même ; les utilisateurs de l’objet ne seront pas concernés par la teneur de cette modification (ce qui n’était bien sûr pas le cas 2 - Java et la Programmation Orientée Objets avec la programmation structurée). De la même manière, l’encapsulation des données facilite grandement la réutilisation d’un objet. 2.2 Le concept de classe Le concept de classe correspond simplement à la généralisation de la notion de type que l’on rencontre dans les langages classiques. En effet, une classe n’est rien d’autre que la description d’un ensemble d’objets ayant une structure de données commune et disposant des mêmes méthodes. Les objets apparaissent alors comme des variables d’un tel type classe (en P.O.O., on dit aussi qu’un objet est une instance de sa classe). Bien entendu, seule la structure est commune, les valeurs des champs étant propres à chaque objet. En revanche, les méthodes sont effectivement communes à l’ensemble des objets d’une même classe. Lorsque, comme cela arrive parfois dans l’écriture d’interfaces graphiques, on est amené à ne créer qu’un seul objet d’une classe donnée, la distinction entre les notions d’objet et de classe n’est pas toujours très évidente. En revanche, lorsque l’on dispose de plusieurs objets d’une même classe, le principe d’encapsulation s’appliquera à la classe et non à chacune de ses instances, comme nous le verrons. 2.3 L’héritage Un autre concept important en P.O.O. est celui d’héritage. Il permet de définir une nouvelle classe à partir d’une classe existante (qu’on réutilise en bloc !), à laquelle on ajoute de nouvelles données et de nouvelles méthodes. La conception de la nouvelle classe, qui hérite des propriétés et des aptitudes de l’ancienne, peut ainsi s’appuyer sur des réalisations antérieures parfaitement au point et les spécialiser à volonté. Comme on peut s’en douter, l’héritage facilite largement la réutilisation de produits existants, d’autant plus qu’il peut être réitéré autant de fois que nécessaire (la classe C peut hériter de B, qui elle-même hérite de A). Cette technique s’appliquera aussi bien aux classes que vous serez amenés à développer qu’aux très nombreuses classes (plus de 1700) fournies en standard avec Java 2. Certains langages, tels C++, offrent la possibilité d’un héritage multiple : une même classe peut hériter simultanément de plusieurs autres. Ce n’est pas le cas de Java, mais nous verrons que la notion d’interface permet de traiter plus élégamment les situations correspondantes. 2.4 Java est presque un pur langage de P.O.O. Certains langages ont été conçus pour appliquer à la lettre les principes de P.O.O. C’est notamment le cas de Simula, Smalltalk et de Eiffel. Dans ce cas, tout est objet (ou instance de classe) et l’encapsulation des données est absolue. Les procédures sont obligatoirement des méthodes, ce qui revient à dire qu’il n’existe pas de procédures indépendantes, c’est-à-dire susceptibles de s’exécuter indépendamment de tout objet. 7