LECON 313


Les flux d'entrées / sorties (1/2)
Je vous préviens tout de suite, ce chapitre sera certainement le plus difficile de tous. Déjà, parce que vous allez voir beaucoup de nouvelles choses, mais surtout parce que la notion de fichier et de son traitement n'est pas évidente au premier abord.

Ne vous inquiétez pas trop tout de même : je vais tenter de vous expliquer tout cela dans le détail, mais là, je ne vous cache pas qu'il faudra de la pratique pour tout assimiler...
J'espère que le fait de découper cette partie en deux vous facilitera le travail.

Alors, pourquoi utiliser les entrées / sorties ?
Tout simplement parce qu'il peut être utile de sauvegarder des données, traitées par votre programme, afin de les réutiliser !
Je tiens à signaler que je ne vais pas trop approfondir le sujet. Sinon il faudrait tout une partie rien que les différents type de fichier (.zip, .properties...). Et je tiens à dire aux puristes que je ne ferai que mettre en garde contre les exceptions que la lecture ou l'écriture dans des fichiers peut engendrer !
Sommaire du chapitre :
  • Les flux : qu'est-ce donc ?
  • L'objet File
  • Les objets FileInputStream et FileOutputStream
  • Les flux filtrés : les classes FilterInputStream et FilterOutputStream
  • La sérialisation : les objets ObjectInputStream et ObjectOutputStream
  • Cas pratique
  • Ce qu'il faut retenir

Les flux : qu'est-ce donc ?

Avant de nous lancer dans la programmation avec des fichiers, nous devons voir ce que sont les entrées / sorties, ou communément ce qui est appelé les flux d'entrée / sortie.

Une entrée / sortie en Java consiste en un échange de données entre le programme et une autre source, qui peut être :
  • la mémoire ;
  • un fichier ;
  • le programme lui-même ;
  • ...

Pour réaliser cela, Java emploie ce qu'on appelle un stream (qui signifie flux).
Celui-ci joue le rôle de médiateur entre la source des données et sa destination.
Nous allons voir que Java met à notre disposition toute une panoplie d'objets afin de pouvoir communiquer de la sorte. Tout comme l'objet Scanner que je ne vous présente plus maintenant, ces objets sont rangés dans un package : java.io (io signifie ici in/out, pour entrée / sortie). Il n'y a pas loin d'une cinquantaine de classes dans ce package... Heureusement, nous n'allons pas toutes les aborder.

Toute opération sur les entrées / sorties, en Java, doit suivre le schéma suivant :
  • ouverture du flux ;
  • lecture du flux ;
  • fermeture du flux.


Je ne vous cache pas qu'il existe plusieurs objets différents qui ont tous des spécificités de travail avec les flux. Dans un souci de simplicité, nous n'aborderons que ceux qui traitent avec des fichiers .

Sachez aussi que Java a décomposé les objets traitant des flux en deux catégories :
  • les objets travaillant avec des flux d'entrée (in), lecture de flux ;
  • les objets travaillant avec des flux de sortie (out), écriture de flux.


Et comme je vous l'ai dit plus haut, il existe un objet Java pour chaque cas.
Par exemple, il existe :
  • l'objet FileInputStream pour ouvrir un flux vers un fichier en lecture ;
  • l'objet FileOutputStream pour ouvrir un flux vers un fichier en écriture ;
  • ...


Ces objets peuvent prendre une chaîne de caractères, précisant le chemin et le nom du fichier à utiliser, en paramètres de leurs constructeurs ! Cependant, il peut être de bon ton d'utiliser un objet File.
Celui-ci permet de faire des tests sur le fichier ou de récupérer des informations le concernant.

C'est par là que nous commencerons.

L'objet File

Avant de commencer, créez-vous un fichier, avec l'extension que vous voulez pour le moment, et enregistrez-le à la racine de votre projet Eclipse ! Personnellement, je me suis fait un fichier test.txt dont voici le contenu :
Code : Autre -
1
2
3
Voici une ligne de test.
Voici une autre ligne de test.
Et comme je suis motivé, en voici une troisième !

Dans votre projet Eclipse, vous pouvez faire un clic droit (sur le dossier de votre projet) / New / File. Vous pouvez saisir le nom de votre fichier et ensuite y écrire !


Le nom du dossier contenant mon projet s'appelle "IO", mon fichier .txt est à cette adresse "D:\Mes documents\Codage\SDZ\Java-SDZ\IO\test.txt".
Voyez :

Maintenant, nous allons voir ce que sait faire l'objet File.
Vous allez voir que cet objet est très simple à utiliser et que ses méthodes sont très explicites.
Pour commencer, nous allons retourner en mode console !

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Package à importer afin d'utiliser l'objet File
import java.io.File;
 
public class Main {
        public static void main(String[] args) {
               //Création de l'objet File
               File f = new File("test.txt");
               System.out.println("Chemin absolu du fichier : " + f.getAbsolutePath());
               System.out.println("Nom du fichier : " + f.getName());
               System.out.println("Est-ce qu'il existe ? " + f.exists());
               System.out.println("Est-ce un répertoire ? " + f.isDirectory());
               System.out.println("Est-ce un fichier ? " + f.isFile());
               
               System.out.println("Affichage des lecteurs racines du PC : ");
               for(File file : f.listRoots())
               {
                       System.out.println(file.getAbsolutePath());
                       try {
                               int i = 1;     
                               //On parcourt la liste des fichiers et répertoires
                               for(File nom : file.listFiles()){
                                              //S'il s'agit d'un dossier, on ajoute un "/"
                                              System.out.print("\t\t" + ((nom.isDirectory()) ? nom.getName()+"/" : nom.getName()));
                                              if((i%4) == 0){
                                                     System.out.print("\n");
                                              }
                                              i++;
                                      }
                                      System.out.println("\n");
                       } catch (NullPointerException e) {
                               //L'instruction peut générer un NullPointerException s'il n'y a
                               //pas de sous-fichier ! !
                       }
               }              
        }
}


Le résultat est bluffant :


Vous conviendrez que les méthodes de cet objet peuvent s'avérer très utiles !
Nous venons d'en essayer quelques-unes et nous avons même listé les sous-fichiers et sous-dossiers de nos lecteurs racines.

