IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Thinking in Java, 3rd ed. Revision 4.0


précédentsommairesuivant

II. Tout est objet

Bien qu'il soit basé sur C++, Java est un langage orienté objet plus « pur ».

C++ et Java sont tous les deux des langages hybrides, mais dans Java, les concepteurs ont pensé que l'hybridation n'était pas aussi importante qu'elle ne l'était en C++. Un langage hybride permet plusieurs styles de programmation : C++ est hybride pour assurer la compatibilité avec le langage C. Comme C++ est une extension du langage C, il contient un grand nombre des particularités indésirables de ce langage, ce qui peut rendre certains aspects du C++ excessivement compliqués.

Le langage Java suppose qu'on ne veut faire que de la programmation orientée objet (POO). Ceci signifie qu'avant de pouvoir commencer, il faut changer sa façon de voir les choses en un monde orienté objet (à moins qu'elle ne le soit déjà). L'avantage de cet effort initial est la capacité à programmer dans un langage qui est plus simple à apprendre et à utiliser que beaucoup d'autres langages de POO. Dans ce chapitre, nous verrons les éléments de base d'un programme Java et nous apprendrons que tout est objet dans Java, même un programme Java.

II-A. Les objets sont manipulés via des références

Chaque langage de programmation possède ses propres moyens de manipulation de données. Dans certains cas, le programmeur doit être constamment conscient du type de manipulation en cours. Manipule-t-on l'objet directement, ou a-t-on affaire à une sorte de représentation indirecte (un pointeur en C ou C++) qui doit être traitée avec une syntaxe particulière ?

Tout ceci est simplifié en Java. On considère tout comme un objet en utilisant une syntaxe cohérente et unique. Bien qu'on traite tout comme un objet, l'identificateur qui est manipulé est en fait une « référence » vers un objet. (10) On pourrait imaginer cette situation comme une télévision (l'objet) avec une télécommande (la référence). Tant qu'on conserve cette référence, on a une liaison vers la télévision, mais quand quelqu'un dit « change de chaîne » ou « baisse le volume », ce qu'on manipule est la référence, qui en retour modifie l'objet. Si on veut se déplacer dans la pièce tout en contrôlant la télévision, on emporte avec soi la télécommande/référence, pas la télévision.

De plus, la télécommande peut exister par elle-même sans télévision. Ainsi, le fait d'avoir une référence ne signifie pas nécessairement qu'un objet y soit associé. Ainsi, si on veut posséder un mot ou une phrase, on crée une référence à une Chaîne de caractère (String) :

 
Sélectionnez
String s;

Mais ici, on a seulement créé la référence, pas un objet. Dans cette situation, si on décidait d'envoyer un message à s, on obtiendrait une erreur (lors de l'exécution) parce que s n'est en rattaché à rien (il n'y a pas de télévision). Une pratique plus sûre est donc de toujours initialiser une référence quand on la crée :

 
Sélectionnez
String s = "asdf";

Toutefois, ceci utilise une particularité de Java : les chaînes de caractères peuvent être initialisées avec du texte entre guillemets. Normalement, on doit utiliser un type d'initialisation plus général pour les objets.

II-B. Vous devez créer tous les objets

Quand vous créez une référence, vous voulez la connecter avec un nouvel objet. Vous le faites, en général, avec le mot clé new. Le mot clé new signifie, « fabrique-moi un de ces objets ». Dans l'exemple précédent, vous pouvez dire:

 
Sélectionnez
String s = new String("asdf");

Non seulement cela veut dire « fabrique-moi un nouveau String », mais cela donne aussi une information sur comment faire le String en apportant une chaîne de caractères initiale.

Bien sûr, String n'est pas le seul type qui existe. Java est livré avec une pléthore de types préconçus. Le plus important est que vous pouvez créer vos propres types. En fait, c'est l'activité fondamentale en programmation Java, et c'est ce que vous allez apprendre dans le reste de ce livre.

II-B-1. Où réside la mémoire

Il est utile de visualiser quelques aspects de l'organisation des choses lorsque le programme tourne, en particulier comment la mémoire est gérée. Il y a six différents endroits où stocker des données :

  • Les Registres. C'est le stockage le plus rapide, car il se situe à un endroit différent des autres : à l'intérieur du processeur. Cependant, le nombre de registres est sévèrement limité, ainsi les registres sont assignés par le compilateur en fonction de ses besoins. Vous n'avez pas le contrôle direct, vous ne voyez même pas dans vos programmes le moindre indice que les registres existent.
  • La pile. Elle se trouve dans la partie générale de la mémoire vive (random-access memory, RAM), mais est gérée directement par le processeur via son pointeur de pile. Le pointeur de pile est déplacé vers le bas pour créer de la place mémoire et remonté pour libérer cette mémoire. C'est un moyen extrêmement rapide et efficace d'allouer de la mémoire, juste en deuxième position derrière les registres. Le compilateur Java doit savoir, quand il est en train de créer un programme, la taille exacte et la durée de vie de toutes les données qui sont stockées dans la pile, car il doit générer le code pour déplacer le pointeur de pile vers le haut et vers le bas. Cette contrainte trace les limites de la flexibilité de vos programmes, ainsi bien que java utilise un stockage sur la pile - en particulier les références des objets - les objets Java eux-mêmes ne sont pas placés sur la pile.
  • Le tas. C'est un endroit mémoire à tout faire (également dans la RAM) où se trouvent tous les objets Java. Le truc bien au sujet du tas est que, à la différence de la pile, le compilateur n'a pas besoin de savoir combien d'espace il doit allouer dans le tas ou combien de temps cet espace doit rester sur le tas. Ainsi, il y a beaucoup de flexibilité à employer le stockage sur le tas. À chaque fois que vous devez créer un objet, vous écrivez simplement le code pour le créer en employant new et le stockage est assigné sur le tas quand ce code est exécuté. Naturellement il y a un prix à payer pour cette flexibilité. Cela prend plus de temps pour allouer de l'espace sur le tas que pour allouer de l'espace sur la pile (dans l'hypothèse où vous pourriez créer des objets sur la pile en Java, comme vous pouvez le faire en C++).
  • Stockage statique. « Statique » est employé ici dans le sens de « dans un endroit fixe » (bien que se soit également dans la RAM). Le stockage statique contient les données qui sont disponibles pour la durée totale du temps d'exécution d'un programme. Vous pouvez employer le mot-clé static pour indiquer qu'un élément particulier d'un objet est statique, mais les objets Java eux-mêmes ne sont jamais placés dans le stockage statique.
  • Stockage constant. Les valeurs constantes sont souvent placées directement dans le code, ce qui est sûr puisqu'elles ne peuvent jamais changer. Parfois des constantes sont regroupées en ensembles de sorte qu'elles puissent être éventuellement placées dans de la mémoire morte (ROM) dans les systèmes embarqués.
  • Stockage hors-RAM. Si les données sont stockées complètement hors du programme, elles peuvent exister alors que le programme ne fonctionne pas, hors de son contrôle. Les deux principaux exemples de ce cas sont les flux de données, dans lesquels les objets sont transformés en flux d'octets, généralement pour être envoyés vers une autre machine, et les objets persistants, dans lesquels les objets sont écrits sur disque afin de conserver leur état même lorsque le programme se termine. L'astuce avec ces types est de transformer les objets en quelque chose qui peut exister sur l'autre support et être ensuite retransformé en un objet normal en mémoire, lorsque c'est nécessaire. Java offre des outils pour une persistance légère et de futures versions de Java pourraient offrir une solution plus complète.

