Date de première publication : 2018/01/29
Notions : Définition de classes, Manipulations de chaines et de fichiers, conteneurs, collections et streams
Nous allons créer un annuaire non exhaustif des personnes ayant créé un langage de programmation.
Mise en place de quelques classes
- Créer une classe
Personne
qui possède les attributs privés suivants : nom
,prenom
,langage
etannee
: des chaînes de caractères- un attribut de classe entier
compteur
.
Java 8 introduit un nouveau package java.time qui définit une classe Year...
- Initialiser
compteur
dans un bloc spécifique exécuté au chargement de la classe. Ce bloc affichera sur la console ce qu’il fait. - Créer un constructeur sans paramètre qui incrémente
compteur
. Afficher l’action sur la console. Quelle est l'instanciation par défaut des attributs ? - Créer un autre constructeur qui permet d’initialiser tous les attributs d’instance et d'incrémenter
compteur
. Pour ne pas faire de la duplication de code, vous avez deux possibilités : - une méthode privée dédiée (méthode qui marche quel que soit le langage)
- un appel de constructeur à partir d'un constructeur (spécifique au java), méthode que l'on peut aussi utiliser pour pallier l'absence de paramètres par défaut
Si vous choisissez l'appel de constructeur à partir d'un autre constructeur, vous devez choisir quel constructeur appelle l'autre. Pour moi, il est plus logique que le constructeur avec moins d'arguments appelle le constructeur qui initialise tous les paramètres - ce dernier peut être private
si nécessaire.
- Doter la classe
Personne
d’une méthode de classe qui affiche le nombre d’instances déjà créées. - Créer une classe
WhoswhoApplication
avec une méthodemain()
. - Vérifier qu’aucune instance de
Personne
n’a été créée. - Créer une instance de
Personne
. Vérifier que le nombre d’instances de la classePersonne
a bien été mis à jour. - Appeler la méthode
toString()
de l’objet créé. Obtient‐on le résultat attendu ? Pourquoi ?
La méthode toString()
est héritée d’Object
. Son comportement par défaut est de donner le nom de la classe et l'adresse mémoire un code de hachage de l’instance concernée.
- Redéfinir la méthode
toString()
pour que celle‐ci renvoie une chaîne de caractères du typenom:prenom:langage:annee
. On utilisera un objetStringBuffer
ouStringBuilder
pour la générer.
Si vous codez une concaténation de String
, "a"+"b", le compilateur transforme automatiquement avec un StringBuilder
MAIS cela reste une bonne idée de le faire et de savoir faire car dans d'autres situations, le compilateur ne saura pas optimiser.
Gestion de fichiers et de plusieurs instances d’une même classe
Manipulation d'un fichier texte
- Sauvegarder cette chaine de caractères dans un fichier texte. Créer quelques autres instances de
Personne
et les sauvegarder dans le fichier de la même manière. Une méthode se trouve dans la copie des transparents (mais à quelle page ? ;-) ).
On peut par exemple chaîner des objets de type File
, FileOutputStream
puis PrintStream
.
Il faut également gérer les exceptions possibles avec double bloc try/catch ou un try-with-resources si vous le pouvez.
try(R1 r1 = new R1(); R2 r2 = new R2()) {
// code utilisant les ressources
} // fermeture automatique des ressources
catch (Exception e) {
}
On va maintenant s’intéresser à la lecture de ce fichier par une classe Whoswho
, un conteneur d’instances de Personne
.
- Créer une classe
Whoswho
qui aura un attributpersonnes
: une collection dePersonne
. Je vous propose différentes solutions : un tableau de personnesPersonne[]
avec un attribut pour compter le nombre d’éléments ou un vecteur dynamique génériqueVector
, ou bien encoreArrayList
. Cette dernière classe dite legacy (mais indispensable quand on fait du multithreading) peut vous sembler trop veille, vous pouvez utiliser également une liste chaînée. Nous avons déjà cité la classe idoine. - Initialiser
personnes
dans le constructeur de la classeWhoswho
. Est‐ce que cela crée des instances dePersonne
? Pourquoi ?
Cela ne crée pas d’instance car c’est un tableau de références. Créer un tableau d’objets en C++ crée autant d’objets que nécessaire en faisant appel au constructeur par défaut de la classe.
- Ajouter une méthode
lire()
qui prend en paramètre le nom d’un fichier et qui lit le fichier précédent avec les classesFileReader
etBufferedReader
. Chaque ligne sera analysée avec la classeStringTokenizer
(la documentation déconseille son usage désormais), la méthodesplit()
deString
ou une expression régulièrejava.util.regex
.
Sérialisation
- Reprendre le point précédent en essayant cette fois la sérialisation des objets.
- Sérialisation classique intégrée au langage (fichier binaire)
- "Sérialisation" XML avec une bibliothèque comme celle présentée en cours x-stream [lien officiel] [miroir]
L'étape la plus délicate pour la "sérialisation" XML est l'ajout de la bilbilothèque spécifique (sous forme de fichier jar) lors des étapes de compilation/exécutation que ce soit à la main ou dans l'EDI. La méthode est décrite dans la FAQ Eclipse.
Pour la compilation ou l'exécution à la main, il faut "inclure" le fichier jar dans la liste des classes disponibles :
javac -cp xstream.jar:. Classe.java
Voilà le code d'écriture pour une version récente de la bibliothèque :
FileOutputStream fos = null;
XStream xstream = null;
fos = new FileOutputStream(name);
xstream = new XStream(new StaxDriver());
xstream.toXML(objects, fos);
Lecture avec un Stream
Si besoin, on peut lire un fichier texte avec un Stream
, par exemple :
Stream<String> stream = Files.lines(Paths.get(nomDuFichier));
stream.forEach(System.out::println);
dans un try-with-resources
. La classe String
peut être par la classe Personne
, et le stream transformé en collection grâce à la méthode collect()
Exploitations des données - Streams
Dans la suite de l'exercice, il s'agit de pouvoir afficher la liste des personnes et des langages de manière différente. N'hésitez pas à tester d'abord les exemples du cours sur les streams.
- Lister les concepteurs de langages dans l'ordre lexicographique de leur nom
- Lister les concepteurs de langages dans l'ordre lexicographique de leur prénom
- Lister les langages dans l'ordre lexicographique de leur nom
- Lister les langages suivant l'année de création
Pour ce faire, on vous propose d'utiliser un Stream et de donner de différentes manières le "comparateur". Pour rappel, voici quelques possibilités vues en cours :
stream.filter(n-> n% 2 ==0);
stream.filter(new Predicate<Integer>)(){
public boolean test(Integer i) {
return Integer.valueOf(i) % 2 == 0;
});
La dernière chose que l'on va faire est de calculer l'année moyenne de création d'un langage. Vous pourrez à cet effet utiliser mapToInt()
pour transformer le stream objet en IntStream
puis appliquer des méthodes comme average()
, sum()
, count()