Vous pouvez aussi effacer le fichier grâce la méthode delete() , créer des répertoires avec la méthode mkdir() (le nom passé à l'objet ne devra pas avoir de ".")...

Maintenant que vous en savez un peu plus sur cet objet, nous allons pouvoir commencer à travailler avec notre fichier créé précédemment !

Les objets FileInputStream et FileOutputStream

C'est par le biais de ces objets que nous allons pouvoir :
  • lire dans un fichier ;
  • écrire dans un fichier.

Ces classes héritent mutuellement des classes abstraites InputStream et OutputStream, présentes dans le package java.io .
Ce sont deux des super-classes présentes dans ce package et une grande partie des objets traitant des flux d'entrée / sortie héritent de ces objets.


Comme vous l'avez sans doute remarqué, il y a une hiérarchie de classe pour les traitements in et une autre pour les traitements out.
Ne vous y trompez pas, les classes héritant de InputStream sont destinées à la lecture et les classes héritant de OutputStream se chargent de l'écriture !
C'est bizarre, j'aurais dit le contraire...

Oui, comme beaucoup de gens au début. Mais c'est uniquement parce que vous situez les flux par rapport à vous, et non à votre programme !

Lorsque ce dernier va lire des informations dans un fichier, ce sont des informations qu'il reçoit, et par conséquent, elles s'apparentent par conséquent à une entrée : in.
Sachez tout de même que, lorsque vous tapez au clavier, cette action est considérée comme un flux d'entrée !

Au contraire, lorsque celui-ci va écrire dans un fichier (ou à l'écran, souvenez-vous de System.out.println ), par exemple, il va faire sortir des informations : donc, pour lui, ce mouvement de données correspond à une sortie : out.

Nous allons enfin commencer à travailler avec notre fichier...
Le but est d'aller en lire le contenu et de le copier dans un autre, dont nous spécifierons le nom dans notre programme, par le biais d'un programme Java !

Ce code est assez compliqué... Donc accrochez-vous à vos claviers !

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//Package à importer afin d'utiliser les objets
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class Main {
        public static void main(String[] args) {
               //Nous déclarons nos objets en dehors du bloc try/catch
        FileInputStream fis;
        FileOutputStream fos;
        
        try {
                //On instancie nos objets. 
               //fis va lire le fichier et 
               //fos va écrire dans le nouveau !
                fis = new FileInputStream(new File("test.txt"));
                fos = new FileOutputStream(new File("test2.txt"));
 
                //On créer un tableau de byte 
                //pour dire de combien en combien on va lire les données
                byte[] buf = new byte[8];
                
                //On crée une variable de type int 
                //pour affecter le résultat de la lecture 
                //retourne -1 quand c'est fini
                int n = 0;
                
                //Tant que l'affectation dans la variable est possible, on boucle.
                //Lorsque le fichier est terminé, l'affectation n'est plus possible ! 
                //Donc on sort de la boucle.
                while((n = fis.read(buf)) >= 0)
                {
                        //On écrit dans notre deuxième fichier
                               //avec l'objet adéquat 
                        fos.write(buf);       
                        //On affiche ce qu'a lu notre boucle
                        //au format byte et au format char
                        for(byte bit : buf)
                               System.out.print("\t" + bit + "(" + (char)bit + ")");
                        
                        System.out.println("");
                }
                
                //On ferme nos flux de données
                fis.close();
                fos.close();
                System.out.println("Copie terminée !");
                
        } catch (FileNotFoundException e) {
               //Cette exception est levée 
               //si l'objet FileInputStream ne trouve aucun fichier
            e.printStackTrace();
        } catch (IOException e) {
               //Celle-ci se produit lors d'une erreur
               //d'écriture ou  de lecture
            e.printStackTrace();
        }      
    }
}

Pour que l'objet FileInputStream fonctionne, le fichier DOIT exister ! Sinon l'exception FileNotFoundException est levée.
Par contre, si vous ouvrez un flux en écriture (FileOutputStream) vers un fichier inexistant, celui-ci sera créé AUTOMATIQUEMENT !

Notez bien les imports pour pouvoir utiliser ces objets ! Mais comme vous le savez déjà, vous pouvez taper votre code et ensuite faire "control + shift + O" pour faire les imports automatiques.


À l'exécution de ce code, vous pouvez voir que le fichier "test2.txt" a bien été créé et qu'il contient exactement la même chose que "test.txt" !

De plus, j'ai ajouté dans la console les données que votre programme va utiliser (lecture et écriture).
Voici le résultat de ce code :


Les objets FileInputStream et FileOutputStream sont assez rudimentaires car ils travaillent avec un certain nombre d'octets à lire. Ceci explique pourquoi ma condition de boucle était si tordue...
Justement, tu ne pourrais pas nous expliquer un peu plus...

Mais bien sûr, je n'allais pas vous laisser comme ça...

Voici un rappel important.
Lorsque vous voyez des caractères dans un fichier ou sur votre écran, ceux-ci ne veulent pas dire grand-chose pour votre PC, car lui, il ne comprend que le binaire ! Vous savez, les suites de 0 et de 1...
Donc, afin de pouvoir afficher et travailler avec des caractères, un système d'encodage a été mis au point (qui a fort évolué d'ailleurs) !

Sachez qu'à chaque caractère que vous saisissez ou que vous lisez dans un fichier, correspond à un code binaire et ce code binaire correspond à un code décimal : voici la table de jeu de caractères.

Cependant, au début, seuls les caractères de a-z, de A-Z et les chiffres de 0-9 (127 premiers caractères de la table du lien ci-dessus) étaient codés (codage UNICODE 1) correspondant aux caractères se trouvant dans la langue anglaise mais, rapidement, ce codage s'est avéré trop limité pour des langues ayant des caractères accentués (français, espagnol...). Un jeu de codage étendu a été mis en place afin de pallier ce problème !

Chaque code binaire UNICODE 1 est codé sur 8 bits, soit 1 octet. Une variable de type byte, en Java, correspond en fait à 1 octet et non 1 bit !

Les objets que nous venons d'utiliser emploient la première version d'UNICODE 1 qui ne comprend pas les caractères accentués, c'est pour cela que les caractères de ce type, dans notre fichier, ont un code décimal négatif !
Lorsque nous définissons un tableau de byte à 8 entrées, cela signifie que nous allons lire 8 octets à la fois.

Vous pouvez voir qu'à chaque tour de boucle, notre tableau de byte contient huit valeurs correspondant chacune à un code décimal qui, lui, correspond à un caractère (valeur entre parenthèses à côté du code décimal).

Vous pouvez voir que les codes décimaux négatifs sont inconnus car ils sont représentés par "?" ; de plus, il y a des caractères invisibles dans notre fichier :
  • les espaces : SP pour SPace, code décimal 32 ;
  • les sauts de lignes : LF pour Line Feed, code décimal 13 ;
  • les retours chariot : CR pour Cariage Return, code décimal 10.


Il en existe d'autres ; en fait, les 32 premiers caractères du tableau des caractères sont invisibles !
On comprend mieux...

Vous voyez que le traitement des flux suivent une logique et une syntaxe précises !
Lorsque nous avons copié notre fichier, nous avons récupéré un certain nombre d'octets dans un flux entrant que nous avons passé à un flux sortant.
À chaque tour de boucle, le flux entrant est lu en suivant tandis que le flux sortant, lui, écrit dans un fichier en suivant.

Cependant, il existe à présent des objets beaucoup plus faciles à utiliser, mais ceux-ci travaillent tout de même avec les deux objets que nous venons de voir !:D
Ces objets font aussi partie de la hiérarchie citée précédemment... Seulement, il existe une super-classe définissant ceux-ci.

Continuons avec les flux filtrés.

Les flux filtrés : les classes FilterInputStream et FilterOutputStream

Ces deux classes sont en fait des classes abstraites. Elles définissent un comportement global pour ces classes filles qui, elles, permettent d'ajouter des fonctionnalités aux flux d'entrée / sortie !

Voici un diagramme de classe représentant la hiérarchie de classes :


Vous pouvez voir qu'il y a quatre classes filles héritant de FilterInputStream (idem pour FilterOutputStream) :
  • DataInputStream : offre la possibilité de lire directement des types primitifs (double, char, int), ceci grâce à des méthodes comme readDouble(), readInt()...
  • BufferedInputStream : cette classe permet d'avoir un tampon à disposition dans la lecture du flux. En gros, les données vont tout d'abord remplir le tampon et, dès que celui-ci est rempli, le programme a accès aux données ;
  • PushbackInputStream : permet de remettre un octet déjà lu dans le flux entrant ;
  • LineNumberInputStream : cette classe offre la possibilité de récupérer le numéro de ligne lue à un instant T.

Les classes dérivant de FilterOutputStream ont les mêmes fonctionnalités mais pour l'écriture !


Ces classes prennent une instance dérivant des classes InputStream (pour les classes héritant de FilterInputStream) ou de OutputStream (pour les classes héritant de FilterOutputStream)...

Vous pouvez cumuler les filtres, c'est-à-dire que, vu que ces classes acceptent une instance de leur super-classe, ils acceptent une instance de leur cousin !

Donc, vous pouvez avoir des choses du genre :

Code : Java -
1
2
3
4
5
6
FileInputStream fis = new FileInputStream(new File("toto.txt"));
DataInputStream dis = new DataInputStream(fis);
BufferedInputStream bis = new BufferedInputStream(dis);
 
//Ou en condensé : 
//BufferedInputStream  bis = new BufferredInputStream(new DataInputStream(FileInputStream(new File("toto.txt"))));


Afin de vous rendre compte des améliorations apportées par ces classes, nous allons lire un ÉNORME fichier texte (3.6Mo) de façon conventionnelle avec l'objet vu précédemment et avec un buffer !
Pour obtenir cet énorme fichier, rendez-vous à cette adresse. Faites un clic droit / Enregistrer sous... et remplacez le contenu de votre fichier test.txt par le contenu de ce fichier.
Maintenant, voici un code qui permet de tester le temps d'exécution de la lecture :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//Package à importer afin d'utiliser l'objet File
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class Main {
        public static void main(String[] args) {
               //Nous déclarons nos objets en dehors du bloc try/catch
        FileInputStream fis;
        BufferedInputStream bis;
        
        try {
               fis = new FileInputStream(new File("test.txt"));
                bis = new BufferedInputStream(new FileInputStream(new File("test.txt")));
                byte[] buf = new byte[8];
                
                //On récupère le temps du système
                long startTime = System.currentTimeMillis();
                //Inutile de faire des traitements dans notre boucle
                while(fis.read(buf) != -1);
                //On affiche le temps d'exécution
                System.out.println("Temps de lecture avec FileInputStream : " + (System.currentTimeMillis() - startTime));
                
                //On réinitialise                
                startTime = System.currentTimeMillis();
                //Inutile de faire des traitements dans notre boucle
                while(bis.read(buf) != -1);
                //On réaffiche
                System.out.println("Temps de lecture avec BufferedInputStream : " + (System.currentTimeMillis() - startTime));
                
                //On ferme nos flux de données
                fis.close();
                bis.close();
                
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }      
    }
}


Et le résultat est bluffant :


La différence de temps est ÉNORME ! 1.578 secondes pour la première méthode et 0.094 seconde pour la deuxième !
Vous conviendrez que l'utilisation d'un buffer permet une nette amélioration des performances de votre code !
Je vous conseille de faire le test pour l'écriture. D'ailleurs, nous allons le faire de ce pas :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//Package à importer afin d'utiliser l'objet File
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class Main {
        public static void main(String[] args) {
               //Nous déclarons nos objets en dehors du bloc try/catch
        FileInputStream fis;
        FileOutputStream fos;
        BufferedInputStream bis;
        BufferedOutputStream bos; 
        
        try {
               fis = new FileInputStream(new File("test.txt"));
               fos = new FileOutputStream(new File("test2.txt"));
               bis = new BufferedInputStream(new FileInputStream(new File("test.txt")));
                bos = new BufferedOutputStream(new FileOutputStream(new File("test3.txt")));
                byte[] buf = new byte[8];
                
                //On récupère le temps du system
                long startTime = System.currentTimeMillis();
                
                while(fis.read(buf) != -1){
                       fos.write(buf);
                }
                //On affiche le temps d'exécution
                System.out.println("Temps de lecture + écriture avec FileInputStream et FileOutputStream : " + (System.currentTimeMillis() - startTime));
                
                //On réinitialise                
                startTime = System.currentTimeMillis();
 
                while(bis.read(buf) != -1){
                       bos.write(buf);
                }
                //On réaffiche
                System.out.println("Temps de lecture + écriture avec BufferedInputStream et BufferedOutputStream : " + (System.currentTimeMillis() - startTime));
                
                //On ferme nos flux de données
                fis.close();
                bis.close();
                
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }      
    }
}

Là, la différence de performance devient démentielle :


7 secondes en temps normal et 0.1 seconde avec un buffer !
Si avec ça vous n'êtes pas convaincus de l'utilité des buffers !

Je ne vais pas passer en revue tous les objets cités un peu plus haut, mais vu que vous risquez d'utiliser les objets Data(Input/Output)Stream, nous allons les aborder rapidement, puisqu'ils s'utilisent comme les objets BufferedInputStream. Je vous ai dit plus haut que ceux-ci ont des méthodes de lecture pour chaque type primitif : cependant, il faut que le fichier soit généré par le biais d'un DataOutputStream pour que les méthodes fonctionnent correctement.

Nous allons donc créer un fichier de toute pièce pour le lire par la suite.

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//Package à importer afin d'utiliser l'objet File
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class Main {
        public static void main(String[] args) {
               //Nous déclarons nos objets en dehors du bloc try/catch
        DataInputStream dis;
        DataOutputStream dos;
        try {
               dos = new DataOutputStream(
                                      new BufferedOutputStream(
                                                     new FileOutputStream(
                                                                     new File("sdz.txt"))));
               
               //Nous allons écrire chaque primitif
               dos.writeBoolean(true);
               dos.writeByte(100);
               dos.writeChar('C');
               dos.writeDouble(12.05);
               dos.writeFloat(100.52f);
               dos.writeInt(1024);
               dos.writeLong(123456789654321L);
               dos.writeShort(2);
               dos.close();
               
               //On récupère maintenant les données !
               dis = new DataInputStream(
                               new BufferedInputStream(
                                              new FileInputStream(
                                                             new File("sdz.txt"))));
            
               System.out.println(dis.readBoolean());
               System.out.println(dis.readByte());
               System.out.println(dis.readChar());
               System.out.println(dis.readDouble());
               System.out.println(dis.readFloat());
               System.out.println(dis.readInt());
               System.out.println(dis.readLong());
               System.out.println(dis.readShort());
               
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }      
        }
}


Et le résultat


Le code est simple et clair et concis...
Vous avez pu constater que ce type d'objet ne manque pas de fonctionnalités ! Mais vous savez, jusqu'ici, nous ne travaillions qu'avec des types primitifs, mais vous pouvez aussi travailler avec des objets !

La sérialisation : les objets ObjectInputStream et ObjectOutputStream

La quoi ?

La sérialisation. C'est le nom que porte l'action de sauvegarder des objets !
Cela fait quelques temps déjà que vous utilisez des objets et, j'en suis sûr, vous auriez aimé que certains d'entre eux aient pu être réutilisés ensuite...

Le moment est venu de sauver vos objets d'une mort certaine !
Pour commencer, nous allons voir comment sérialiser un objet de notre composition.
Voici la classe avec laquelle nous allons travailler :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//package à importer
import java.io.Serializable;
 
public class Game implements Serializable{
        private String nom, style;
        private double prix;
        
        public Game(String nom, String style, double prix) {
               this.nom = nom;
               this.style = style;
               this.prix = prix;
        }
        
        public String toString(){
               return  "Nom du jeu : " + this.nom + 
                               "\nStyle de jeu : " + this.style + 
                               "\nPrix du jeu : " + this.prix + 
                               "\n";
        }       
}

Qu'est-ce que c'est que cette interface ? Tu n'as même pas implémenté de méthode !

En fait, cette interface n'a pas de méthode à redéfinir !
L'interface Serializable est ce qu'on appelle une interface marqueur !
Juste en implémentant cette interface dans un objet, Java sait que cet objet peut être sérialisé et j'irais même plus loin : si vous n'implémentez pas cette interface dans vos objets, ceux-ci ne pourront pas être sérialisés !
Par contre, si une super-classe implémente l'interface Serializable, ses enfants seront considérés comme sérialisables !

Vous savez quasiment tout...
Maintenant, voilà comment vont se passer les choses :
  • nous allons créer deux ou trois objets Game ;
  • nous allons les sérialiser dans un fichier de notre choix ;
  • nous allons ensuite les dé-sérialiser afin de pouvoir les réutiliser.

Nous allons donc pouvoir faire ceci grâce aux objets ObjectInputStream et ObjectOutputStream ?

Tout à fait !
Vous avez sûrement déjà deviné comment se servir de ces objets, mais nous allons tout de même travailler sur un exemple. D'ailleurs, le voici :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//Package à importer afin d'utiliser l'objet File
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class Main {
        public static void main(String[] args) {
               //Nous déclarons nos objets en dehors du bloc try/catch
        ObjectInputStream ois;
        ObjectOutputStream oos;
        try {
               oos = new ObjectOutputStream(
                                      new BufferedOutputStream(
                                                     new FileOutputStream(
                                                                     new File("game.txt"))));
               
               //Nous allons écrire chaque objet Game dans le fichier
               oos.writeObject(new Game("Assassin Creed", "Aventure", 45.69));
               oos.writeObject(new Game("Tomb Raider", "Plateforme", 23.45));
               oos.writeObject(new Game("Tetris", "Stratégie", 2.50));
               //NE PAS OUBLIER DE FERMER LE FLUX ! ! !
               oos.close();
               
               //On récupère maintenant les données !
               ois = new ObjectInputStream(
                               new BufferedInputStream(
                                              new FileInputStream(
                                                             new File("game.txt"))));
            
               try {
                               System.out.println("Affichage des jeux :");
                               System.out.println("*************************\n");
                       System.out.println(((Game)ois.readObject()).toString());
                               System.out.println(((Game)ois.readObject()).toString());
                               System.out.println(((Game)ois.readObject()).toString());
                       } catch (ClassNotFoundException e) {
                               e.printStackTrace();
                       }
                       ois.close();
               
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }      
     }
}

La dé-sérialisation d'objet peut engendrer une ClassNotFoundException ! Pensez donc à capturer cette exception !

Et le résultat :

VICTOIRE !
Nos objets sont enregistrés et nous avons réussi à ré-utiliser ceux-ci après enregistrement !

Ce qu'il se passe est simple : les données de vos objets sont enregistrés dans le fichier ; mais que se passerait-il si notre objet Game avait un autre objet de votre composition en son sein ?

Voyons ça tout de suite. Créez la classe Notice comme suit :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Notice {
        private String langue ;
        public Notice(){
               this.langue = "Français";
        }
        public Notice(String lang){
               this.langue = lang;
        }
        public String toString() {
               return "\t Langue de la notice : " + this.langue + "\n";
        }
}


Nous allons maintenant implémenter une notice par défaut dans notre objet Game. Voici notre classe modifiée :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.Serializable;
 
public class Game implements Serializable{
        private String nom, style;
        private double prix;
        private Notice notice;
        
        public Game(String nom, String style, double prix) {
               this.nom = nom;
               this.style = style;
               this.prix = prix;
               this.notice = new Notice();
        }
        
        public String toString(){
               return  "Nom du jeu : " + this.nom + 
                               "\nStyle de jeu : " + this.style + 
                               "\nPrix du jeu : " + this.prix + 
                               "\n";
        }       
}


Réessayez votre code sauvegardant vos objets Game. Et voici le résultat :


Eh oui, votre code ne compile plus ! Ceci pour une bonne raison : votre objet Notice n'est pas sérialisable !
Une erreur de compilation est levée !
Maintenant, deux choix s'offrent à vous :
  • soit vous faites en sorte de rendre votre objet sérialisable ;
  • soit vous spécifiez dans votre classe Game que la variable "notice" n'a pas à être sérialisée !

Pour la première option, c'est simple, il suffit d'implémenter l'interface sérialisable dans notre classe Notice.
Par contre, on ne voit pas comment dire qu'un attribut n'a pas à être sérialisé...

Vous allez voir que c'est très simple, il suffit de déclarer votre variable : transient.

Comme ceci :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.Serializable;
 
public class Game implements Serializable{
        private String nom, style;
        private double prix;
        //Maintenant, cette variable ne sera pas sérialisée
        //Elle sera tout bonnement ignorée ! !
        private transient Notice notice;
        
        public Game(String nom, String style, double prix) {
               this.nom = nom;
               this.style = style;
               this.prix = prix;
               this.notice = new Notice();
        }
        
        public String toString(){
               return  "Nom du jeu : " + this.nom + 
                               "\nStyle de jeu : " + this.style + 
                               "\nPrix du jeu : " + this.prix +
                               "\n";
        }       
}

Vous aurez sans doute remarqué que nous n'utilisons pas la variable notice dans la méthode toString() de notre objet Game.
Si vous faites ceci, que vous sérialisez puis dé-sérialisez vos objets, vous aurez une NullPointerException à l'invocation de la dite méthode.
Eh oui ! L'objet Notice est ignoré : il n'existe donc pas !

C'est simple, n'est-ce pas ?
Pour ceux que la sérialisation XML intéresse, je vous propose d'aller faire un tour sur ce cours. Il est très bien fait et vous apprendrez beaucoup de choses.


Ce que je vous propose maintenant, c'est d'appliquer cela avec un cas concret : nous allons sauvegarder les zolis dessins que nous pouvons faire sur notre ardoise mazique...

Pour ceux qui n'auraient pas gardé le projet, je propose donc d'aller refaire un tour sur le TP en question.


Cas pratique

Vu que, dans ce projet, les dessins se font grâce à l'objet Point, c'est celui-ci que nous allons sérialiser !
Je ne vais pas seulement vous proposer d'en sérialiser un seul, mais plusieurs et dans différents fichiers afin que vous puissiez réutiliser différents objets.

Nous allons avoir besoin d'un objet très pratique pour réussir ceci : un JFileChooser !
Cet objet affiche une mini-fenêtre demandant à l'utilisateur de choisir l'endroit où ouvrir ou sauvegarder des données ! Il possède aussi tout une batterie de méthodes très intéressante :
  • showOpenDialog() : affiche une fenêtre d'ouverture de fichier ;
  • showSaveDialog() : ouvre une fenêtre de sauvegarde ;
  • getSelectedFile() : retourne le fichier sélectionné par l'utilisateur ;
  • getSelectedFiles() : retourne les fichiers sélectionnés par l'utilisateur. S'il y a plusieurs sélections...
  • ...

Grâce à tout ceci, vous pourrez enregistrer vos objets dans des fichiers.
Vous devez savoir que l'objet JFileChooser est très laxiste en matière d'extension de fichier.
Il ne connaît rien du contenu des fichiers qu'il traite... Donc, rien ne vous empêchera de sauvegarder vos fichiers avec l'extension .exe ou encore .java et de les relire !


Afin d'avoir une cohérence dans nos fichiers de sauvegarde, nous allons spécifier à notre JFileChooser qu'il ne devra sauvegarder et lire que des fichiers ayant une certaine extension !
Pour faire ceci, nous devons créer une classe à part entière héritant de la classe FileFilter car celle-ci est une classe abstraite.

Cette classe permet de redéfinir deux méthodes :
  • accept(File file) : retourne vrai si le fichier est accepté ;
  • getDescription() : retourne la description de l'extension de fichier.


Voici donc la classe ZFileFilter que nous allons utiliser afin de créer des filtres pour nos fichiers :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.io.File;
import javax.swing.filechooser.FileFilter;
 
public class ZFileFilter extends FileFilter {
 
        private String extension = ".sdz", description = "Fichier Ardoise Mazique";
        
        public ZFileFilter(String ext, String descrip){
               this.extension = ext;
               this.description = descrip;
        }
        
        public boolean accept(File file){
               return (file.isDirectory() || file.getName().endsWith(this.extension));
        }
        
        public String getDescription(){
               return this.extension + " - " + this.description;
        }       
}


Maintenant nous pouvons créer des filtres !
Voici les codes source que vous devriez parfaitement comprendre vu qu'il n'y a pas beaucoup de nouveautés !
Point.java


Code : Java
import java.awt.Color;
import java.io.Serializable;


public class Point implements Serializable{

                //Couleur du point
                private Color color = Color.red;
                //Taille
                private int size = 10;
                //position sur l'axe X : initialisé au dehors du conteneur
                private int x = -10;
                //Position sur l'axe Y : initialisé au dehors du conteneur
                private int y = -10;
                //Type de point
                private String type = "ROND";
               
                /**
                 * Constructeur par défaut
                 */
                public Point(){}
                /**
                 * Constructeur avec paramètre
                 * @param x
                 * @param y
                 * @param size
                 * @param color
                 * @param type
                 */
                public Point(int x, int y, int size, Color color, String type){
                               this.size = size;
                               this.x = x;
                               this.y = y;
                               this.color = color;
                               this.type = type;
                }
               
                //****************************************
                //                                                           ACCESSEURS
                //****************************************
                public Color getColor() {
                               return color;
                }
                public void setColor(Color color) {
                               this.color = color;
                }
                public int getSize() {
                               return size;
                }
                public void setSize(int size) {
                               this.size = size;
                }
                public int getX() {
                               return x;
                }
                public void setX(int x) {
                               this.x = x;
                }
                public int getY() {
                               return y;
                }
                public void setY(int y) {
                               this.y = y;
                }
                public String getType() {
                               return type;
                }
                public void setType(String type) {
                               this.type = type;
                }
}


DrawPanel.java

J'ai rajouté des accesseurs pour la collection de points à sauvegarder !

Code : Java -

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import javax.swing.JPanel;


public class DrawPanel extends JPanel{

                //Couleur du pointeur
                private Color pointerColor = Color.red;
                //Forme du pointeur
                private String pointerType = "CIRCLE";
                //Position X du pointeur
                private int posX = -10, oldX = -10;
                //Position Y du pointeur
                private int posY = -10, oldY = -10;
                //pour savoir si on doit dessiner ou non
                private boolean erasing = true;
                //Taille du pointeur
                private int pointerSize = 15;
                //Collection de points !
                private ArrayList<Point> points = new ArrayList<Point>();         
               
                /**
                 * Constructeur
                 */
                public DrawPanel(){
                              
                               this.addMouseListener(new MouseAdapter(){
                                               public void mousePressed(MouseEvent e){
                                                               points.add(new Point(e.getX() - (pointerSize / 2), e.getY() - (pointerSize / 2), pointerSize, pointerColor, pointerType));
                                                               repaint();
                                               }
                               });

                               this.addMouseMotionListener(new MouseMotionListener(){
                                               public void mouseDragged(MouseEvent e) {
                                                               //On récupère les coordonnées de la souris
                                                               //et on enlève la moitié de la taille du pointeur
                                                               //pour centrer le tracé
                                                               points.add(new Point(e.getX() - (pointerSize / 2), e.getY() - (pointerSize / 2), pointerSize, pointerColor, pointerType));
                                                               repaint();
                                               }

                                               public void mouseMoved(MouseEvent e) {}
                               });
                              
                }
               
                /**
                 * Vous la connaissez maintenant, celle-là ;)
                 */         
                public void paintComponent(Graphics g) {
                              
                               g.setColor(Color.white);
                               g.fillRect(0, 0, this.getWidth(), this.getHeight());
                              
                               //Si on doit effacer, on ne passe pas dans le else => pas de dessin
                               if(this.erasing){
                                               this.erasing = false;
                               }
                               else{
                                               //On parcourt notre collection de points
                                               for(Point p : this.points)
                                               {
                                                               //On récupère la couleur
                                                               g.setColor(p.getColor());
                                                              
                                                               //Selon le type de point
                                                               if(p.getType().equals("SQUARE")){
                                                                              g.fillRect(p.getX(), p.getY(), p.getSize(), p.getSize());
                                                               }
                                                               else{
                                                                              g.fillOval(p.getX(), p.getY(), p.getSize(), p.getSize());
                                                               }
                                               }
                               }                                                            
                }
               
                /**
                 * Efface le contenu
                 */
                public void erase(){
                               this.erasing = true;
                                this.points = new ArrayList<Point>();
                               repaint();
                }
               
                /**
                 * Définit la couleur du pointeur
                 * @param c
                 */
                public void setPointerColor(Color c){
                               this.pointerColor = c;
                }
               
                /**
                 * Définit la forme du pointeur
                 * @param str
                 */
                public void setPointerType(String str){
                               this.pointerType = str;
                }

                public ArrayList<Point> getPoints() {
                               return points;
                }

                public void setPoints(ArrayList<Point> points) {
                               this.points = points;
                               repaint();
                }             
                              
}


Fenetre.java


Code : Java

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;


public class Fenetre extends JFrame {

                //**************************************
                //                                    LE MENU
                //**************************************
                private JMenuBar menuBar = new JMenuBar();
                JMenu fichier = new JMenu("Fichier"),
                                               edition = new JMenu("Edition"),
                                               forme = new JMenu("Forme du pointeur"),
                                               couleur = new JMenu("Couleur du pointeur");
               
                JMenuItem        nouveau = new JMenuItem("Effacer"),
                                                              
                                                               //Nos nouveaux points de menus
                                                               enregistrer = new JMenuItem("Enregistrer"),
                                                               enregistrerSous = new JMenuItem("Enregistrer Sous"),
                                                               ouvrir = new JMenuItem("Ouvrir"),
                                                              
                                                               quitter = new JMenuItem("Quitter"),
                                                               rond = new JMenuItem("Rond"),
                                                               carre = new JMenuItem("Carré"),
                                                               bleu = new JMenuItem("Bleu"),
                                                               rouge = new JMenuItem("Rouge"),
                                                              vert = new JMenuItem("Vert");
               
                //****************************************
                //                                           LA BARRE D OUTILS
                //****************************************
               
                JToolBar toolBar = new JToolBar();
               
                JButton square = new JButton(new ImageIcon("images/carré.jpg")),
                                               circle = new JButton(new ImageIcon("images/rond.jpg")),
                                               red = new JButton(new ImageIcon("images/rouge.jpg")),
                                               green = new JButton(new ImageIcon("images/vert.jpg")),
                                               blue = new JButton(new ImageIcon("images/bleu.jpg"));
               
                //***************************************
                //                                           LES ÉCOUTEURS
                //***************************************
                private FormeListener fListener = new FormeListener();
                private CouleurListener cListener = new CouleurListener();
               
                //Le JFileChooser
                //ici j'ai créé un répertoire "backup" à la racine de mon projet
                //En le spécifiant dans le constructeur, mon chooser s'ouvrira dans ce répertoire !
                JFileChooser fileChooser = new JFileChooser("backup/");
                //Nos filtres
                ZFileFilter zFiltre = new ZFileFilter();
                ZFileFilter filtre = new ZFileFilter(".amz", "Fichier Ardoise Mazique");
                File file;
               
                //Notre zone de dessin
                private DrawPanel drawPanel = new DrawPanel();
               
                public Fenetre(){
                               this.setSize(700, 500);
                               this.setLocationRelativeTo(null);
                               this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               
                               //On ajoute nos filtres sur l'objet !
                               this.fileChooser.addChoosableFileFilter(zFiltre);
                               this.fileChooser.addChoosableFileFilter(filtre);
                              
                              
                               //On initialise le menu
                               this.initMenu();
                               //Idem pour la barre d'outils
                               this.initToolBar();
                               //On positionne notre zone de dessin
                               this.getContentPane().add(drawPanel, BorderLayout.CENTER);
                               this.setVisible(true);                    
                }
               
                /**
                 * Initialise le menu
                 */
                private void initMenu(){
                               nouveau.addActionListener(new ActionListener(){
                                               public void actionPerformed(ActionEvent arg0) {
                                                               drawPanel.erase();
                                               }                                            
                               });
                              
                               nouveau.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK));
                              
                               quitter.addActionListener(new ActionListener(){
                                               public void actionPerformed(ActionEvent arg0) {
                                                               System.exit(0);
                                               }                                            
                               });
                               quitter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK));
                              
                               fichier.add(nouveau);
                               fichier.addSeparator();
                              
                               //On ajoute les nouveau menus !
                               enregistrer.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK));
                               //On détermine l'action à faire !
                               enregistrer.addActionListener(new ActionListener(){

                                               public void actionPerformed(ActionEvent arg0) {
                                                               ObjectOutputStream oos ;
                                                               //S'il ne s'agit pas du premier enregistrement !
                                                               if(file != null){
                                                                              try {
                                                                                             
                                                                                              oos = new ObjectOutputStream(new FileOutputStream(file));
                                                                                              oos.writeObject(drawPanel.getPoints());
                                                                                              oos.close();
                                                                                             
                                                                              } catch (FileNotFoundException e) {
                                                                                              e.printStackTrace();
                                                                              } catch (IOException e) {
                                                                                              e.printStackTrace();
                                                                              }
                                                               }
                                                               //Sinon on demande le nom du fichier
                                                               else{
                                                                              if(fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION){
                                                                                              file = fileChooser.getSelectedFile();
                                                                                              //Si l'extension est valide
                                                                                              if(fileChooser.getFileFilter().accept(file))
                                                                                              {
                                                                                                              try {
                                                                                                                            
                                                                                                                             oos = new ObjectOutputStream(new FileOutputStream(file));
                                                                                                                             oos.writeObject(drawPanel.getPoints());
                                                                                                                             oos.close();
                                                                                                                            
                                                                                                              } catch (FileNotFoundException e) {
                                                                                                                             e.printStackTrace();
                                                                                                              } catch (IOException e) {
                                                                                                                             e.printStackTrace();
                                                                                                              }
                                                                                              }                                                                                           
                                                                                              else{
                                                                                                              //Si vous n'avez pas spécifié une extension valide !
                                                                                                              JOptionPane alert = new JOptionPane();
                                                                                                              alert.showMessageDialog(null, "Erreur d'extension de fichier ! \nVotre sauvegarde a échoué !", "Erreur", JOptionPane.ERROR_MESSAGE);
                                                                                              }                                                                                           
                                                                              }
                                                               }
                                               }                                            
                               });                          
                               fichier.add(enregistrer);
                              
                               enregistrerSous.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
                               //On détermine l'action à faire !
                               enregistrerSous.addActionListener(new ActionListener(){

                                               public void actionPerformed(ActionEvent arg0) {
                                                               if(fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION){
                                                                              file = fileChooser.getSelectedFile();
                                                                              //Si l'extension est valide
                                                                              if(fileChooser.getFileFilter().accept(file))
                                                                              {
                                                                                              try {
                                                                                                             
                                                                                                              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
                                                                                                              oos.writeObject(drawPanel.getPoints());
                                                                                                              oos.close();
                                                                                                             
                                                                                              } catch (FileNotFoundException e) {
                                                                                                              e.printStackTrace();
                                                                                              } catch (IOException e) {
                                                                                                             e.printStackTrace();
                                                                                              }
                                                                              }
                                                                              else{
                                                                                              //Si vous n'avez pas spécifié une extension valide !
                                                                                              JOptionPane alert = new JOptionPane();
                                                                                              alert.showMessageDialog(null, "Erreur d'extension de fichier ! \nVotre sauvegarde a échoué !", "Erreur", JOptionPane.ERROR_MESSAGE);
                                                                              }
                                                               }
                                               }                                            
                               });
                               fichier.add(enregistrerSous);
                              
                               ouvrir.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK));
                               ouvrir.addActionListener(new ActionListener(){
                                               public void actionPerformed(ActionEvent e){
                                                               if(fileChooser.showOpenDialog(null) ==JFileChooser.APPROVE_OPTION){
                                                                              file = fileChooser.getSelectedFile();
                                                                              if(fileChooser.getFileFilter().accept(file))
                                                                              {
                                                                                              try {
                                                                                                             
                                                                                                              ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
                                                                                                              drawPanel.setPoints((ArrayList<Point>)ois.readObject());
                                                                                                              ois.close();
                                                                                                             
                                                                                              } catch (FileNotFoundException e1) {
                                                                                                              e1.printStackTrace();
                                                                                              } catch (IOException e1) {
                                                                                                              e1.printStackTrace();
                                                                                              } catch (ClassNotFoundException e2) {
                                                                                                              e2.printStackTrace();
                                                                                              }
                                                                              }
                                                                              else{
                                                                                              JOptionPane alert = new JOptionPane();
                                                                                              alert.showMessageDialog(null, "Erreur d'extension de fichier ! \nVotre chargement a échoué !", "Erreur", JOptionPane.ERROR_MESSAGE);
                                                                              }
                                                               }
                                               }
                               });
                               fichier.add(ouvrir);
                               fichier.addSeparator();
                              
                               fichier.add(quitter);
                               fichier.setMnemonic('F');
                              
                               carre.addActionListener(fListener);
                               rond.addActionListener(fListener);
                               forme.add(rond);
                               forme.add(carre);
                              
                               rouge.addActionListener(cListener);
                               vert.addActionListener(cListener);
                               bleu.addActionListener(cListener);
                               couleur.add(rouge);
                               couleur.add(vert);
                               couleur.add(bleu);
                              
                               edition.setMnemonic('E');
                               edition.add(forme);
                               edition.addSeparator();
                               edition.add(couleur);
                              
                               menuBar.add(fichier);
                               menuBar.add(edition);
                              
                               this.setJMenuBar(menuBar);
                }
               
                /**
                 * Initialise la barre d'outils
                 */
                private void initToolBar(){
                              
                               JPanel panneau = new JPanel();
                               square.addActionListener(fListener);
                               circle.addActionListener(fListener);
                               red.addActionListener(cListener);
                               green.addActionListener(cListener);
                               blue.addActionListener(cListener);
                              
                               toolBar.add(square);
                               toolBar.add(circle);
                              
                               toolBar.addSeparator();
                               toolBar.add(red);
                               toolBar.add(blue);
                               toolBar.add(green);
                                                                                             
                               this.getContentPane().add(toolBar, BorderLayout.NORTH);
                }
               
               
                //ÉCOUTEUR POUR LE CHANGEMENT DE FORME
                //************************************
                class FormeListener implements ActionListener{
                               public void actionPerformed(ActionEvent e) {
                                               if(e.getSource().getClass().getName().equals("javax.swing.JMenuItem")){
                                                               if(e.getSource()==carre)drawPanel.setPointerType("SQUARE");
                                                               else drawPanel.setPointerType("CIRCLE");
                                               }
                                               else{
                                                               if(e.getSource()==square)drawPanel.setPointerType("SQUARE");
                                                               else drawPanel.setPointerType("CIRCLE");                                                      
                                               }
                               }                             
                }
               

                //ÉCOUTEUR POUR LE CHANGEMENT DE COULEUR
                //**************************************
                class CouleurListener implements ActionListener{
                               public void actionPerformed(ActionEvent e) {
                                               System.out.println(e.getSource().getClass().getName());
                                               if(e.getSource().getClass().getName().equals("javax.swing.JMenuItem")){
                                                               System.out.println("OK !");
                                                               if(e.getSource()==vert)drawPanel.setPointerColor(Color.green);
                                                               else if(e.getSource()==bleu)drawPanel.setPointerColor(Color.blue);
                                                               else drawPanel.setPointerColor(Color.red);
                                               }
                                               else{
                                                               if(e.getSource()==green)drawPanel.setPointerColor(Color.green);
                                                               else if(e.getSource()==blue)drawPanel.setPointerColor(Color.blue);
                                                               else drawPanel.setPointerColor(Color.red);                                                    
                                               }
                               }                             
                }
               
               
                public static void main(String[] args){
                               Fenetre fen = new Fenetre();
                }
                              
}