II-B-2. Cas particulier: les types primitifs

Une famille de types, que vous utiliserez assez souvent dans vos développements, jouit d'un traitement particulier. On peut considérer ces types comme des types « primitifs ». La raison de ce traitement spécial est que créer un objet avec new, surtout une petite et simple variable, n'est pas très efficace, car new ajoute l'objet au sommet tas. Pour ces types, java revient à une approche choisie par C et C++. La méthode consiste, au lieu de créer une variable en utilisant new, à créer une variable « automatique » qui n'est pas une référence. Cette variable contient la valeur et est placée sur la pile, ce qui est bien plus efficace.

Java fixe la taille de chaque type primitif. Contrairement à d'autres langages, la taille de ces types est indépendante de la plate-forme, elle est identique sur toutes. Cette invariance est une des raisons auxquelles on doit la portabilité des programmes Java.

Type primitif

Taille

Minimum

Maximum

Classe enveloppe

boolean

-

-

-

Boolean

char

16-bit

Unicode 0

Unicode 216-1

Character

byte

8-bit

-128

+127

Byte

short

16-bit

-215

+215-1

Short

int

32-bit

-231

+231-1

Integer

long

64-bit

-263

+263-1

Long

float

32-bit

IEEE754

IEEE754

Float

double

64-bit

IEEE754

IEEE754

Double

void

-

-

-

Void

Tous les types numériques ont un signe, ne cherchez donc de type « unsigned » (dépourvu de signe).

La taille du boolean(booléen) n'est pas spécifiée, elle est uniquement définie pour pouvoir prendre les valeurs littérales true ou false.

Les classes « enveloppes » des types primitifs permettent de créer des objets non primitifs sur la pile, pour représenter un type primitif. Par exemple:

 
Sélectionnez
char c = 'x';
Character C = new Character(c);

On pourrait aussi utiliser:

 
Sélectionnez
Character C = new Character('x');

Les raisons pour lesquelles on fait ceci seront expliquées dans des chapitres ultérieurs.

II-B-2-a. Les nombres de haute précision

Java inclut deux classes pour l'arithmétique de haute précision: BigInteger et BigDecimal Bien que celles-ci entrent à peu près dans la même catégorie que les classes « enveloppes », aucune d'entre elles n'a de type primitif correspondant.

Les deux classes ont des méthodes pour les opérations que l'on effectue sur les types primitifs. Cela étant, on peut faire avec BigInteger et BigDecimal tout ce que l'on fait avec int ou float, il s'agira simplement d'utiliser des méthodes en lieu et place des opérateurs. Cependant il y a des conséquences: les opérations seront plus lentes. On troque alors la vitesse contre la précision.

BigInteger prend en charge des entiers de précision arbitraire. Cela signifie que vous pouvez représenter avec précision des valeurs entières de toutes tailles sans perdre de précision durant les opérations.

BigDecimal est destiné aux nombres décimaux à virgule fixe: vous pouvez utiliser ce type pour les calculs monétaires précis entre autres.

Consulter la documentation du JDK pour plus de détail concernant les constructeurs et les méthodes disponibles pour ces deux classes.

II-B-3. Les tableaux en Java

Virtuellement, tous les langages de programmation supportent les tableaux. L'utilisation des tableaux en C et C++ est périlleuse parce que ces tableaux ne sont que des blocs de mémoire. Si un programme accède au tableau en dehors de son bloc de mémoire ou utilise la mémoire avant l'initialisation (des erreurs de programmation classiques), on s'expose à des résultats imprévisibles.

Un des buts premiers de java est la sécurité, ainsi un grand nombre de problèmes contre lesquels les programmeurs C et C++ pestent n'existent plus en Java. Un tableau en Java est à coup sûr initialisé et on ne peut y accéder en dehors de ses bornes. La vérification de ces bornes a un coût: l'existence d'une petite quantité supplémentaire de mémoire allouée pour chaque tableau,et la vérification systématique des index lors de l'exécution. L'idée est que la sécurité et l'augmentation de productivité valent bien ces concessions.

Quand on crée un tableau d'objets, on crée en réalité un tableau de références et chacune de ces références est automatiquement initialisée avec une valeur spéciale ayant son propre mot clé: null. Quand Java rencontre null, il sait que ladite référence ne pointe pas sur un objet. On doit associer un objet à chaque référence avant de l'utiliser, et si on utilise une référence qui est encore null, le problème sera rapporté au moment de l'exécution. De cette manière, les erreurs typiques des tableaux sont évitées en Java.

Il est également possible de créer un tableau d'éléments de types primitifs. Là encore, le compilateur garantit l'initialisation, parce qu'il remplit de zéros l'espace mémoire de ce tableau.

Les tableaux seront traités en détail dans les chapitres à venir.

II-C. Vous n'avez jamais besoin de détruire un objet

Dans la majorité des langages de programmation, le concept de durée de vie d'une variable occupe une part significative de l'effort de programmation. Combien de temps une variable dure-t-elle ? Si vous êtes censé la détruire, quand devez-vous le faire ? La confusion sur les durées de vie des variables peut conduire à de nombreux bugs, et cette section montre comment Java simplifie considérablement le problème en faisant pour vous tout le travail de nettoyage.

II-C-1. La portée

La plupart des langages procéduraux ont le concept de portée. Celui-ci détermine à la fois la visibilité et la durée de vie des noms définis dans cette portée. En C,C++ et Java, cette portée est déterminée par les crochets « { » et « } ». Par exemple:

 
Sélectionnez
{
  int x = 12;
  // Seul x est accessible
  {
    int q = 96;
    // x et q sont disponibles.
  }
  // Seul x est accessible
  // q est "hors de portée"
}

Une variable définie à l'intérieur d'un couple {} n'est accessible que jusqu'au crochet fermant }.

Tout texte précédé d'un '//' est un commentaire.

L'indentation rend la lecture de code Java plus aisée. Puisque Java ne tient pas compte de la mise en forme, les espaces surnuméraires, tabulations et retours de chariot n'affectent pas le résultat de la compilation.

Notez qu'on ne peut pas faire ce qui suit, bien que cela soit accepté en C et C++:

 
Sélectionnez
{
    int x = 12;
    {
        int x = 96; // Illégal
    }
}

Le compilateur renverra que la variable x a déjà été définie. Ainsi, la capacité de C et C++ de « cacher » la variable ayant une portée plus grande n'est pas permise, parce que les concepteurs du langage ont estimé que cela conduisait à des programmes confus.

II-C-2. Portée des objets

Les objets Java n'ont pas la même durée de vie que les primitives. Quand on crée un objet avec new, il reste vivant après avoir atteint la fin de sa portée. Ainsi, si on utilise:

 
Sélectionnez
{
    String s = new String("a string");
} // Fin de la portée

la référence s disparaît après le « } ». Cependant, l'objet String sur lequel s pointait demeure et occupe toujours de la mémoire. Dans cet extrait de code, il n'y a aucun moyen d'accéder à l'objet, car sa seule référence est hors de portée. Dans des chapitres ultérieurs, nous verrons comment la référence à cet objet peut être transmise et dupliquée durant l'exécution du programme.

Parce que les objets créés avec new persistent aussi longtemps que l'on a besoin d'eux, toute une série de problèmes rencontrés en C++ disparait en Java. Les problèmes les plus difficiles semblent exister en C++ parce que le langage ne fournit aucune aide permettant de déterminer si un objet est disponible quand on en a besoin. Plus important encore, en C++ il faut s'assurer que l'on détruit les objets quand on en a fini avec eux.

