IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 11 - Le système d’E/S de Java

pages : 1 2 3 4 5 6 7 8 9 10 

La librairie qui supporte le format Zip est bien plus vaste. Avec elle vous pouvez facilement stocker des fichiers multiples, et il y a même une classe séparée pour amener le procédé de lecture d'un fichier Zip simple. La librairie utilise le format Zip standard de manière à ce qu'il fonctionne avec tous les outils couramment téléchargeables sur l'Internet. L'exemple suivant prend la même forme que l'exemple précédent, mais il manipule autant d'arguments de ligne de commande que vous le désirez. En plus, il met en valeur l'emploi de la classe Checksum pour calculer et vérifier la somme de contrôle [checksum] pour le fichier. Il y a deux sortes de Checksum : Adler32 (qui est rapide) et CRC32 (qui est plus lent mais légèrement plus précis).

//: c11:ZipCompress.java
// Emploi de la compression Zip pour compresser n'importe quel
// nombre de fichiers passés en ligne de commande.
import java.io.*;
import java.util.*;
import java.util.zip.*;

public class ZipCompress {
  // Lance les exeptions vers la console :
  public static void main(String[] args)
  throws IOException {
    FileOutputStream f =      new FileOutputStream("test.zip");
    CheckedOutputStream csum =      new CheckedOutputStream(
        f, new Adler32());
    ZipOutputStream out =      new ZipOutputStream(
        new BufferedOutputStream(csum));
    out.setComment("A test of Java Zipping");
    // Pas de getComment() correspondant, bien que.
    for(int i = 0; i < args.length; i++) {
      System.out.println(
        "Writing file " + args[i]);
      BufferedReader in =        new BufferedReader(
          new FileReader(args[i]));
      out.putNextEntry(new ZipEntry(args[i]));
      int c;
      while((c = in.read()) != -1)
        out.write(c);
      in.close();
    }
    out.close();
    // Validation de Checksum seulement après que
    // le fichier est été fermé !
    System.out.println("Checksum: " +
      csum.getChecksum().getValue());
    // Maintenant extrait les fichiers :
    System.out.println("Reading file");
    FileInputStream fi =       new FileInputStream("test.zip");
    CheckedInputStream csumi =      new CheckedInputStream(
        fi, new Adler32());
    ZipInputStream in2 =      new ZipInputStream(
        new BufferedInputStream(csumi));
    ZipEntry ze;
    while((ze = in2.getNextEntry()) != null) {
      System.out.println("Reading file " + ze);
      int x;
      while((x = in2.read()) != -1)
        System.out.write(x);
    }
    System.out.println("Checksum: " +
      csumi.getChecksum().getValue());
    in2.close();
    // Méthode alternative pour ouvrir et lire
    // les fichiers zip :
    ZipFile zf = new ZipFile("test.zip");
    Enumeration e = zf.entries();
    while(e.hasMoreElements()) {
      ZipEntry ze2 = (ZipEntry)e.nextElement();
      System.out.println("File: " + ze2);
      // ... et extrait les données comme précédemment.
    }
  }
} ///:~

Pour chaque fichier à ajouter à l'archive, vous devez appeler putNextEntry() et lui passer un objet ZipEntry. L'objet ZipEntry contient une interface extensible qui vous permet d'obtenir et de positionner toutes les données disponibles sur cette entrée précise dans votre fichier Zip : nom, tailles compressé et non-compressé, date, somme de contrôle CRC, données supplémentaires, commentaire, méthode de compression, et si il s'agit d'une entrée de répertoire. Toutefois, même si le format Zip possède une méthode pour établir un mot de passe, il n'est pas supporté dans la librairie Zip de Java. Et bien que CheckedInputStream et CheckedOutputStream supportent les deux contrôles de somme Adler32 et CRC32, la classe ZipEntry supporte seulement une interface pour la CRC (Contrôle de Redondance Cyclique). C'est une restriction sous-jacente du format Zip, mais elle pourrait vous limiter d'utiliser l'Adler32 plus rapide.