Vous pouvez tester ce code, il fonctionne très bien !
Vous êtes obligés d'écrire le nom de fichier avec extension pour la sauvegarde et l'ouverture, sinon le traitement sera refusé !

Si vous voulez que l'extension soit ajoutée automatiquement à votre nom de fichier, vous devez créer votre propre JFileChooser et redéfinir la méthode approveSelection() .

Bon : je crois que le moment est venu d'aller faire un tour sur le topo !

Ce qu'il faut retenir

  • Les classes traitant des entrées / sorties se trouvent dans le package java.io .
  • Les classes que nous avons vues dans ce chapitre sont héritées des classes :
    • InputStream, pour les classes gérant les flux en entrées ;
    • OutputStream, pour les classes gérant les flux en sorties.
  • La façon dont on travaille avec des flux doit respecter la logique suivante :
    • ouverture de flux ;
    • lecture / écriture de flux ;
    • fermeture de flux.
  • La gestion des flux peut engendrer la levée d'exception comme : FileNotFoundException, IOException...
  • L'action de sauvegarder les objets s'appelle la sérialisation !
  • Pour qu'un objet soit sérialisable, celui-ci doit implémenter l'interface Serializable.
  • Si un objet sérialisable a un objet d'instance non sérialisable, une exception sera levée lorsque vous voudrez sauvegarder votre objet.
  • Une solution consiste à rendre l'objet d'instance sérialisable ou alors à le déclarer transient afin que celui-ci soit ignoré à la sérialisation !
  • L'utilisation de buffer permet une nette amélioration des performances, en lecture et en écriture, avec les fichiers.
Vous avez pu voir que ce chapitre était très riche en informations et en nouveautés.
Quand je vous disais que le rythme allait s'accélérer...

Je vous rappelle tout de même que les classes que nous venons de voir dans ce chapitre héritent des classes InputStream et OutputStream.
Il existe encore des classes dans le package java.io qui sont très utiles aussi, mais qui n'héritent pas des mêmes classes...

Vous êtes encore motivés ? Alors, allons-y et terminons-en avec ces maudits flux.


0 comments to "LECON 313"

Post a Comment

Powered by Blogger.

About This Blog

Aller au debut de la page