Cela soulève une question intéressante. Si Java conserve les objets, qu'est-ce qui les empêche de remplir la mémoire et d'arrêter le programme ? C'est exactement le genre de problème qui arriverait en C++. Et c'est là qu'un peu de magie intervient. Java possède un ramasse-miettes (garbage collector) qui surveille tous les objets créés par new et détermine lesquels ne sont plus référencés. Dans ce cas, il libère la mémoire qui était allouée afin qu'elle puisse être utilisée pour de nouveaux objets. Cela signifie que l'on n'a jamais à se soucier soi-même de demander de la mémoire. On crée simplement des objets et quand on n'en aura plus besoin, il disparaîtront d'eux-mêmes. Ceci élimine un problème fréquent: la « fuite de mémoire » qui a lieu quand un développeur oublie de libérer de la mémoire.

II-D. Créer de nouveaux types de données : class

Si tout est objet, qu'est-ce qui définit à quoi ressemble une classe particulière d'objets et comment elle se comporte ? Autrement dit, qu'est-ce qui constitue le type d'un objet ? On pourrait s'attendre à avoir un mot-clef appelé « type », et cela serait parfaitement sensé. Historiquement, toutefois, la plupart des langages orientés objet ont utilisé le mot-clef class qui signifie « je vais décrire à quoi ressemble un nouveau type d'objet ». Le mot-clef class (qui est si commun qu'il ne sera plus mis en gras dans la suite de ce livre) est suivi par le nom du nouveau type. Par exemple :

 
Sélectionnez
class ATypeName { /* le corps de la classe vient ici */ }

Ceci introduit un nouveau type, le corps de la classe étant juste constitué d'un commentaire (les caractères « étoile » (*) et « slash » (/) ainsi que ce qui est contenu entre ces caractères seront détaillés plus loin dans ce chapitre), ce qui fait qu'il n'y a pas grand-chose que vous puissiez faire avec. Malgré tout, vous pouvez créer un objet de ce type en utilisant new :

 
Sélectionnez
ATypeName a = new ATypeName();

Mais vous ne pouvez pas lui demander de faire quoi que ce soit (du fait que vous ne puissiez pas lui envoyer de message intéressant) jusqu'à ce que vous ayez défini des méthodes pour cet objet.

II-D-1. Champs et méthodes

Lorsque l'on définit une classe (et tout ce que l'on fait en Java consiste à définir des classes, fabriquer des objets à partir de ces classes et envoyer des messages à ces objets) on peut mettre deux types d'éléments dans ces classes : des données membres de la classe (aussi appelées champs ou attributs) et des fonctions membres de la classe (habituellement appelées méthodes). Un attribut est un objet de n'importe quel type avec lequel on peut communiquer via sa référence. Il peut aussi s'agir d'un des types primitifs (dans ce cas, ce n'est pas une référence). S'il s'agit d'une référence à un objet, il faut initialiser cette référence avant de la référencer dans l'objet réel (en utilisant new comme indiqué précédemment) et ceci grâce à une méthode particulière appelée constructeur (entièrement décrit dans le chapitre 4). S'il s'agit d'un type primitif, il est possible de l'initialiser directement lors de sa définition dans la classe (comme on le verra plus tard, les références peuvent aussi être initialisées lors de la définition).

Chaque objet gère ses attributs dans sa propre zone de mémoire ; les attributs ne sont pas partagés entre les objets. Voici un exemple de classe avec des attributs :

 
Sélectionnez
class DataOnly {
    int i;
    float f;
    boolean b;
}

Cette classe ne fait rien, mais on peut créer un objet :

 
Sélectionnez
DataOnly d = new DataOnly();

On peut affecter des valeurs aux attributs, mais il faut d'abord savoir comment faire référence à un attribut d'un objet. Ceci s'effectue en indiquant le nom de la référence à l'objet, suivi par un point, suivi par le nom du membre dans l'objet :

 
Sélectionnez
objectReference.member

Par exemple :

 
Sélectionnez
d.i = 47;
d.f = 1.1f; // 'f' référence une constante de type 'float'
d.b = false;

Il est aussi possible que l'objet puisse contenir d'autres objets qui contiennent des données que l'on souhaite modifier. Pour cela il suffit de continuer à « associer les points ». Par exemple :

 
Sélectionnez
myPlane.leftTank.capacity = 100;

La classe DataOnly ne peut pas faire grand-chose à part contenir des données, car elle n'a pas de méthodes. Pour comprendre comment celles-ci fonctionnent, il faut d'abord comprendre les notions de paramètres et de valeurs de retour, qui seront brièvement décrites.

II-D-1-a. Valeurs par défaut des attributs

Quand une donnée d'un type primitif est un attribut d'une classe, on est assuré qu'elle a une valeur par défaut si on ne l'initialise pas :

Primitive type

Default

boolean

false

char

'\u0000' (null)

byte

(byte)0

short

(short)0

int

(int)0

long

0L

float

0.0f

double

0.0d

Par prudence, il faut remarquer que les valeurs par défaut sont celles que Java garantit quand la variable est utilisée comme un membre d'une classe. Ceci assure que les variables membres de type primitif sont toujours initialisées (quelque chose que C++ ne fait pas), ce qui supprime une source de bugs. Toutefois, cette valeur initiale peut ne pas être correcte ou même légale pour le programme qui est écrit. Il est préférable de toujours initialiser explicitement les variables.

Cette garantie ne s'applique pas aux variables « locales » -- celles qui ne sont pas des champs d'une classe. Ainsi, si dans la définition d'une méthode, on a :

 
Sélectionnez
int x;

Alors x aura une valeur arbitraire (comme en C et C++), il ne sera pas initialisé automatiquement à zéro. On a la responsabilité d'affecter une valeur appropriée avant d'utiliser x. Si on oublie de le faire, Java est sans aucun doute mieux conçu que C++ sur ce point : on obtient une erreur de compilation qui dit que la variable pourrait ne pas être initialisée (avec beaucoup de compilateurs C++ on a des avertissements concernant les variables non initialisées, mais avec Java ce sont des erreurs).

II-E. Méthodes, paramètres et valeurs de retour

Dans de nombreux langages (comme C et C++), le terme fonction est employé pour désigner une sous-routine nommée. Le terme qui est plus généralement employé en Java est méthode, en tant que « moyen de faire quelque chose ». Il est possible, si on le souhaite, de continuer à raisonner en terme de fonctions. Il s'agit simplement d'une différence de syntaxe, mais on utilisera « méthode » plutôt que fonction dans ce livre.

Les méthodes en Java définissent les messages qu'un objet peut recevoir. Dans cette partie, on verra à quel point il est simple de définir une méthode.

Les éléments fondamentaux d'une méthode sont le nom, les paramètres, le type de retour et le corps. Voici la forme de base :

 
Sélectionnez
returnType methodName( /* liste de paramètres */ ) {
    /* corps de la méthode */
}

Le type de retour est le type de la valeur qui est retournée par la méthode après son appel. La liste de paramètres donne le type et le nom des informations qu'on souhaite passer à la méthode. L'association du nom de la méthode et de la liste de paramètres identifie de façon unique la méthode.

En Java, les méthodes ne peuvent être créées que comme une composante d'une classe. Une méthode ne peut être appelée que pour un objet (11) et cet objet doit être capable de réaliser cet appel de méthode. Si on essaye d'appeler une mauvaise méthode pour un objet, on obtient un message d'erreur lors de la compilation. On appelle une méthode pour un objet en nommant l'objet suivi d'un point, suivi du nom de la méthode et de sa liste d'arguments, comme ça :

 
Sélectionnez
objectName.methodName(arg1, arg2, arg3);

Par exemple, si on suppose qu'on a une méthode f( ) qui ne prend aucun paramètre et qui retourne une valeur de type int. Alors, si on a un objet appelé a pour lequel f( ) peut être appelée, on peut écrire :

 
Sélectionnez
int x = a.f();

Le type de la valeur de retour doit être compatible avec le type de x.

On appelle généralement envoyer un message à un objet cet acte d'appeler une méthode. Dans l'exemple précédent, le message est f( ) et l'objet est a. La programmation orientée objet est souvent simplement ramenée à « envoyer des messages à des objets ».

II-E-1. La liste de paramètres

La liste de paramètres de la méthode spécifie quelles informations on passe à la méthode. Comme on peut le supposer, ces informations -- comme tout le reste en Java -- sont sous la forme d'objets. On doit donc indiquer dans la liste de paramètres les types des objets à transmettre et les noms à employer pour chacun. Comme dans toutes les situations où on a l'impression de manipuler des objets, en Java on passe effectivement des références. (12) Toutefois, le type de la référence doit être correct. Si le paramètre est censé être un objet de type String, ce qu'on transmet doit être de type String sinon le compilateur génère une erreur.

Considérons une méthode qui prend un objet de classe String en paramètre. Voici la définition qui doit être mise à l'intérieur de la définition d'une classe pour qu'elle soit compilée :

 
Sélectionnez
int storage(String s) {
    return s.length() * 2;
}

Cette méthode indique combien d'octets sont nécessaires pour contenir une String donnée. (Chaque char dans une String fait 16 bits, ou deux octets, pour permettre les caractères Unicode.) Le paramètre est de type String et il est appelé s. Une fois que s est passé à une méthode, il peut être traité comme n'importe quel autre objet. (On peut lui envoyer des messages). Ici, on appelle la méthode length( ) qui est une des méthodes de la classe String ; elle retourne le nombre de caractères que contient la chaîne.

On peut aussi voir l'utilisation du mot-clef return qui fait deux choses. D'abord, il signifie « quitte la méthode, j'ai terminé ». Ensuite, si la méthode retourne une valeur, cette valeur est placée juste après la déclaration du return. Dans le cas présent, la valeur de retour est produite en évaluant l'expression s.length( ) * 2.

On peut retourner des valeurs de tous les types qu'on souhaite, mais si on souhaite ne rien retourner du tout on peut le faire en indiquant que la méthode retourne void. Voici quelques exemples :

 
Sélectionnez
boolean flag() { return true; }
float naturalLogBase() { return 2.718f; }
void nothing() { return; }
void nothing2() {}

Quand le type de retour est void, alors le mot-clef return n'est utilisé que pour sortir de la méthode, il n'est donc pas nécessaire quand on atteint la fin de la méthode. On peut retourner d'une méthode à n'importe quel endroit, mais si on a indiqué un type de retour qui n'est pas void alors le compilateur imposera (avec des messages d'erreur) un retour avec une valeur d'un type approprié sans tenir compte de l'endroit auquel le retour se produit.