Pour extraire les fichiers, ZipInputStream a une méthode getNextEntry() qui renvoie la ZipEntry suivante si il y en a une. Comme alternative plus succincte, vous pouvez lire le fichier en utilisant un objet ZipFile, lequel possède une méthode entries() pour renvoyer une Enumeration auZipEntries.

Afin de lire la somme de contrôle vous devrez d'une manière ou d'une autre avoir accès à l'objet Checksum associé. Ici, une référence vers les objets CheckedOutputStream et CheckedInputStream est retenue, mais vous pourriez aussi juste vous en tenir à une référence à l'objet Checksum.

Une méthode déconcertante dans les flux de Zip est setComment(). Comme montré plus haut, vous pouvez établir un commentaire lorsque vous écrivez un fichier, mais il n'y a pas de manière pour récupérer le commentaire dans le ZipInputStream. Les commentaires sont apparemment complètement supportés sur une base d'entrée-par-entrée par l'intermédiaire de ZipEntry.

Bien sûr, vous n'êtes pas limité aux fichiers lorsque vous utilisez les librairies GZIP et Zip — vous pouvez compresser n'importe quoi, y compris les données a envoyer en passant par une connexion réseau.

ARchives Java (JARs)

Le format Zip est aussi employé dans le format de fichier JAR (Java ARchive), qui est une manière de rassembler un groupe de fichiers dans un seul fichier compressé, tout à fait comme Zip. Cependant, comme tout le reste en Java, les fichiers JAR sont multi-plate-forme donc vous n'avez pas a vous soucier des distributions de plate-forme. Vous pouvez aussi inclure des fichiers audio et image en plus des fichiers class.

Les fichiers JAR sont particulièrement utile quand on a affaire à l'Internet. Avant les fichiers JAR, votre navigateur Web devait faire des requêtes répétées sur un serveur Web afin de télécharger tous les fichiers qui composaient une applet. En plus, chacun de ces fichiers n'était pas compressé. En combinant tous les fichiers d'une applet précise dans un seul fichier JAR, une seule requête au serveur est nécessaire et le transfert est plus rapide en raison de la compression. Et chaque entrée dans un fichier JAR peut être signée digitalement pour la sécurité (se référer à la documentation de Java pour les détails).

Un JAR consiste en un seul fichier contenant une collection de fichiers zippés ensemble avec un « manifeste » qui en fait la description. (Vous pouvez créer votre propre fichier manifeste; sinon le programme jar le fera pour vous.) Vous pouvez trouver plus de précisions sur les manifestes dans la documentation HTML du JDK.

L'utilitaire jar qui est fourni avec le JDK de Sun compresse automatiquement les fichiers de votre choix. Vous lui faites appel en ligne de commande :

jar [options] destination [manifest] inputfile(s)

Les options sont simplement une collection de lettres (aucun trait d'union ou autre indicateur n'est nécessaire). Les utilisateurs noterons la similitude avec les options tar. Celles-ci sont :

