560 likes | 712 Views
Compléments. Chapitre III. Au sommaire de ce chapitre. Les fichiers ----------------- 3 Les classes utilitaires ----- 26 La généricité --------------- 46. 1 – Les fichiers. Les flots. Java propose un ensemble de classes pour la gestion des E/S décrites dans le package java.io
E N D
Compléments Chapitre III
Au sommaire de ce chapitre • Les fichiers ----------------- 3 • Les classes utilitaires ----- 26 • La généricité --------------- 46
Les flots • Java propose un ensemble de classes pour la gestion des E/S décrites dans le package java.io • les E/S sont implémentées par les flots • un flot peut être représenté comme un buffer et des mécanismes associés pour réaliser l'E/S
Les flots (suite) • dans un flot binaire aucune transformation n'est effectuée sur les octets échangés • entre mémoire et disque • dans un flot caractère les données sont stockées sous forme de caractères sur le disque • il y a donc une transformation qui est effectuée
Les classes • La hiérarchie des classes relatives aux E/S comprend 4 classes abstract • InputStream : lecture sur les flots binaires • OutputStream : écriture sur les flots binaires • Reader : lecture sur les flots caractères • Writer : écriture sur les flots caractères
Les classes d'E/S • A cela il faut rajouter aussi comme classes de base : • File : fichier • RandomAcessFile : accès aléatoires sur un fichier
Sérialisation-Désérialisation • Parmi tous les mécanismes d'E/S proposés par Java, le plus intéressant est la sérialisation qui permet : • de sauvegarder des objets dans un fichier • ou de restaurer des objets à partir d'un fichier • Deux classes du package java.io sont utilisées • ObjectOutputStream pour la sérialisation • ObjectInputStream pour la desérialisation
La sérialisation • Classe java.io.FileOutputStream • un FileOutputStream est un flux en sortie pour écrire des données dans un fichier • FileOutputStream est une sous-classe de OutputStream • Classe java.io.ObjectOutputStream • un ObjectOutputStream est un tampon qui reçoit les objets à sérialiser • écrit des données de type primitif (boolean, byte, char, int, etc) et des objets dans un flot de OutputStream
La sérialisation • Pour écrire le tampon dans le flot, on appelle la méthode writeObject() • la classe de l'objet qui effectue la sérialisation doit être public • la classe de l'objet doit implémenter Serializable • Cette méthode appelle également la méthode writeObject() de chaque objet composite • si leur classe implémente l'interface Serializable • et jusqu'à ce que tout ce qui compose l'objet soit écrit
Exemple 1 : sérialisation d’un objet import java.io.*; public class SerializerPersonne { public static void main(String argv[]) { Personne personne = new Personne("Dupond","Jean",175); try { FileOutputStream fichier = new FileOutputStream("personne.ser"); ObjectOutputStream oos = new ObjectOutputStream(fichier); oos.writeObject(personne); oos.flush(); // pour forcer le tampon à se vider dans le fichier oos.close(); // on ferme le tampon pour terminer l’opération } catch (IOException e) { e.printStackTrace(); } } // de main() } Une exception de type IOException peut être levée si un problème intervient avec le fichier
Le fichier obtenu • Après l'exécution de cet exemple, un fichier nommé « personne.ser » est créé. • On peut visualiser sont contenu mais surtout pas le modifier car sinon il serait corrompu • En effet, les données contenue dans ce fichier ne sont pas toutes au format caractères.
L'interface Serializable • Cette interface ne définit aucune méthode • mais permet simplement de marquer une classe comme pouvant être sérialisée (cf. Cloneable) • Elle est définie dans java.io • Si l'on tente de sérialiser un objet qui n'implémente pas l'interface Serializable • une exception NotSerializableException est levée.
Sérialiser des types primitifs • La classe ObjectOutputStream contient aussi plusieurs méthodes qui permettent de sérialiser des types élémentaires et non des objets : • writeInt, writeDouble, writeFloat ...
La dé-sérialisation • Classe java.io.FileInputStream • Un FileInputStream est un flux en entrée pour lire des données à partir d'un fichier • FileInputStream est une sous-classe de InputStream • Classe java.io.ObjectInputStream • Un ObjectInputStream est un tampon qui lit des données de type primitif et des objets à partir un InputStream
Processus de dé-sérialisation • Il est un tout petit peu plus complexe… • Pour remplir un tampon de lecture à partir d'un fichier, on appelle la méthode readObject() • cette méthode renvoie une référence sur Object • il faut donc effectuer un transtypage • Si on ne connaît pas la classe d'appartenance d'un objet (parmi plusieurs possibles) • on récupère la classe par les méthodes getName()et getClass() de la classe Class • exemple : if (monObjet.getClass().getName().equals(MaClasse)
Exemple 1 : dé-sérialiser une Personne public class DeSerializerPersonne { public static void main(String argv[]) { try { FileInputStream fichier = new FileInputStream("personne.ser"); ObjectInputStream ois = new ObjectInputStream(fichier); Personne personne = (Personne) ois.readObject(); System.out.println("Personne : "); System.out.println("nom : "+personne.getNom()); System.out.println("prenom : "+personne.getPrenom()); System.out.println("taille : "+personne.getTaille()); } catch (java.io.IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } // de main() }
A noter… • La classe ObjectInputStream possède aussi des méthodes pour lire des données de type primitives : • readInt(), readDouble(), readFloat ... • Lors de la déserialisation, le constructeur de l'objet n'est jamais utilisé.
Précisions • Une classe peut effectuer un traitement durant la sérialisation ou désérialisation en implémentant les méthodes suivantes : • private void writeObject (ObjectOutputStream stream) throws IOException; • private void readObject(ObjectInputStream stream)throws IOException, ClassNotFoundException;
Exceptions possibles • Si la classe a changée entre le moment ou elle a été sérialisée et le moment ou elle est déserialisée • une exception est levée : java.io.InvalidClassException • Une exception de type treamCorruptedException peut être levée si le fichier a été corrompu • par exemple en le modifiant avec un éditeur.
Exceptions possibles (suite) • Au cours de la déserialisation : Une exception de type ClassNotFoundException peut être levée si l'objet est transtypé vers une classe qui n'existe plus ou pas au moment de l'exécution. • Par ex. si on renomme Personne.class
Précisions (suite) • Lorsqu'un attribut d'une classe ne doit pas être sérialisé il faut le déclarer transient • Le contenu des attributs sont visibles dans le flux dans lequel est sérialisé l'objet. • Il est ainsi possible pour toute personne ayant accès au flux de voir le contenu de chaque attribut même si ceux si sont private : • Ex. : private transient String codeSecret; • Attention lors de la déserialisation, les champs transient sont initialisé avec la valeur null ! • Un attribut static n'est pas sérialisé • Les sous-classes d'une classe sérialisable le sont également
Classes utilitaires • Composants du package java.util fournis • sous forme de classe • ou sous forme d'interface • Quelques exemples • classes conteneurs Vector, Stack, HashMap • classes outils Random, Date • interfaces CollectionIteratorObserver
La classe Vector • Cette classe définit un conteneur de type tableau dynamique • il s'agit d'une collection d'objets de type quelconque • à laquelle on peut accéder soit par un index soit par un itérateur • la classe Vector implémente l'interface List ce qui permet de l'utiliser comme une liste • les instances stockées peuvent être des objets de classes différentes
Constructeurs de la classe Vector • Vector() //crée un vecteur vide • Vector(int capacitéInitiale) // crée un vecteur vide de capacitéinitialCapacité • Vector(int capacitéInitiale, int incrémenteCap ) // crée un vecteur vide de facteur d'accroissement spécifié • …
Classe Vector • Principales méthodes supportées: • ajout, insertion, suppression, recherche, extraction d'un élément Quelques méthodes de la classe Vector • Insertion d'un élément • boolean add(Object o) // ajoute l'élément à la fin du vecteur • void add(int Index, Object element) // insère l'élément donné en position donnée
Quelques méthodes de la classe Vector • Recherche - information • Object get(int index) // renvoie l'élément situé à la position indiquée • int indexOf(Object element) // recherche la première occurrence d'un élément- surcharge // nécessaire de la méthode equals / critère de recherche • int size() // renvoie le nombre de composants du vecteur • Suppression - Modification • Object remove(int index) // supprime l'élément situé à la position indiquée • boolean remove(Object o) // supprime la première occurrence de l'élément donné • void clear() // supprime tous les éléments du vecteur
Quelques méthodes de la classe Vector • Conversion d'un vecteur en un tableau • Object[] toArray() • Object[] toArray(Object[]tab) • Tri du conteneur • Spécifier que la classe des objets du conteneur implémente l'interface Comparable • Implémenter la méthode compareTo dans cette classe • Invoquer la méthode sort de la classe Collections Vector conteneur = new Vector(20,5); …. Collections.sort(conteneur);
Exemple type Object par défaut Transtypage obligatoire
Interface Comparable • Tous les objets qui doivent définir un ordre utilisé par le tri d'une collection • doivent implémenter cette interface. • Cette interface = une seule méthode : int compareTo(Object) • Cette méthode doit renvoyer : - une valeur entière négative si l'objet courant est inférieur à l'objet fourni - une valeur entière positive si l'objet courant est supérieur à l'objet fourni - une valeur nulle si l'objet courant est égal à l'objet fourni
Interface Comparable • Les classes wrappers (=Integer, Short, Double, etc), String et Date implémentent cette interface.
Les collections • Les collections en Java stockent toujours des références sur les objets contenus dans la collection et non les objets eux mêmes. • Ce sont obligatoirement des objets qui doivent être ajoutés dans une collection • Il n'est pas possible de stocker directement des types primitifs : il faut obligatoirement encapsuler ces données dans des wrappers.
Interfaces Java pour les collections Les interfaces à utiliser par des objets qui gèrent des collections sont : • Collection : interface qui est implementée par la plupart des objets qui gèrent des collections. • Map : interface qui définie des méthodes pour des objets qui gèrent des collections sous la forme clé/valeur • Set : interface pour des objets qui n'autorisent pas la gestion des doublons dans l'ensemble • List : interface pour des objets qui autorisent la gestion des doublons et un accès direct à un élément • SortedSet : interface qui étend l'interface Set et permet d'ordonner l'ensemble • SortedMap : interface qui étend l'interface Map et permet d'ordonner l'ensemble
Collections personnalisées • Le package Java pour les collections fournit aussi plusieurs classes abstraites • qui proposent une implémentation partielle d'une interface pour pouvoir créer une collection personnalisée : • AbstractCollection, AbstractList, AbstractMap, AbstractSequentialList et AbstractSet
Classe COLLECTIONS • La classe Collections propose plusieurs méthodes statiques • qui effectuent des opérations sur des collections • Ces traitements sont polymorphiques car ils demandent en paramètre un objet qui implémente une interface et retourne une collection • Ex. void copy(List, List) //copie tous les éléments de la seconde liste dans la première • Enumaration enumeration(Collection) // renvoie un objet Enumeration pour parcourir la collection • Object max(Collection) // renvoie le plus grand élément de la collection selon l'ordre naturel des éléments • Object max(Collection, Comparator) // renvoie le plus grand élément de la collection selon l'ordre naturel précisé par l'objet Comparator
Classe Collections (suite) • void sort(List) // trie la liste dans un ordre ascendant selon l'ordre des éléments defini par l'interface Comparable qui est donc obligatoire dans les élements constituant la liste !! • void sort(List, Comparator) // trie la liste dans un ordre ascendant selon l'ordre précisé par l'objet Comparator • Si un élément dans la liste n’implémente pas l'interface Comparable : • une exception de type ClassCastException est levée
Quelques méthodes de l’Interface Collection • boolean add(Object) // ajoute l'élément fourni en paramètre à la collection. La valeur de retour indique si la collection a été mise à jour • boolean addAll(Collection) // ajoute à la collection tous les éléments de la collection fournie en paramètre • void clear() // supprime tous les éléments de la collection • boolean contains(Object) // indique si la collection contient au moins un élément identique à celui fourni en paramètre • boolean containsAll(Collection) // indique si tous les éléments de la collection fournie en paramètre sont contenus dans la collection
Quelques méthodes de l’Interface Collection • boolean isEmpty() // indique si la collection est vide • Iterator iterator() // renvoie un objet qui permet de parcourir l'ensemble des éléments de la collection • boolean remove(Object) // supprime l'élément fourni en paramètre de la collection. La valeur de retour indique si la collection a été mise à jour • boolean removeAll(Collection) • supprime tous les éléments de la collection qui sont contenus dans la collection fournie en paramètre • int size() // renvoie le nombre d'éléments contenu dans la collection • Object[] toArray() // renvoie d'un tableau d'objets qui contient tous les éléments de la collection
Rappel sur la généricité • Problème du typage des données • chaque type a une représentation interne particulière • et un certain nombre d'opérateurs associés • conséquence: pour une même algorithme l'implémentation dépend du type • La généricité vise donc à s'affranchir du type des données • classes génériques • méthodes génériques
Java et la généricité • Pas de mécanisme dédié pour la généricité • comme en C++ avec les templates • Cependant en Java : • toutes les classes héritent implicitement de la classe Object • et en pratiquant le transtypage on peut transformer une instance de Object dans un type voulu • Ainsi, en définissant un modèle au moyen de la classe Object • il est possible de structurer des données de type quelconque
Java et la généricité • Mais l'absence pour l'instant de template en Java comporte des limites • En effet un template est modèle générique • il est expansé lors de son utilisation • la résolution de la définition appropriée des méthodes à utiliser est donc faite lors de l'utilisation du modèle
Java et la généricité • En Java la classe qui traduit le modèle est compilée, d'où un certain nombre de problèmes • dans le cas du clonage la seule méthode clone connue à la compilation est celle de Object qui ne fait qu'une copie superficielle • dans le cas d'une comparaison, tout dépend dépend de la sémantique de l'objet qui est inconnue au moment de la traduction