À ce point, on peut penser qu'un programme n'est qu'un paquet d'objets avec des méthodes qui prennent d'autres objets en paramètres pour transmettre des messages à ces autres objets. C'est effectivement l'essentiel de ce qui se passe, mais dans les chapitres suivants on verra comment faire le travail de bas niveau en prenant des décisions au sein d'une méthode. Pour ce chapitre, envoyer des messages est suffisant.

II-F. Construction d'un programme Java

Il y a plusieurs autres éléments à comprendre avant de voir le premier programme Java.

II-F-1. Visibilité des noms

Un problème commun à tous les langages de programmation est le contrôle des noms. Si on utilise un nom dans un module du programme et si un autre programmeur utilise le même nom dans un autre module, comment distingue-t-on un nom d'un autre et comment empêche-t-on les « collisions » de noms ? En C c'est un problème particulier, car un programme est souvent un océan de noms incontrôlable. Les classes C++ (sur lesquelles les classes Java sont basées) imbriquent les fonctions dans les classes de telle sorte qu'elles ne peuvent pas entrer en collision avec les noms de fonctions imbriqués dans d'autres classes. Toutefois, C++ autorise toujours les données et les fonctions globales, donc les collisions sont toujours possibles. Pour résoudre ce problème, C++ a introduit les domaines de noms (namespace) en utilisant des mots-clefs supplémentaires.

Java a pu éviter tout cela en employant une approche originale. Pour générer sans ambiguïté un nom pour une bibliothèque, le spécificateur utilisé n'est pas très différent d'un nom de domaine Internet. En fait, les créateurs de Java veulent qu'on utilise son propre nom de domaine Internet inversé, puisqu'on est assuré que ceux-ci sont uniques. Puisque mon nom de domaine est BruceEckel.com, ma bibliothèque d'utilitaires (utility) pour mes marottes (foibles) devrait être appelée com.bruceeckel.utility.foibles. Après avoir inversé le nom de domaine, les points sont destinés à représenter des sous-répertoires.

Dans Java 1.0 et Java 1.1 les extensions de domaines com, edu, org, net, etc. étaient mises en lettres capitales par convention, ainsi la bibliothèque serait : COM.bruceeckel.utility.foibles. Toutefois, au cours du développement de Java 2, on s'est rendu compte que cela causait des problèmes et par conséquent les noms de packages sont entièrement en lettres minuscules.

Ce mécanisme signifie que tous les fichiers existent automatiquement dans leur propre domaine de nom et toutes les classes contenues dans un fichier donné doivent avoir un identificateur unique. Ainsi, on n'a pas besoin d'apprendre de particularités spécifiques au langage pour résoudre ce problème -- le langage s'en occupe à votre place.

II-F-2. Utilisation d'autres composantes

Lorsqu'on souhaite utiliser une classe prédéfinie dans un programme, le compilateur doit savoir comment la localiser. Bien entendu, la classe pourrait déjà exister dans le même fichier source que celui d'où elle est appelée. Dans ce cas on utilise simplement la classe - même si la classe n'est définie que plus tard dans le fichier. (Java élimine le problème des « références anticipées », il n'y a donc pas à s'en préoccuper).

Qu'en est-il des classes qui existent dans un autre fichier ? On pourrait penser que le compilateur devrait être suffisamment intelligent pour aller simplement la chercher lui même, mais il y a un problème. Imaginons que l'on veuille utiliser une classe ayant un nom spécifique, mais qu'il existe plus d'une classe ayant cette définition (il s'agit probablement de définitions différentes). Ou pire, imaginons que l'on écrive un programme et qu'en le créant on ajoute à sa bibliothèque une nouvelle classe qui entre en conflit avec le nom d'une classe déjà existante.

Pour résoudre ce problème, il faut éliminer les ambiguïtés potentielles. Ceci est réalisé en disant exactement au compilateur Java quelles classes on souhaite, en utilisant le mot-clef import. import dit au compilateur d'introduire un package qui est une bibliothèque de classes (dans d'autres langages, une bibliothèque pourrait comporter des fonctions et des données au même titre que des classes, mais il faut se rappeler que tout le code Java doit être écrit dans des classes).

La plupart du temps on utilise des composantes des bibliothèques Java standard qui sont fournies avec le compilateur. Avec celles-ci il n'y a pas à se tracasser à propos des longs noms de domaines inversés ; il suffit de dire, par exemple :

 
Sélectionnez
import java.util.ArrayList;

