Looking for Computer Science & Information Technology online
courses ?
Check my new web site: https://www.yesik.it !
Sommaire
Cet article va introduire certaines constructions du langage Java à partir d'un exemple. Il suppose que vous sachiez compiler et exécuter un programme Java, et que vous ayez quelques notions élémentaires de la syntaxe de ce langage (déclaration de classe, variables, boucles, méthodes, création d'objet).
L'exemple
Dans cet article vous travaillez pour la banque privée Golding. Celle-ci désire refondre la partie de son système d'information chargée d'éditer les relevés de compte de ses clients. Dans un premier temps, il s'agit de l'édition des relevés "papier", mais à terme, il est souhaitable que le travail réalisé puisse être ré-investi pour l'édition de relevés électronique (e-mail et consultation sur le web).
Vous en êtes actuellement à la phase préliminaire du projet. Le langage Java a été retenu. Votre client vous a fourni un relevé type, et un programme a été réalisé par un de vos collègue pour générer ce relevé.
Le relevé-type est le suivant:
Compte: 12345-0000-37-EUR / Agence de St-Malo
Titulaire: John Cresus
Relevé mensuel: Octobre 2008
Détail des transactions -----------------------------
Date Désignation Crédit Débit
01/10 Solde précédent 237.55
05/10 Chèque n°2008-17 12.27
06/10 Chèque n°2008-16 22.31
11/10 Virement vers compte ext. 300.00
15/10 Prélèvement n°4567234-44 25.71
15/10 Prélèvement n°4569873-31 25.71
16/10 Paiement CB Visa (EUR) 102.25
16/10 Retrait espèce 40.00
20/10 Remboursement n°879712345 12.27
22/10 Paiement CB Visa (USD) 47.19
22/10 Frais achat étranger (USD) 0.50
30/10 Traitement 1175.22
--/-- Incident 12/10 - 30/10 8.50
--/-- Solde à échéance 840.60
Note: Frais sur découvert 0.25%/jour avec un minimum de
8.50 EUR/mois
Avez-vous pensé à Disponi$ notre réserve de crédit
renouvelable? Renseignements en agence.
Le programme chargé de produire ce document se trouve dans la classe Releve.class. Et lors de l'exécution, ce programme génère un fichier releve.out.txt qui contient le relevé:
sh$ ls Compte.class Releve.class sh$ java Releve sh$ ls Compte.class Releve.class releve.out.txt
Pour vérifier que le document produit est absolument identique au document initial, nous pouvons utiliser l'utilitaire diff:
sh$ diff -s releve.out.txt ../test/releve.txt Files releve.out.txt and ../test/releve.txt are identical
Voilà, visiblement le programme fonctionne. Nous allons passer le reste de ce tutoriel à examiner comment il fonctionne - et vous remarquerez vite qu'il y a encore un peu de travail à accomplir.
Le code source
Le source du programme est réparti en deux fichiers: Releve.java et compte.java
Releve.java
import java.io.*; public class Releve { public static void main(String[] argv) { OutputStreamWriter out = null; try { out = new OutputStreamWriter( new FileOutputStream("releve.out.txt"), "UTF-8"); Compte compte = new Compte(); compte.afficheReleve(out); } catch(UnsupportedEncodingException e) { System.err.println("Erreur: l'encodage demandé n'est pas disponible"); System.err.println(e); } catch(FileNotFoundException e) { System.err.println("Erreur: impossible d'accéder au fichier"); System.err.println(e); } finally { try { out.close(); } catch(Exception e) { System.err.println(e); }; } } }
Compte.java
import java.io.*; /** * Un compte bancaire */ public class Compte { /** * Ecrit le relevé du compte. * @param out fichier destination */ public void afficheReleve(OutputStreamWriter out) { InputStreamReader in = null; try { in = new InputStreamReader( new FileInputStream("../test/releve.txt"), "UTF-8"); final int BUF_SIZE = 80; char[] buffer = new char[BUF_SIZE]; int nb_lu; while((nb_lu = in.read(buffer)) != -1) { out.write(buffer, 0, nb_lu); } } catch(UnsupportedEncodingException e) { System.err.println("Erreur: l'encodage demandé n'est pas disponible"); System.err.println(e); } catch(FileNotFoundException e) { System.err.println("Erreur: impossible d'accéder au fichier"); System.err.println(e); } catch(IOException e) { System.err.println("Erreur: problème d'entrée/sortie"); System.err.println(e); } finally { try { in.close(); } catch(Exception e) { System.err.println(e); }; } } }
Note:
Comme vous le constatez, l'auteur de ce code ne s'est pas forcément beaucoup creusé la tête. En effet, le programme se contente de recopier le contenu du fichier de test releve.txt! Ca n'est rien de dire qu'on est encore bien loin d'un programme opérationnel.
Néanmoins, c'est bien assez pour notre objectif qui est de voir un certain nombre de techniques de programmation Java.
Entrées/sorties dans un fichier texte
Le premier point que nous allons étudier est celui des entrées/sorties dans un fichier texte. C'est une manière compliquée de dire quelque chose de simple: comment lire ou écrire dans un fichier texte?
Java offre une pléthore de classe pour les entrées/sorties (plus de 60!). Beaucoup de ces classes sont basées sur le modèle d'entrées/sorties par flux. Dans ce modèle, on considère que la lecture (ou l'écriture) se fait séquentiellement.
Ainsi, les opérations de lectures ont lieu les unes à la suite des autres, chaque lecture "consommant" des caractères:
De la même manière, chaque écriture prend place après l'écriture précédente.
Remarque:
Cela s'oppose avec la notion d'entrées/sorties aléatoires où il est possible (voire nécessaire) d'indiquer avant chaque opération de lecture/écriture où les données doivent être lues (ou écrites).
Par ailleurs, Java distingue les classes chargées de l'accès au média (par exemple un fichier) des classes fournissant des interfaces de plus haut niveau vers ces média.
En outre, comme dans de nombreux langages, en Java un flux doit être ouvert avant de pouvoir être manipulé (avant de pouvoir lire ou écrire). Cette ouverture se fait presque toujours lors de la construction de l'objet chargé d'accéder au flux. Une fois que le flux ne sert plus, il doit être explicitement fermé.
Si l'on examine le code de la classe Releve, on retrouve toutes ces idées:
/* ... */ out = new OutputStreamWriter( new FileOutputStream("releve.out.txt"), "UTF-8"); /* ... */ try { out.close(); } catch(Exception e) { System.err.println(e); }; /* ... */
- new FileOutputStream("releve.out.txt")
- Crée un nouvel objet de la classe FileOutputStream pour accéder au fichier releve.out.txt. Cet objet n'est capable que d'opérations élémentaires sur le fichier.
- in = new OutputStreamWriter( ... )
- Crée un nouvel objet de la classe OutputStreamWriter pour accéder au fichier. Cet objet offre des opérations de plus haut niveau sur le média sous-jacent.
- out.close()
- Quand le flux ne sert plus, il est fermé. Toutes les données éventuellement encore en mémoire sont réellement écrites, et les ressources utilisées pour gérer ce flux sont libérées.
À vous de jouer:
Vous vous aiderez de la documentation officielle de l'API Java pour répondre aux questions suivantes:
- Peut-on lire des données dans le flux ouvert dans le fichier Releve.java?
- Peut-on écrire des données dans le flux ouvert dans le fichier Releve.java?
- S'agit-il plutôt d'un flux d'entrée ou de sortie?
- Quelles sont les méthodes proposées par FileOutputStream pour écrire dans le fichier?
- Quelles sont les méthodes proposées par OutputStreamWriter pour écrire dans le fichier?
- Laquelle de ces deux classes prend en charge les problèmes d'encodage des caractères?
- Quelles sont les deux classes correspondantes pour lire dans un flux?
- Le programme utilise un troisième flux. Lequel? Au travers de quel objet y accède-t-on? Quelle est sa classe? Comment gère-t-elle l'encodage des caractères? Vous pourrez rechercher certaines de ces réponses à partir de la page http://java.sun.com/javase/6/docs/api/java/lang/package-summary.html
- A quel package appartiennent ces classes?
import
En Java, un package est une collection de classes, généralement apparentées. Une classe appartient toujours à un package. Java fournit en standard un certain nombre de ces collections. Par exemple, dans la section précédente, vous avez examiné un certain nombre de classes du package java.io.
Pour que le compilateur et la machine virtuelle puissent retrouver une classe, il est systématiquement nécessaire d'indiquer à quel package elle appartient. Cela peut se faire explicitement, en préfixant le nom de la classe avec le nom du package:
/* ... */ new java.io.FileOutputStream("releve.out.txt") /* ... */
Cela peut aussi se faire implicitement. Dans ce cas, on indique au début du fichier source la liste des packages dans lesquels le compilateur pourra trouver les classes utilisées. On utilise alors la directive import, comme ça a été fait dans le fichier Releve.java:
import java.io.*; /* ... */ new FileOutputStream("releve.out.txt") /* ... */
À vous de jouer:
Qu'indique le compilateur si on "oublie" d'importer le package java.io au début du fichier Compte.java?
try ... catch ... finally
Dans un programme, certaines opérations peuvent échouer. Par exemple, il y a une multitude de raisons qui peuvent faire qu'il soit impossible de lire ou d'écrire dans un fichier. Un programme bien écrit doit impérativement être capable de gérer ces situations exceptionnelles.
Pour gérer les exceptions, Java propose donc une structure de contrôle particulière: la structure try ... catch ... finally. Celle-ci permet:
- d'essayer de faire quelque chose (c'est le rôle du bloc try)
- de gérer une exception qui surviendrait dans un bloc try (c'est le rôle du (des) bloc(s) catch)
- d'effectuer un traitement dans tous les cas (c'est le rôle du bloc finally)
/* ... */ try { out = new OutputStreamWriter( new FileOutputStream("releve.out.txt"), "UTF-8"); Compte compte = new Compte(); compte.afficheReleve(out); } catch(UnsupportedEncodingException e) { System.err.println("Erreur: l'encodage demandé n'est pas disponible"); System.err.println(e); } catch(FileNotFoundException e) { System.err.println("Erreur: impossible d'accéder au fichier"); System.err.println(e); } finally { try { out.close(); } catch(Exception e) { System.err.println(e); }; } /* ... */
- try { ... }
- le programme essaye de créer des flux de sorties, de créer un objet de la classe Compte, et d'afficher le relevé de ce compte. Si une exception se produit à n'importe laquelle de ces étapes, l'exécution du bloc try se termine, et le contrôle est passé à un bloc catch
- catch(...Exception e) { ... }
- Chaque bloc catch est chargé de gérer un type d'exception. Au plus un seul bloc catch est exécuté.
- finally { ... }
- Enfin, le bloc finally est exécuté dans tous les cas (qu'il y ait eu une exception ou pas!).
Note:
Les blocs try ... catch ... finally peuvent être imbriqués. Soit explicitement:
/* ... */ finally { try { out.close(); } catch(Exception e) { System.err.println(e); }; } /* ... */
Soit implicitement quand une méthode contenant un bloc try ... catch ... finally est appelée à partir d'un autre bloc try ... catch ... finally.
À vous de jouer:
- D'après la documentation, quelles sont les exceptions qui peuvent être lancées par le constructeur de la classe FileOutputStream utilisée dans notre programme?
- Que se passe-t-il si l'on "oublie" dans le code source le bloc catch correspondant?
- Réalisez le tutoriel de Sun sur les exceptions.
Final
Maintenant que vous avez vu le bloc try ... catch ... finally ne confondez pas cette structure de contrôle avec le mot clé final:
final int BUF_SIZE = 80;
Utilisé dans la déclaration d'une variable, le mot-clé final signifie que le contenue de la variable ne changera plus une fois celle-ci initialisée. C'est ce que Java offre de plus prochede la notion de contante.
Note:
Il est d'usage de donner un nom en MAJUSCULE aux variables définies avec le mot-clé final. C'est un usage hérité du langage C.
Array
Dernier point de syntaxe que vous ne connaissez peut-être pas encore, la notation []. Vous l'avez déjà vue lors de la déclaration du programme principal Java:
public static void main(String[] args) { /* ... */ }
Cette notation dénote un tableau. C'est à dire une variable qui va pouvoir contenir plusieurs valeurs du même type. Pour créer un tableau en Java, il est nécessaire d'utiliser l'opérateur new:
char[] buffer = new char[BUF_SIZE];
Ensuite, les éléments du tableau peuvent être accédés individuellement en donnant leur index entre crochet:
System.out.println(buffer[0]); /* not in example code */ System.out.println(buffer[1]); /* not in example code */ /* ... */ System.out.println(buffer[BUF_SIZE-1]); /* not in example code */
Piège:
En Java (comme en C, C++, PHP JavaScript), les éléments d'un tableau sont indexés à partir de 0. Pas de 1! Un tableau de 4 éléments contiendra donc les élements d'index 0, 1, 2 et 3. Pas d'élément 4!
Javadoc
Vous avez beaucoup utilisé dans ce TP la documentation de l'api de Java. Cette documentation est générée automatiquement par un outil appelé javadoc à partir des commentaires laissés dans le programme.
javadoc peut aussi être utilisé pour générer automatiquement la documentation pour le code source que vous écrivez. Un commentaire javadoc est visible dans le programme car il commence par /**. Comme tout commentaire, il se termine par */.
À vous de jouer:
- Utilisez javadoc pour générer la documentation des classes Releve et Compte;
- Ajoutez la description de la classe Releve dans la documentation.
Conclusion
Cet article a été l'occasion de vous familiariser avec un certain nombre de notions typiques de la "Java way of Life":
- Les entrées/sorties par flux
- la notion de package
- la gestion des exceptions
- la déclaration de constantes et de tableaux
- la documentation automatique
Ces notions sont tellement fondamentales qu'elles sont présentes dans tout programme Java! Il va donc sans dire que leur maîtrise est un pré-requis indispensable à toute forme de programmation sérieuse en Java.
En outre, vous vous êtes également habitué à rechercher des informations dans la documentation de l'API Java. C'est aussi un passage obligé: en effet avec plusieurs milliers de classes disponibles, il est illusoire de vouloir tout connaître par cœur. Ainsi, à moins de se contenter d'écrire des programmes élémentaires ou de recopier des fragments de codes trouvés sur internet, il est indispensable d'être capable d'utiliser cette source d'information.
Ressources
- http://java.sun.com/docs/books/tutorial/essential/index.html - L'essentiel pour Java
- http://java.sun.com/javase/6/docs/api/index.html - La documentation de l'API Java SE 6



