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 |
Sources & Récipients: Classe Java 1.0 | Classe Java 1.1 correspondante |
InputStream | Reader convertisseur : InputStreamReader |
OutputStream | Writer convertisseur : OutputStreamWriter |
FileInputStream | FileReader |
FileOutputStream | FileWriter |
StringBufferInputStream | StringReader |
(pas de classe correspondante) | StringWriter |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
PipedInputStream | PipedReader |
PipedOutputStream | PipedWriter |
En général, vous constaterez que les interfaces pour les deux différentes hiérarchies sont semblables sinon identiques.
Pour les InputStreams et OutputStreams, les flux sont adaptés à des usages particuliers en utilisant des sous-classes « décoratives » de FilterInputStream et FilterOutputStream. La hiérarchie de classe Reader et Writer poursuit l'usage de ce concept — mais pas exactement.
Dans le tableau suivant, la correspondance est une approximation plus grossière que dans la table précédente. La différence est engendrée par l'organisation de la classe : Quand BufferedOutputStream est une sous-classe de FilterOutputStream, BufferedWriter n'est pas une sous-classe de FilterWriter (laquelle, bien qu'elle soit abstract, n'a pas de sous-classe et donc semble avoir été mise dedans de manière à réserver la place ou simplement de manière à ce que vous ne sachiez pas où elle se trouve). Cependant, les interfaces pour les classes est plutôt un combat terminé.
Filtres : classe Java 1.0 | Classe correspondante en Java 1.1 |
---|---|
FilterInputStream | FilterReader |
FilterOutputStream | FilterWriter (classe abstract sans sous-classe) |
BufferedInputStream | BufferedReader (a aussi readLine()) |
BufferedOutputStream | BufferedWriter |
DataInputStream | Utilise DataInputStream (sauf quand vous voulez utiliser readLine(), alors vous devez utiliser un BufferedReader) |
PrintStream | PrintWriter |
LineNumberInputStream | LineNumberReader |
StreamTokenizer | StreamTokenizer (utilise un constructeur qui prend un Reader à la place) |
PushBackInputStream | PushBackReader |
Il y a un sens qui est tout à fait clair : Chaque fois que vous voulez utiliser readLine(), vous ne devrez plus le faire avec un DataInputStream (ceci recevant un message de depréciation au moment de la compilation), mais utiliser à la place un BufferedReader. À part cela, DataInputStream est pourtant l'élément « préféré » de la bibliothèque d'E/S.
Pour faire la transition vers l'emploi facile d'un PrintWriter, il possède des constructeurs qui prennent n'importe quel objet OutputStream, aussi bien que des objets Writer. Cependant, PrintWriter n'a pas plus de support pour formater comme le faisait PrintStream ; les interfaces sont de fait les mêmes.
Le constructeur de PrintWriter possède également une option pour effectuer le vidage automatique de la mémoire [automatic flushing], lequel se produit après chaque println() si le drapeau du constructeur est levé dans ce sens.
Certaines classes ont été laissées inchangées entre Java 1.0 et Java 1.1 :
Les classes de Java 1.0 qui n'ont pas de classes correspondantes en Java 1.1 |
---|
DataOutputStream |
File |
RandomAccessFile |
SequenceInputStream |
DataOutputStream, en particulier, est utilisé sans modification, donc pour stocker et retrouver des données dans un format transportable vous utiliserez les hiérarchies InputStream et OutputStream.
RandomAccessFile est employé pour les fichiers dont la taille de l'enregistrement est connue, de sorte que vous pouvez bouger d'un enregistrement à un autre en utilisant seek(), puis lire ou changer les enregistrements. Les enregistrements n'ont pas forcément la même taille ; vous devez seulement être capable de déterminer de quelle grandeur ils sont et où ils sont placés dans le fichier.
D'abord il est un peu difficile de croire que RandomAccessFile ne fait pas partie de la hiérarchie d'InputStream ou d'OutputStream. Cependant, il n'y a pas d'association avec ces hiérarchies autre que quand il arrive de mettre en œuvre les interfaces DataInput et DataOutput (qui sont également mises en œuvre par DataInputStream et DataOutputStream). Elle n'utilise même pas la fonctionnalité des classes existantes InputStream et OutpuStream — il s'agit d'une classe complétement différente, écrite en partant de zéro, avec toutes ses propres méthodes (pour la plupart native). Une raison à cela pouvant être que RandomAccessFile a des comportements essentiellement différents des autres types d'E/S, dès qu'il est possible de se déplacer en avant et en arrière dans un fichier. De toute façon, elle reste seule, comme un descendant direct d'Object.
Essentiellement, un RandomAccessFile fonctionne comme un DataInputStream collé ensemble avec un DataOutputStream, avec les méthodes getFilePointer() pour trouver où on se trouve dans le fichier, seek() pour se déplacer vers un nouvel emplacement dans le fichier, et length() pour déterminer la taille maximum du fichier. En complément, les constructeurs requièrent un deuxième argument (identique à fopen() en C) indiquant si vous effectuez de manière aléatoire une lecture (« r ») ou une lecture et écriture (« rw »). Il n'y a pas de ressource pour les fichiers en lecture seule, ce qui pourrait suggérer que RandomAccessFile aurait mieux fonctionné s'il se trouvait hérité de DataInputStream.
Les méthodes de recherche sont valables seulement dans RandomAccessFile, qui fonctionne seulement avec des fichiers. Le BufferedInputStream permet de marquer « mark() » une position (dont la valeur est tenue dans une seule variable interne) et d'annuler cette position « reset() », mais c'est limité et pas très pratique.
Bien que pouvez combiner les classes de flux d'E/S de différentes manières, vous utiliserez probablement quelques combinaisons. L'exemple suivant pourra être employé comme une référence de base ; il montre la création et l'utilisation de configurations d'E/S typiques. Notez que chaque configuration commence par un commentaire avec numéro et titre qui correspondent aux titres des paragraphes suivant et fournissant l'explication approprié.
Voici les descriptions pour les sections numérotées du programme :
La partie 1 à 4 démontre la création et l'utilisation des flux d'entrée. La partie 4 montre aussi l'emploi simple d'un flux de sortie.
Afin d'ouvrir un fichier pour l'entrée de caractères, on utilise un FileInputReader avec un objet String ou File comme nom de fichier. Pour la vitesse, on désirera que le fichier soit mis en mémoire tampon alors on passera la référence résultante au constructeur à un BufferedReader. Puisque BufferedReader fournit aussi la méthode readLine( ), qui est notre objet final et l'interface depuis laquelle on lit. Quand on cherche la fin du fichier, readLine( ) renverra null qui sera utilisé pour sortir de la boucle while.
Le String s2 est utilisé pour accumuler le contenu entier du fichier (incluant les nouvelles lignes qui doivent être ajoutées puisque readLine( ) les enlève). s2 est ensuite employé dans la dernière partie de se programme. Enfin, close( ) est appelé pour fermer le fichier. Techniquement, close( ) sera appelé au lancement de finalize(), et ceci est supposé se produire (que le garbage collector se mette en route ou pas) lors de la fermeture du programme. Cependant, ceci a été inconséquemment implémenté, c'est pourquoi la seule approche sûre est d'appeler explicitement close( ) pour les fichiers.
La section 1b montre comment envelopper System.in afin de lire l'entrée sur la console. System.in est un DataInputStream et BufferedReader nécessite un argument Reader, voilà pourquoi InputStreamReader est introduit pour effectuer la traduction.
Cette partie prend le String s2 qui contient maintenant le contenu entier du fichier et l'utilise pour créer un StringReader. Puis read( ) est utilisé pour lire chaque caractère un par un et les envoie vers la console. Notez que read( ) renvoie le byte suivant sous la forme d'un int et pour cette raison il doit être convertit en char afin de s'afficher correctement.
Pour lire une donnée « formatée », vous utiliserez un DataInputStream, qui est une classe d'E/S orientée-byte (plutôt qu'orientée-char). Ainsi vous devrez utiliser toutes les classes InputStream plutôt que les classes Reader. Bien sur, vous pouvez lire n'importe quoi (du genre d'un fichier) comme des bytes en utilisant les classes InputStream, mais ici c'est un String qui est utilisé. Pour convertir le String en un tableau de bytes, ce qui est approprié pour un ByteArrayInputStream, String possède une méthode getBytes()pour faire le travail. A ce stade, vous avez un InputStream adéquat pour porter un DataInputStream.
Si on lit les caractères depuis un DataInputStream un byte à chaque fois en utilisant readByte( ), n'importe quelle valeur de byte donne un résultat juste donc la valeur de retour ne peut pas être employée pour détecter la fin de l'entrée. À la place, on peut employer la méthode avalaible( ) pour découvrir combien de caractères sont encore disponibles. Voici un exemple qui montre comment lire un fichier byte par byte :
Notons qu'available() fonctionne différemment en fonction du type de ressource depuis laquelle on lit; c'est littéralement « le nombre de bytes qui peuvent être lus sans blocage. » Avec un fichier cela signifie le fichier entier, mais avec une autre sorte d'entrée cela ne pourra pas être possible, alors employez le judicieusement.
On peut aussi détecter la fin de l'entrée dans des cas comme cela en attrapant une exception. Cependant, l'emploi des exeptions pour le contrôle du flux est considéré comme un mauvais emploi de cette caractéristique.