pour dire au compilateur que l'on veut utiliser la classe Java ArrayList. Toutefois, util contient de nombreuses classes et on pourrait vouloir utiliser plusieurs d'entre elles sans les déclarer explicitement. Ceci est facilement réalisé en utilisant '*' pour indiquer un joker :

 
Sélectionnez
import java.util.*;

Il est plus courant d'importer une collection de classes de cette manière que d'importer les classes individuellement.

II-F-3. Le mot-clef static

Normalement, quand on crée une classe, on décrit ce à quoi ressemblent les objets de cette classe et comment ils se comportent. Rien n'existe réellement avant de créer un objet de cette classe avec new, et à ce moment, la zone de données est créée et les méthodes deviennent disponibles.

Mais il y a deux situations pour lesquelles cette approche n'est pas suffisante. L'une, si l'on veut avoir une zone de stockage pour des données spécifiques, sans tenir compte du nombre d'objets créés, ou même si aucun objet n'a été créé. L'autre, si l'on a besoin d'une méthode qui n'est associée à aucun objet particulier de la classe. C'est-à-dire si on a besoin d'une méthode qui puisse être appelée même si aucun objet n'a été créé. On peut obtenir ces deux effets avec le mot-clef static. Dire que quelque chose est static signifie que la donnée ou la méthode n'est pas spécifiquement rattachée à un objet instance de cette classe. Donc, même si aucun objet de cette classe n'a jamais été créé il est possible d'appeler une méthode static ou d'accéder à une donnée static. Avec des données et des méthodes non static ordinaires, il faut connaître l'objet spécifique avec lequel elles fonctionnent. Bien entendu, étant donné que les méthodes static n'ont pas besoin qu'un objet soit créé avant d'être utilisées, elles ne peuvent pas accéder directement à des membres ou des méthodes non static en appelant ces autres membres sans faire référence à un objet nommé (puisque les membres et méthodes non static doivent être rattachés à un objet spécifique).

Certains langages orientés objet emploient les expressions données de classe et méthodes de classe, ce qui signifie que les données et les méthodes n'existent que pour la classe en tant que tout et pas pour des objets particuliers de la classe. Parfois la littérature Java utilise aussi ces expressions.

Pour rendre static une méthode ou une donnée membre il suffit de mettre le mot-clef static avant la définition. Par exemple, le code suivant crée une donnée membre static et l'initialise :

 
Sélectionnez
class StaticTest {
    static int i = 47;
}

Maintenant, même en créant deux objets StaticTest, il n'y aura qu'une seule zone de stockage pour StaticTest.i. Tous les objets partageront le même i. Considérons :

 
Sélectionnez
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();

A ce point, st1.i et st2.i ont la même valeur 47 puisqu'elles font référence à la même zone mémoire.

Il y a deux façons de faire référence à une variable static. Comme indiqué ci-dessus, il est possible de la nommer via un objet, en disant par exemple st2.i. Il est aussi possible d'y faire référence directement par le nom de la classe, ce qui ne peut pas être fait avec un membre non static (c'est le moyen de prédilection pour faire référence à une variable static puisque cela met en évidence la nature static de la variable).

 
Sélectionnez
StaticTest.i++;

L'opérateur ++ incrémente la variable. À ce point, st1.i et st2.i auront tous deux la valeur 48.

Une logique similaire s'applique aux méthodes statiques. On peut faire référence à une méthode statique soit par l'intermédiaire d'un objet, comme on peut le faire avec n'importe quelle méthode, ou avec la syntaxe spécifique supplémentaire ClassName.method( ). Une méthode statique est définie de façon similaire :

 
Sélectionnez
class StaticFun {
    static void incr() { StaticTest.i++; }
}

On peut voir que la méthode incr( ) de StaticFun incrémente la donnée static i avec l'opérateur ++. On peut appeler incr( ) de façon classique, par le biais d'un objet :

 
Sélectionnez
StaticFun sf = new StaticFun();
sf.incr();

Ou, parce que incr( ) est une méthode statique, il est possible de l'appeler directement par sa classe :

 
Sélectionnez
StaticFun.incr();

Alors que static, lorsqu'il est appliqué à une donnée membre, change sans aucun doute la façon dont la donnée est créée (une pour chaque classe par opposition à une pour chaque objet dans le cas des données non statiques), lorsqu'il est appliqué à une méthode, le changement est moins significatif. Un cas important d'utilisation des méthodes static est de permettre d'appeler cette méthode sans créer d'objet. C'est essentiel, comme nous le verrons, dans la définition de la méthode main( ) qui est le point d'entrée pour exécuter une application.

Comme pour toute méthode, une méthode static peut créer ou utiliser des objets nommés de son type, ainsi les méthodes static sont souvent utilisées comme « berger » pour un troupeau d'instances de son propre type.

II-G. Votre premier programme Java

Enfin, voici le premier programme complet. Il commence par afficher une chaine, puis une date, en utilisant la classe Date issue de la bibliothèque standard Java.

 
Sélectionnez
// HelloDate.java
import java.util.*;

public class HelloDate {
    public static void main(String[] args) {
        System.out.println("Hello, it's: ");
        System.out.println(new Date());
    }
}

Au début de chaque programme, il doit être défini une section import afin de charger toutes les classes supplémentaires nécessaires dans le code de la classe du fichier en cours. Il est à noter que j'ai employé le terme « supplémentaires ». C'est parce qu'il existe une bibliothèque qui est automatiquement chargée dans chaque fichier Java : java.lang. Démarrez votre navigateur Web et consultez la documentation de Sun. (Si vous n'avez pas téléchargé la documentation du JDK à partir de java.sun.com, faites-le maintenant). (13) Si vous consultez la liste des packages, vous pourrez identifier les différentes bibliothèques qui sont fournies avec Java. Choisissez java.lang. Cela devrait charger une liste de toutes les classes incluses dans cette bibliothèque. Depuis que java.lang est implicitement inclus dans chaque fichier de code Java, ces classes sont automatiquement disponibles. Il n'existe pas de classe Date dans java.lang, ce qui signifie que vous devez importer une autre bibliothèque pour l'utiliser. Si vous ne connaissez pas la bibliothèque dans laquelle est localisée une classe, ou si vous voulez voir toutes les classes, vous pouvez sélectionner « Tree » dans la documentation Java. Maintenant, vous pouvez trouver toutes les classes fournies avec Java. Ainsi, vous pouvez utiliser la fonction « find » de votre navigateur pour trouver la classe Date. Quand vous le faites, vous la voyez listée sous java.util.Date, ce qui vous permet de savoir que la classe est dans la bibliothèque util et que vous devez inclure import java.util.* pour utiliser Date.

Si vous retournez au début, choisissez java.lang puis System, vous pourrez alors constater que la classe System possède plusieurs champs, et si vous choisissez out, vous découvrirez que c'est un objet static PrintStream. Puisque c'est static, vous n'avez pas besoin de créer quoi que ce soit. L'objet out est toujours là, vous pouvez juste l'utiliser. Ce que vous pouvez faire avec l'objet out est déterminé par son type : PrintStream. Par commodité, PrintStream figure comme un hyperlien dans la description, et si vous cliquez sur cet hyperlien, vous verrez la liste de toutes les méthodes que vous pouvez appeler pour PrintStream. Une description plus complète sera fournie dans la suite de ce livre. Pour l'instant, la seule composante qui nous intéresse est println( ), qui signifie « écris sur la console ce que je te fournis et termine par une nouvelle ligne ». Ainsi, dans chaque programme Java que vous écrivez, vous pouvez dire System.out.println(« chose »); à chaque fois vous voulez imprimer quelque chose à la console.