c Crée une archive nouvelle ou vide.
t Établit la table des matières.
x Extrait tous les fichiers.
x file Extrait le fichier nommé.
f Dit : « Je vais vous donner le nom du fichier. » Si vous n'utilisez pas ceci, jar considère que sont entrée viendra de l'entrée standard, ou , si il crée un fichier, sa sortie ira vers la sortie standard.
m Dit que le premier argument sera le nom du fichier manifeste crée par l'utilisateur.
v Génère une sortie « verbose » décrivant ce que jar effectue.
0 Stocke seulement les fichiers; ne compresse pas les fichiers (utilisé pour créer un fichier JAR que l'on peut mettre dans le classpath).
M Ne crée pas automatiquement un fichier manifeste.

Si un sous-répertoire est inclus dans les fichiers devant être placés dans le fichier JAR, ce sous-répertoire est ajouté automatiquement, incluant tous ces sous-répertoire, etc. Les informations de chemin sont ainsi préservées.

Voici quelques façon typiques d'invoquer jar :

jar cf myJarFile.jar *.class

Ceci crée un fichier JAR appelé myJarFile.jar qui contient tous les fichiers class du répertoire courant, avec la génération automatique d'un fichier manifeste.

jar cmf myJarFile.jar myManifestFile.mf *.class

Comme l'exemple précèdent, mais ajoute un fichier manifeste crée par l'utilisateur nommé myManifestFile.mf.

jar tf myJarFile.jar

Produit une table des matières des fichiers dans myJarFile.jar.

jar tvf myJarFile.jar

Ajoute le drapeau « verbose » pour donner des informations plus détaillées sur les fichiers dans myJarFile.jar.

jar cvf myApp.jar audio classes image

Supposant que audio, classes, et image sont des sous-répertoires, ceci combine tous les sous-répertoires dans le fichier myApp.jar. Le drapeau « verbose » est aussi inclus pour donner contrôle d'information supplémentaire pendant que le programme jar travaille.

Si vous créez un fichier JAR en utilisant l'option o, ce fichier pourra être placé dans votre CLASSPATH :

CLASSPATH="lib1.jar;lib2.jar;"

Ainsi Java pourra chercher dans lib1.jar et lib2pour trouver des fichiers class.

L'outil jar n'est pas aussi utile que l'utilitaire zip. Par exemple, vous ne pouvez ajouter ou mettre à jour un fichier JAR existant; vous pouvez créer des fichiers JAR seulement à partir de zéro. Aussi, vous ne pouvez déplacer les fichiers dans un fichier JAR, les effaçant dès qu'ils sont déplacés. Cependant un fichier JAR crée sur une plate-forme sera lisible de manière transparente par l'outil jar sur n'importe quelle autre plate-forme (un problème qui apparaît parfois avec l'utilitaire zip.

Comme vous le verrez dans le chapitre 13, les fichiers JAR sont aussi utilisés pour emballer les JavaBeans.

La sérialisation objet

La sérialisation objet en Java vous permet de prendre n'importe quel objet qui implémente l'interface Serializable et le dévie en une séquence de bytes qui pourront ensuite être complètement restaurés pour régénérer l'objet original. C'est même vrai à travers un réseau, ce qui signifie que le mécanisme de sérialisation compense automatiquement des différences dans les systèmes d'exploitation. C'est à dire, vous pouvez créer un objet sur un machine Windows, le sérialiser, et l'envoyer à travers le réseau sur une machine Unix où il sera correctement reconstruit. Vous n'avez pas à vous soucier de la représentation des données sur les différentes machines, l'ordonnancement des bytes, ou tout autres détails.

Par elle-même, la sérialisation objet est intéressante parce qu'elle vous permet de mettre en application la persistance légère [lightweight persistence]. Rappelez-vous que la persistance signifie que la durée de vie de l'objet n'est pas déterminée tant qu'un programme s'exécute — l'objet vit dans l'intervalle des invocations du programme. En prenant un objet sérialisable et en l'écrivant sur le disque, puis en ressortant cet objet lors de la remise en route du programme, vous êtes alors capable de produire l'effet de persistance. La raison pour laquelle elle est appelée « légère » est que vous pouvez simplement définir un objet en employant un certain type de mot-clé pour la « persistance » et de laisser le système prendre soin des détails (bien que cela peut bien arriver dans le futur). À la place de cela, vous devrez sérialiser et désérialiser explicitement les objets dans votre programme.

La sérialisation objet a été ajoutée au langage pour soutenir deux caractéristiques majeures. La remote method invocation (RMI) de Java permet aux objets qui vivent sur d'autres machines de se comporter comme si ils vivaient sur votre machine. Lors de l'envoi de messages aux objets éloignés, la sérialisation d'objet est nécessaire pour transporter les arguments et les valeurs retournées. RMI est abordé au Chapitre 15.

La sérialisation des objets est aussi nécessaire pour les JavaBeans, décrit au Chapitre 13. Quand un Bean est utilisé, son information d'état est généralement configuré au moment de la conception. Cette information d'état doit être stockée et récupérée ultérieurement quand le programme est démarré; la sérialisation objet accomplit cette tâche.

Sérialiser un objet est assez simple, aussi longtemps que l'objet implémente l'interface Serializable (cette interface est juste un drapeau et n'a pas de méthode). Quand la sérialisation est ajoutée au langage, de nombreuses classes sont changés pour les rendre sérialisables, y compris tous les envelloppeurs [wrappers] pour les types de primitives, toutes les classes de récipients [container], et bien d'autres. Même les objets Class peuvent être sérialisés. (Voir le Chapitre 12 pour ce que cela implique.)

Pour sérialiser un objet, vous créez une sorte d'objet OutputStream et l'enveloppez ensuite dans un objet ObjectOutputStream. À ce point vous avez seulement besoin d'appeler writeObject() et votre objet est sérialisé et envoyé à l'OutputStream. Pour inverser le processus, vous enveloppez un InputStream dans un ObjectInputStream et appelez readObject(). Ce qui renvoie, comme d'habitude, une référence à un Objet dont on a sur-forcé le type [upcast], ainsi vous devrez sous-forcer pour préparer les objets directement.

Un aspect particulièrement astucieux de la sérialisation objet est qu'elle ne sauve pas uniquement une image de votre objet, mais cela s'occupe aussi de toutes les références contenues dans votre objet et sauve ces objets, et poursuit dans toutes les références de ces objets, etc. Ceci est parfois rapporté comme le « Web des objets » auquel un simple objet peut être connecté, et il comprend des tableaux de références aux objets aussi bien que d'objets membres. Si vous devez entretenir votre propre schéma de sérialisation, entretenir le code pour suivre tous ces liens serait un peu un casse-tête. Pourtant, la sérialisation d'objet Java semble s'en sortir sans faute, sans aucun doute en utilisant un algorithme optimalisé qui traverse le Web des objets. L'exemple suivant teste le mécanisme de sérialisation en créant un « vers » d'objets liés, chacun d'entre eux ayant un lien jusqu'au prochain segment dans le vers en plus d'un tableau de références aux objets d'une classe différent, Data :

//: c11:Worm.java
// Demontre la sérialisation des objets.
import java.io.*;

class Data implements Serializable {
  private int i;
  Data(int x) { i = x; }
  public String toString() {
    return Integer.toString(i);
  }
}

public class Worm implements Serializable {
  // Génère une valeur d'int aléatoire :
  private static int r() {
    return (int)(Math.random() * 10);
  }
  private Data[] d = {
    new Data(r()), new Data(r()), new Data(r())
  };
  private Worm next;
  private char c;
  // Value de i == nombre de segments
  Worm(int i, char x) {
    System.out.println(" Worm constructor: " + i);
    c = x;
    if(--i > 0)
      next = new Worm(i, (char)(x + 1));
  }
  Worm() {
    System.out.println("Default constructor");
  }
  public String toString() {
    String s = ":" + c + "(";
    for(int i = 0; i < d.length; i++)
      s += d[i].toString();
    s += ")";
    if(next != null)
      s += next.toString();
    return s;
  }
  // Lance les exeptions vers la console :
  public static void main(String[] args)
  throws ClassNotFoundException, IOException {
    Worm w = new Worm(6, 'a');
    System.out.println("w = " + w);
    ObjectOutputStream out =      new ObjectOutputStream(
        new FileOutputStream("worm.out"));
    out.writeObject("Worm storage");
    out.writeObject(w);
    out.close(); // Vide aussi la sortie
    ObjectInputStream in =      new ObjectInputStream(
        new FileInputStream("worm.out"));
    String s = (String)in.readObject();
    Worm w2 = (Worm)in.readObject();
    System.out.println(s + ", w2 = " + w2);
    ByteArrayOutputStream bout =      new ByteArrayOutputStream();
    ObjectOutputStream out2 =      new ObjectOutputStream(bout);
    out2.writeObject("Worm storage");
    out2.writeObject(w);
    out2.flush();
    ObjectInputStream in2 =      new ObjectInputStream(
        new ByteArrayInputStream(
          bout.toByteArray()));
    s = (String)in2.readObject();
    Worm w3 = (Worm)in2.readObject();
    System.out.println(s + ", w3 = " + w3);
  }
} ///:~

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Armel Fortun ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java