Le nom de la classe est le même que celui du fichier. Quand vous créez un programme autonome comme celui-ci, une des classes dans le fichier doit avoir le même nom que le fichier. (Le compilateur génère une erreur si vous ne le faites pas.) Cette classe doit contenir une méthode appelée main( ) avec la signature :

 
Sélectionnez
public static void main(String[] args) {

Le mot-clé public signifie que la méthode est accessible par le monde extérieur (décrit en détail dans le chapitre 5). L'argument de main( ) est un tableau d'objets String. args ne sera pas utilisé dans ce programme, mais le compilateur Java réclame sa présence, car il contient les arguments de la ligne de commande.

La ligne qui affiche la date est très intéressante :

 
Sélectionnez
System.out.println(new Date());

L'argument est un objet Date qui est créé juste pour renvoyer sa valeur (qui est automatiquement convertie en une String pour println( ). Dès que cette étape est terminée, cette Date devient inutile, et le ramasse-miettes peut la détruire n'importe quand. Vous n'avez pas à vous préoccuper de sa destruction.

II-G-1. Compilation et exécution

Pour compiler et exécuter ce programme, et tous les autres programmes de ce livre, vous devez tout d'abord avoir un environnement de programmation Java. Il existe beaucoup d'environnements tiers pour le développement Java, mais dans ce livre nous supposerons que vous utilisez le Java Developer's Kit (JDK) de Sun, qui en plus est gratuit. Si vous utilisez un autre environnement de développement, (14) vous devrez consulter la documentation de ce système pour savoir compiler et exécuter des programmes.

Connectez-vous à Internet et allez sur java.sun.com. Vous pourrez y trouver des informations et des liens qui pourront vous guider pour le téléchargement et l'installation du JDK dédié à votre plateforme.

Une fois que le JDK est installé et que vous avez positionné convenablement le « path » de votre ordinateur afin que javac et java soient disponibles, téléchargez et installez le code source de ce livre (vous pouvez le trouver à www.BruceEckel.com). Cela créera un sous-répertoire pour chaque chapitre de ce livre. Allez dans le sous-répertoire c02 et tapez :

 
Sélectionnez
javac HelloDate.java

Cette commande ne devrait rien faire. Si vous obtenez n'importe quel message d'erreur, cela signifie que vous n'avez pas installé le JDK correctement et vous devez alors regarder de plus près ces erreurs.

Par contre, si vous obtenez à nouveau simplement le « prompt » de votre ligne de commande, vous pouvez taper :

 
Sélectionnez
java HelloDate

et vous obtiendrez le message et la date en sortie.

C'est le processus que vous pouvez utiliser pour compiler et exécuter chaque programme de ce livre. Malgré tout, vous constaterez que le code source de ce livre bénéficie aussi d'un fichier appelé build.xml dans chaque chapitre, et que celui-ci contient des commandes « ant » pour construire automatiquement les fichiers pour chaque chapitre. Ces fichiers « BuildFiles » et Ant (incluant où le télécharger) sont décrits plus amplement dans le chapitre 15, mais dès que Ant est installé (depuis http://jakarta.apache.org/ant) vous pouvez taper 'ant' sur la ligne de commande pour compiler et exécuter le programme de chaque chapitre. Si vous n'avez pas encore installé Ant, vous pouvez juste taper les commandes javac et java à la main.

II-H. Commentaires et documentation intégrée

Il y a deux types de commentaires en Java. Le premier est le commentaire traditionnel, style C, dont a hérité C++. Ces commentaires commencent par /* et continuent, éventuellement sur plusieurs lignes, jusqu'à un */. Il faut remarquer que de nombreux programmeurs commencent chaque ligne de continuation de commentaire avec *, on voit donc souvent :

 
Sélectionnez
/* Ceci est un commentaire
* qui continue
* sur plusieurs lignes
*/

Il faut toutefois se rappeler que tout ce qui se trouve entre /* et */ est ignoré, il n'y a donc aucune différence avec :

 
Sélectionnez
/* Ceci est un commentaire qui
   continue sur plusieurs lignes */

La seconde forme de commentaires vient du C++. C'est le commentaire sur une seule ligne qui commence avec // et continue jusqu'à la fin de la ligne. Ce type de commentaire est pratique et souvent rencontré, car il est simple. Il n'y a pas à se démener sur le clavier pour trouver / puis * (à la place il suffit d'appuyer deux fois sur la même touche) et il n'est pas nécessaire de fermer le commentaire. On trouve donc fréquemment :

 
Sélectionnez
// Ceci est un commentaire sur une seule ligne

II-H-1. Commentaires de documentation

Une des meilleures idées de Java réside dans le fait que coder n'est pas la seule activité importante - documenter est au moins aussi important. Le plus gros problème avec la documentation de code est probablement la maintenance de cette documentation. Si la documentation et le code sont séparés, ça devient embêtant de changer la documentation chaque fois que le code change. La solution a l'air simple : relier le code et la documentation. Le moyen le plus simple de le faire est de tout mettre dans le même fichier. Toutefois, pour compléter le tableau il faut une syntaxe de commentaire particulière pour repérer la documentation et un outil pour extraire ces commentaires et les mettre sous une forme exploitable. C'est ce que Java a fait.

L'outil pour extraire les commentaires est appelé javadoc et fait parti du JDK. Il utilise certaines technologies du compilateur Java pour rechercher les marqueurs spécifiques des commentaires qui ont été mis dans les programmes. Il ne se contente pas d'extraire les informations repérées par ces marqueurs, mais il extrait aussi le nom de classe ou le nom de méthode adjoint au commentaire. Ainsi on parvient avec un travail minimal à générer une documentation de programme convenable.

La sortie de javadoc est un fichier HTML qui peut être visualisé avec un navigateur Web. Cet outil permet de créer et maintenir un unique fichier source et à générer automatiquement une documentation utile. Grâce à javadoc on a un standard pour créer la documentation et il est suffisamment simple pour qu'on puisse espérer ou même exiger une documentation avec toute bibliothèque Java.

En plus, il est possible d'écrire ses propres extensions javadoc, appelées doclets, si le besoin existe de réaliser des opérations spécifiques sur les informations traitées par javadoc (sortie dans un format différent, par exemple). La notion de doclets est traitée au chapitre 15.

Ce qui suit est uniquement une introduction et un survol des principes de base de javadoc. Une description plus approfondie peut être trouvée dans la documentation du JDK téléchargeable depuis java.sun.com (à noter que cette documentation n'est pas fournie avec le JDK; il est nécessaire de la télécharger séparément). Lorsque la documentation sera installée, il faut regarder dans le répertoire « tooldocs » (ou suivre le lien « tooldocs »).

II-H-2. Syntaxe

Toutes les commandes javadoc n'apparaissent que dans les commentaires /**. Les commentaires finissent avec */ comme d'habitude. Il y a deux principales façons d'utiliser javadoc : du HTML intégré ou des « onglets doc ». Les onglets doc autonomes sont des commandes qui commencent avec un '@' et qui sont placées au début d'une ligne de commentaire (toutefois, un '*' en tête est ignoré). Les onglets doc intégrés peuvent apparaître dans un commentaire javadoc et commencent par un '@', mais sont entourés par des accolades.

Il y a trois types de commentaires de documentation qui correspondent aux éléments suivant le commentaire : classe, variable ou méthode. C'est-à-dire qu'un commentaire de classe apparaît juste avant la définition de la classe, un commentaire de variable apparaît juste avant la définition de la variable et un commentaire de méthode apparaît juste avant la définition de la méthode. Voici un exemple simple :

 
Sélectionnez
/** Un commentaire de classe  */
public class DocTest {
    /** Un commentaire de variable */    
    public int i;
    /** Un commentaire de méthode */
    public void f() {}
}

Il faut noter que javadoc ne traite les commentaires de documentation que pour les membres public et protected. Les commentaires pour les membres private et « amis » (voir Chapitre 5) sont ignorés et on ne verra aucune sortie (toutefois on peut utiliser le flag -private pour inclure aussi les membres private). Ceci est sensé puisque seuls les membres public et protected sont accessibles en dehors du fichier, ce qui est la perspective du client du programmeur. Toutefois, tous les commentaires de classe sont inclus dans le fichier de sortie.

La sortie du code précédent est un fichier HTML qui a le même format standard que tout le reste de la documentation Java, ainsi les utilisateurs seront à l'aise avec le format et pourront facilement naviguer dans les classes. Ça vaut la peine de taper le code précédent, de le passer dans javadoc et d'étudier le fichier HTML résultant pour voir le résultat.

II-H-3. HTML intégré

Javadoc passe les commandes HTML dans les documents HTML générés. Ceci permet une utilisation complète de HTML, la motivation principale étant de permettre le formatage du code comme suit :

 
Sélectionnez
/**
 * <pre>
 * System.out.println(new Date());
 * </pre>
 */

On peut aussi utiliser HTML comme on pourrait le faire dans n'importe quel autre document Web pour formater du texte courant dans les descriptions :

 
Sélectionnez
/**
 * On peut <em>même</em> insérer une liste :
 * <ol>
 * <li> élément un
 * <li> élément deux
 * <li> élément trois
 * </ol>
 */

Il faut noter que dans les commentaires de documentation, les astérisques en début de ligne sont éliminés par javadoc, ainsi que les espaces en tête de ligne. Javadoc reformate tout pour assurer la conformité avec l'apparence des documentations standard. Il ne faut pas utiliser des titres tels que <h1> ou <hr> dans le HTML intégré, car javadoc insère ses propres titres et il y aurait des interférences.

Tous les types de commentaires de documentation -- classes, variables et méthodes -- acceptent l'intégration de HTML.

II-H-4. Des exemples de tags

À suivre, quelques onglets javadoc disponibles pour la documentation du code. Avant de faire quoi que ce soit de sérieux avec javadoc, il est recommandé de consulter la documentation de référence sur javadoc, documentation disponible dans la documentation du JDK, afin de bénéficier d'une description complète des possibilités d'utilisation.

II-H-4-a. @see : faire référence aux autres classes

Les onglets @see permettent de faire référence à de la documentation dans d'autres classes. Javadoc génèrera du HTML où les onglets @see seront des hyperliens vers d'autres documentations. Les différentes formes sont :

 
Sélectionnez
@see classname
@see fully-qualified-classname
@see fully-qualified-classname#method-name

Chacune d'entre elles ajoute un hyperlien de type « Voir aussi » à la documentation générée. Javadoc ne vérifie pas si l'hyperlien indiqué est valide.

II-H-4-b. {@link package.class#member label}

Très similaire à @see, excepté le fait qu'il puisse être utilisé « en interne » et utilise label comme hyperlien au lieu de « Voir aussi ».

II-H-4-c. {@docRoot}

Génère le chemin relatif à la racine de la documentation. Utile pour générer des hyperliens explicites vers des pages de l'arborescence de la documentation.

II-H-4-d. {@inheritDoc} 

Permet de faire hériter la documentation d'une classe de sa classe de base la plus proche, dans le commentaire de la documentation en cours.

II-H-4-e. @version

Voici le format :

 
Sélectionnez
@version version-information

dans lequel version-information est n'importe quelle information significative que l'on souhaite inclure. Quand le flag -version est mis sur la ligne de commande de javadoc, les informations de version seront exploitées, particulièrement dans la documentation HTML.

II-H-4-f. @author

Voici le format :

 
Sélectionnez
@author author-information

dans lequel author-information est, à priori, votre nom, mais il peut aussi contenir une adresse email ou toute autre information appropriée. Quand le flag -author est mis sur la ligne de commande javadoc, les informations sur l'auteur seront exploitées, particulièrement dans la documentation HTML.

On peut avoir plusieurs onglets d'auteur pour une liste d'auteurs, mais ils doivent être placés consécutivement. Toutes les informations d'auteurs seront regroupées dans un unique paragraphe dans le code HTML généré.

II-H-4-g. @since

Cet onglet permet d'indiquer la version du code qui a commencé à utiliser une caractéristique particulière. On la verra apparaître dans la documentation HTML de Java pour indiquer quelle version de JDK est utilisée.

II-H-4-h. @param

Cet onglet est utilisé pour documenter les méthodes, et son format est le suivant :

 
Sélectionnez
@param parameter-name description

dans lequel parameter-name est l'identificateur dans la liste de paramètres et description est du texte qui peut se prolonger sur les lignes suivantes. La description est considérée comme terminée quand un nouvel onglet de documentation est trouvé. On peut en avoir autant qu'on veut, à priori un pour chaque paramètre.

II-H-4-i. @return

Cet onglet est utilisé pour documenter les méthodes, et son format est le suivant :

 
Sélectionnez
@return description

dans lequel description indique la signification de la valeur de retour. Le texte peut se prolonger sur les lignes suivantes.

II-H-4-j. @throws

Les exceptions seront introduites dans le Chapitre 9. Brièvement, ce sont des objets qui peuvent être émis (thrown) par une méthode si cette méthode échoue. Bien qu'une seule exception puisse surgir lors de l'appel d'une méthode, une méthode donnée est susceptible de produire n'importe quel nombre d'exceptions de types différents, chacune d'entre elles nécessitant une description. Ainsi, le format de l'onglet d'exception est :

 
Sélectionnez
@throws fully-qualified-class-name description

dans lequel fully-qualified-class-name indique sans ambiguïté un nom de classe d'exception qui est définie quelque part, et description (qui peut se prolonger sur les lignes suivantes) précise pourquoi ce type particulier d'exception peut survenir lors de l'appel de la méthode.

II-H-4-k. @deprecated

Ceci est utilisé pour marquer des fonctionnalités qui ont été remplacées par d'autres qui sont meilleures. L'onglet deprecated suggère de ne plus utiliser cette fonctionnalité particulière étant donné qu'elle sera probablement supprimée ultérieurement. Une méthode marquée @deprecated fait produire un warning par le compilateur si elle est utilisée.

II-H-5. Exemple de documentation

Voici à nouveau le premier programme Java, cette fois après avoir ajouté les commentaires de documentation :

 
Sélectionnez
//: c02:HelloDate.java
import java.util.*;

/** Le premier exemple de programme de Thinking in Java.
* Affiche une chaîne de caractères et la date du jour.
* @author Bruce Eckel
* @author www.BruceEckel.com
* @version 2.0
*/
public class HelloDate {
    /** Unique point d'entrée de la classe et de l'application
     * @param args tableau de paramètres sous forme de chaînes de caractères
     * @return Pas de valeur de retour
     * @exception exceptions Pas d'exceptions émises
     */  
    public static void main(String[] args) {
        System.out.println("Hello, it's: ");
        System.out.println(new Date());
    }
} ///:~

La première ligne du fichier utilise une technique personnelle qui consiste à mettre un '//:' comme marqueur spécifique pour la ligne de commentaire contenant le nom du fichier source. Cette ligne contient le chemin du fichier (dans ce cas, c02 indique le Chapitre 2) suivi du nom de fichier. (15) La dernière ligne finit aussi avec un commentaire et celui-ci ('///:~') indique la fin du listing du code source, ce qui permet de le mettre à jour automatiquement dans le texte de ce livre après l'avoir contrôlé avec un compilateur puis exécuté.

II-I. Style de programmation

Le style décrit dans les conventions de codage du langage Java(16) consiste à mettre en majuscule la première lettre des noms de classes. Si le nom de classe est composé de plusieurs mots, ils sont accolés (c'est-à-dire qu'on ne sépare pas les noms avec un trait bas) et la première lettre de chaque mot est mise en majuscule ainsi :

 
Sélectionnez
class AllTheColorsOfTheRainbow { // ...

Cette pratique est parfois appelée « camel-casing ». Pour pratiquement tout le reste : méthodes, champs (variables membres) et les noms des références d'objets, le style retenu est comme pour les classes sauf que la première lettre de l'identificateur est une minuscule. Par exemple :

 
Sélectionnez
class AllTheColorsOfTheRainbow {
    int anIntegerRepresentingColors;
    void changeTheHueOfTheColor(int newHue) {
        // ...
    }
    // ...
}

Bien entendu il faut se rappeler que l'utilisateur doit aussi taper tous ces longs noms, donc soyez clément.

Le code Java qu'on voit dans les bibliothèques de Sun respecte aussi le placement des accolades ouvrantes et fermantes qui est utilisé dans ce livre.

II-J. Sommaire

L'objectif de ce chapitre est juste de voir suffisamment Java pour comprendre comment écrire un programme simple. Il a aussi été donné une vue d'ensemble du langage et de certaines des idées de base. Toutefois, les exemples vus jusqu'à présent ont tous été du type « faire ceci, puis faire cela, puis faire autre chose ». Qu'en advient-il si on veut faire un programme pour réaliser des choix, comme dans « si le résultat de ceci est rouge, faire cela, sinon faire autre chose » ? Les outils disponibles en Java pour cette activité de programmation fondamentale seront vus dans le prochain chapitre.

II-K. Exercices

Les solutions aux exercices peuvent être obtenues dans le document électronique The Thinking in Java Annotated Solution Guide, disponible pour une faible somme depuis www.BruceEckel.com.

  1. En suivant l'exemple HelloDate.java de ce chapitre, créez un programme « hello, world » qui affiche simplement cette phrase. Vous n'avez besoin que d'une seule méthode dans la classe (la méthode « main » qui est exécutée quand le programme démarre). Pensez à la rendre static et à indiquer la liste de paramètres, même si la liste de paramètres n'est pas utilisée. Compilez ce programme avec javac et exécutez-le avec java. Si vous utilisez un environnement de développement autre que JDK, apprenez à compiler et exécuter les programmes dans cet environnement.
  2. Trouver le morceau de code concernant ATypeName et faites-en un programme qui compile et s'exécute.
  3. Transformez le morceau de code DataOnly en un programme qui compile et qui s'exécute.
  4. Modifiez l'exercice 3 de telle sorte que les valeurs des données dans DataOnly soient affectées et affichées dans main( ).
  5. Écrivez un programme qui inclut et appelle la méthode storage( ) définie comme morceau de code dans ce chapitre.
  6. Transformez le morceau de code StaticFun en un programme qui fonctionne.
  7. Écrivez un programme qui imprime trois paramètres saisis sur la ligne de commande. Pour faire ceci il faut indexer le tableau de String représentant la ligne de commande.
  8. Transformez l'exemple AllTheColorsOfTheRainbow en un programme qui compile et s'exécute.
  9. Trouvez le code de la seconde version de HelloDate.java qui est le petit exemple de commentaire de documentation. Exécutez javadoc sur le fichier et visualisez le résultat avec votre navigateur Web.
  10. Transformez docTest en un fichier qui compile et exécutez javadoc dessus. Contrôlez la documentation qui en résulte avec votre navigateur Web.
  11. Ajoutez une liste d'éléments en HTML, à la documentation de l'exercice 10.
  12. Prenez le programme de l'exercice 1 et ajoutez-lui une documentation. Sortez cette documentation dans un fichier HTML à l'aide de javadoc et visualisez-la avec votre navigateur Web.
  13. Dans le chapitre 4, recherchez l'exemple Overloading.java et ajoutez une documentation javadoc. Extrayez cette documentation dans un fichier HTML en utilisant javadoc et visualisez le résultat dans votre navigateur Web.

précédentsommairesuivant
Ceci peut être un point d'accrochage. Il y a ceux qui affirment « c'est clairement un pointeur », mais ceci suppose une implémentation sous-jacente. De plus, les références Java sont beaucoup plus apparentées aux références C++ qu'aux pointeurs dans leur syntaxe. Dans la première édition de ce livre, j'ai choisi d'inventer un nouveau terme, « manipulateur » (handle), car il existe d'importantes différences entre les références C++ et les références Java. Je venais du C++ et je ne voulais pas embrouiller les programmeurs C++ que je supposais être le principal public pour Java. Dans la 2d édition, j'ai décidé que « référence » était le terme le plus généralement utilisé, et que tous ceux qui venaient du C++ auraient à s'occuper de bien d'autres choses que de la terminologie des références, donc qu'ils pourraient aussi bien y plonger les deux pieds en avant. Il y a toutefois des gens qui ne sont pas d'accord, même avec le terme « référence ». J'ai lu dans un livre qu'il était « complètement faux de dire que Java supportait les passages par références », parce que les identificateurs des objets Java (selon cet auteur) sont en fait des « références à des objets ». Et (continue-t-il) tout est en fait passé par valeur. Donc on ne passe pas une référence, on « passe une référence à un objet par valeur ». On pourrait débattre de la précision d'une explication si alambiquée, mais je pense que mon approche simplifie la compréhension du concept sans rien dégrader (d'accord, les avocats du langage pourraient prétendre que je mens, mais je répondrai que je fournis une abstraction appropriée).
les méthodes statiques, dont il en sera dit plus bientôt, peuvent être appelées depuis la classe, sans création d'un objet.
Avec les exceptions habituelles pour les types de données précités boolean, char, byte, short, int, long, float, et double. En général, toutefois, on passe des objets, ce qui veut dire qu'en réalité on passe des références à des objets.
Le compilateur Java et la documentation de Sun n'ont pas été inclus dans le CD du livre, car le contenu tend à changer régulièrement. En le téléchargeant vous-même, vous obtiendrez la version la plus récente.
Le compilateur « jikes » d'IBM est une alternative courante, et qui est significativement plus rapide que javac de Sun.
À l'origine, j'avais écrit un outil utilisant Python (voir www.Python.org), qui utilise cette information pour extraire les fichiers de code, les positionner dans les sous-répertoires appropriés, et créer les makefiles. Dans cette édition, tous les fichiers sont gérés via Concurrent Versions System (CVS) et automatiquement incorporés dans ce livre via une macro écrite avec Visual BASIC for Applications (VBA). Cette nouvelle approche semble mieux fonctionner pour tout ce qui concerne la maintenance du code, surtout grâce à CVS.
http://java.sun.com/docs/codeconv/index.html. Afin de préserver de la place dans ce livre et les présentations lors de séminaires, toutes ces recommandations ne seront pas obligatoirement suivies.

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.