Vous êtes sur la page 1sur 47

Plan de cette partie 1

ƒ Gestion des fichiers (Path, Files et


FileSystem du JDK 7)
ƒ Les flots (streams), modèle de conception
Entrées-sorties 1 « décorateur »
ƒ Classes URL et URI
Université de Nice - Sophia Antipolis ƒ Noms de fichiers, ressources
Version 3.7 – 17/7/13
ƒ Sérialisation
Richard Grin

R. Grin Java : entrées-sorties 2

Introduction
ƒ Nouvelle API NIO.2 introduite par le JDK 7,
contenue dans le paquetage java.nio.file
Gestion des fichiers ƒ Elle remplace en particulier la classe
java.io.File (qui est donc maintenant à
Path et Files éviter) qui est présentée à la fin de la 2ème
partie de ce support

R. Grin Java : entrées-sorties 3 R. Grin Java : entrées-sorties 4

Fonctionnalités
ƒ Manipulation des noms de fichier (Path ;
Files se charge du reste des fonctionnalités)
ƒ Opérations de base sur les fichiers considérés Manipulation des chemins des fichiers
comme un tout
Interface Path
ƒ Traverser une arborescence de fichiers
ƒ Surveiller les changements dans un répertoire
ƒ Lire et écrire le contenu des fichiers

R. Grin Java : entrées-sorties 5 R. Grin Java : entrées-sorties 6

1
Interface de base : Path Description d’un Path
ƒ Correspond à un nom/chemin de fichier relatif ƒ Un chemin est composé d’une suite de noms de
ou absolu dans un système de fichiers répertoire et d’un dernier élément
ƒ Indépendant de l’existence ou non d’un (getFileName()) qui est un nom de répertoire
fichier qui a ce nom ou de fichier
ƒ Hérite de Comparable<Path> (comparaison ƒ Une racine (getRoot()) de système de
lexicographique des noms, y compris le fichiers peut être présent au début (par exemple
séparateur de nom de fichier et la racine), « C:\ » sous Windows)
Iterable<Path> (itère sur les éléments du ƒ Le chemin peut représenter un lien symbolique
chemin) et Watchable (voir plus loin
« Surveiller un répertoire »
R. Grin Java : entrées-sorties 7 R. Grin Java : entrées-sorties 8

Instance de Path
Chaînage des méthodes
ƒ Dans la suite, « instance de Path » signifiera
« instance d’une classe qui implémente Path » ƒ Plusieurs méthodes de Path renvoient un
ƒ Une instance de Path ne peut être modifiée Path, ce qui permet de chaîner les appels de
(comme String ou Integer) et il est donc méthode
possible de la partager entre plusieurs threads ƒ Par exemple
(« thread-safe ») Paths.get("/home/dupond/../fich")
.normalize()

R. Grin Java : entrées-sorties 9 R. Grin Java : entrées-sorties 10

Classe Paths - création d’un Path Classe Paths - création d’un Path
ƒ La classe Paths contient 2 méthodes get ƒ Path.get est un raccourci pour
FileSystems.getDefault().getPath
static pour créer une instance de Path à
partir d’une ou plusieurs String ou d’un URI
(voir « URL et URI » plus loin sur ce support)
ƒ Path get(String, String...) :
– s’il n’y a qu’un paramètre, il décrit le
chemin
– sinon le chemin est constitué des différents
paramètres
R. Grin Java : entrées-sorties 11 R. Grin Java : entrées-sorties 12

2
Exemples
ƒ Path path = Paths.get("/rep/truc");
Joindre 2 chemins
ƒ Path path = Paths.get("/rep", "truc");
correspond au même chemin que l’exemple ƒ path.resolve(pathRelatif)
précédent retourne un nouveau Path en prenant pour
base path et en y ajoutant pathRelatif
ƒ Sous Windows, le paramètre de type String
peut être donné avec le séparateur « / » ou ƒ path.resolveSibling(pathRelatif)
« \\ » (il faut doubler le « \ »), mais les retourne un nouveau Path en prenant pour
transformations de chemin sont base le répertoire parent de path
données/affichées avec le séparateur du
système utilisé)
R. Grin Java : entrées-sorties 13 R. Grin Java : entrées-sorties 14

Itinéraire entre 2 chemins Comparaison de chemins


ƒ boolean equals(Object) (redéfinit le
ƒ Path relativize(Path) retourne le equals de Object) ; voir aussi
chemin relatif pour aller de this au Files.isSameFile
paramètre (inverse de resolve) ƒ boolean startsWith ; paramètre String
ƒ Exemple : si path correspond à /a/b et si le ou Path
paramètre correspond à /a/b/c/d, ƒ boolean endsWith ; paramètre String ou
relativize renvoie le chemin qui Path
correspond à c/d ƒ int compareTo(Path) (de l’interface
ƒ Le chemin renvoyé peut comporter des « .. » Comparable<Path>) ; comparaison
lexicographique du chemin
R. Grin Java : entrées-sorties 15 R. Grin Java : entrées-sorties 16

Informations sur un Path


Itérer sur un Path
ƒ Path getFileName() : nom terminal du fichier
ƒ Path getName(i) : ième élément du chemin ƒ Une boucle « for-each » permet d’itérer sur les
ƒ int getNameCount() : nombre d’éléments éléments d’un chemin
ƒ Path subpath(début, fin) : éléments ƒ Exemple :
Path path = Paths.get("C:", "a/b", "f.txt");
compris entre début (compris) et fin (pas for(Path nom : path) {
compris), sans la racine System.out.println(nom + "*");
ƒ Path getParent() : tout sauf le dernier élément }
affiche a*b*f.txt*
ƒ Path getRoot() : racine du chemin
ƒ boolean isAbsolute() : le chemin est absolu ?
R. Grin Java : entrées-sorties 17 R. Grin Java : entrées-sorties 18

3
Exemples
Normalisation d’un Path
/home/dupond/fich dupond/fich
toString() /home/dupond/fich dupond/fich
ƒ Normalisation : enlever les « . » et « .. » qui
getFileName() fich fich
getName(0) home dupond
ne servent à rien
getNameCount() 3 2 ƒ Exemple :
Paths.get("/home/dupond/../fich")
subpath(0,2) home/dupond dupond/fich
.normalize()
getParent() /home/dupond dupond
correspond à /home/fich
getRoot() / null
ƒ Pour Windows C:\home\dupond\fichier,
getRoot renvoie C:\
R. Grin Java : entrées-sorties 19 R. Grin Java : entrées-sorties 20

Conversion en chemin absolu Conversion d’un Path


ƒ Utilise le répertoire courant ƒ En URI (voir section « URL et URI ») :
ƒ Path toAbsolutePath() URI toUri()
Le fichier peut exister ou ne pas exister Type de résultat :
ƒ Path toRealPath(LinkOption.. options) file:///C:/home/dupond/fich
Lance une java.io.IOException si le fichier Utilise le répertoire courant si le chemin est
n’existe pas ; donne le même résultat que relatif
toAbsolutePath() sinon ; Ne tient pas compte de l’existence ou non
les options en paramètre indiquent s’il faut du fichier correspondant au chemin
suivre (valeur par défaut) ou non
ƒ En File (pour interopérabilité avec ancien
(LinkOption.NOFOLLOW_LINKS) les liens
code) : File toFile()
symboliques (dépendant
R. Grin
du système)
Java : entrées-sorties 21 R. Grin Java : entrées-sorties 22

Rappel : quelques propriétés système


En résumé, diverses conversions ƒ user.dir : répertoire courant
ƒ file.separator : caractère pour séparer
ƒ Path Æ URI : Path.toURI() les différentes parties d’un nom de fichier (/
ƒ Path Æ URL : Path.toURI().toURL() pour Unix, \ pour Windows) ; aussi fourni
ƒ Path Æ File : Path.toFile() par FileSystems.getDefault()
.getSeparator()
ƒ URI Æ Path : Paths.get(URI)
ƒ user.home : répertoire « home » de
(URI.getPath() retourne une String !)
l’utilisateur
ƒ URL Æ Path : Paths.get(URL.toURI())
ƒ Exemple :
ƒ File Æ Path : File.toPath() System.getProperty("user.dir")
R. Grin Java : entrées-sorties 23 R. Grin Java : entrées-sorties 24

4
Parenthèse sur les IDE
ƒ Le répertoire courant quand on lance une
application sous Eclipse est le répertoire qui
contient le projet (le répertoire père de src)
ƒ Le classpath est le répertoire src (en fait le Classe Files
répertoire bin pendant l’exécution)
ƒ Situation similaire pour NetBeans
ƒ Attention, vérifiez que votre application
fonctionne toujours après l’avoir mise dans un
jar, lorsque vous la lancez en dehors d’un IDE
R. Grin Java : entrées-sorties 25 R. Grin Java : entrées-sorties 26

Classe Files Les options


ƒ La classe Files (avec un s final) fournit des
méthodes static pour travailler avec les ƒ Plusieurs méthodes de Files ont un
fichiers (y compris les répertoires et les liens) paramètre qui représente une option
ƒ La plupart des méthodes de Files peuvent ƒ Le paramètre peut servir, par exemple, à
lancer une IOException, ou plus dire ce qu’il faut faire si on ouvre en
spécifiquement une écriture un fichier qui existe déjà ; faut-il
java.nio.file.FileSystemException ajouter ce qui est écrit à la fin du fichier
ƒ Au contraire des méthodes de Path, ces (APPEND) ou faut-il effacer l’ancien contenu
méthodes tiennent compte de l’existence des du fichier et écrire dans un fichier vide
fichiers (TRUNCATE_EXISTING) ?
R. Grin Java : entrées-sorties 27 R. Grin Java : entrées-sorties 28

Les types des options Utilisation de OpenOption


ƒ Ce sont des types du paquetage
java.nio.file qui sont des interfaces
ƒ Utilisé pour indiquer des options à l’ouverture
« marqueurs » (ils ne contiennent ni méthodes d’un fichier, en particulier par les méthodes
newBufferedWriter, newByteChannel,
ni constantes) : OpenOption et CopyOption
newInputStream, newOutputStream, write
ƒ Les options sont en fait des valeurs
d’énumérations qui implantent ces interfaces :
– OpenOption est implémenté par
StandardOpenOption et LinkOption
– CopyOption est implanté par
StandardOpenOption et LinkOption
R. Grin Java : entrées-sorties 29 R. Grin Java : entrées-sorties 30

5
Valeurs de StandardOpenOption StandardCopyOption
ƒ READ (ouvre en lecture), WRITE (ouvre en
ƒ Définit les valeurs ATOMIC_MOVE (le
écriture), APPEND, TRUNCATE_EXISTING,
déplacement du fichier sera une opération
CREATE (crée un nouveau fichier s’il n’existe
« atomique » : pas de risque que la moitié du
pas déjà), CREATE_NEW (crée un nouveau
déplacement seulement soit effectuée),
fichier ; erreur si le fichier existe déjà), COPY_ATTRIBUTES (les attributs du fichier
DELETE_ON_CLOSE (pour fichier temporaire),
sont copiés, par exemple le propriétaire ou les
SPARSE, SYNC (garde le contenu du fichier et
attributs « rwx ») et REPLACE_EXISTING
les métadonnées synchronisés avec le fichier
(écrase un fichier s’il existe déjà ; sinon la
enregistré sur le support), DSYNC (garde
copie n’a pas lieu)
seulement le contenu du fichier synchronisé)
R. Grin Java : entrées-sorties 31 R. Grin Java : entrées-sorties 32

LinkOption Fonctionnalités (1/2)


ƒ Définit la seule valeur NOFOLLOW_LINKS ƒ Opérations sur les fichiers considérés comme
un tout :
– Copier, déplacer, supprimer
– Lister les fichiers d’un répertoire, créer et
supprimer un répertoire
– Afficher et modifier les métadonnées sur les
fichiers (autorisations, propriétaire,…)

R. Grin Java : entrées-sorties 33 R. Grin Java : entrées-sorties 34

Fonctionnalités (2/2)
ƒ Traverser une arborescence de fichiers en
lançant des actions sur les répertoires ou les ƒ Les sections suivantes étudient ces
fichiers ordinaires rencontrés fonctionnalités
ƒ Surveiller les changements dans un répertoire
ƒ Lire et écrire le contenu de petits fichiers
ƒ Obtenir des flots

R. Grin Java : entrées-sorties 35 R. Grin Java : entrées-sorties 36

6
Vérifications sur les fichiers
ƒ Méthodes dont le type retour est boolean
ƒ Vérifier l’existence (liens symboliques suivis
ou non) : exists(Path, LinkOption) et
Opérations sur les fichiers notExists(Path, LinkOption)
Classe Files ƒ Vérifier les autorisations (voir aussi la suite
sur les métadonnées) : isReadable(Path),
isWritable(Path), isExecutable(Path)
ƒ Vérifier si 2 chemins représentent le même
fichier : isSameFile(Path, Path)
R. Grin Java : entrées-sorties 37 R. Grin Java : entrées-sorties 38

Copier un fichier ou un répertoire


Supprimer un fichier ou un répertoire
ƒ Path copy(Path, Path, CopyOption...)
ƒ void delete(Path) : lance retourne le chemin de la cible (pour chaînage)
NoSuchFileException si le fichier ƒ Les options possibles :
n’existe pas – REPLACE_EXISTING
ƒ boolean deleteIfExist(Path) : idem – COPY_ATTRIBUTES
mais ne lance pas une exception si le fichier – NOFOLLOW_LINKS
n’existe pas (renvoie true si le fichier a été ƒ On peut aussi faire des copies entre un flot et
supprimé) un fichier (dans les 2 sens : avec
InputStream et OutputStream)
R. Grin Java : entrées-sorties 39 R. Grin Java : entrées-sorties 40

Exemples
Déplacer un fichier ou un répertoire
ƒ Renommer un fichier (il reste dans le même
ƒ Path move(Path src, Path dest, répertoire) :
CopyOption...) throws IOException Files.move(
source,
ƒ Les options possibles : source.resolveSibling(nouveauNom));
– REPLACE_EXISTING : écrase un éventuel ƒ Déplacer un fichier (en lui laissant le même
fichier existant nom) ; écrase un éventuel fichier avec le
– ATOMIC_MOVE : l’opération est même nom :
Files.move(source,
complètement exécutée, ou pas du tout autreRep.resolve(source.getFileName()),
StandardCopyOption.REPLACE_EXISTING);
R. Grin Java : entrées-sorties 41 R. Grin Java : entrées-sorties 42

7
Créer un fichier ou un répertoire Exemple
ƒ Path createFile(Path, Set<PosixFilePermission> perms =
PosixFilePermissions.fromString("rwxr-x---");
FileAttribute<?>...) FileAttribute<Set<PosixFilePermission>> attr =
ƒ Path createDirectory(Path, PosixFilePermissions.asFileAttribute(perms);
FileAttribute<?>...) Files.createDirectory(file, attr);

ƒ Path createDirectories(Path,
FileAttribute<?>...) : crée avant tous
les répertoires intermédiaires s’ils n’existent
pas (idem mkdir –p d’Unix)
R. Grin Java : entrées-sorties 43 R. Grin Java : entrées-sorties 44

Créer un fichier temporaire


Créer un lien
ƒ Path createTempFile(Path rep,
String préfixe, String suffixe,
ƒ Path createLink(Path lien, Path
FileAttribute<?>...) : crée un fichier
existant) : crée un lien « hard » (le 1er
temporaire ; utilise « .tmp » si le suffixe est
paramètre) du 2ème paramètre
null (voir javadoc pour détails)
ƒ Path createSymbolicLink(Path lien,
Path fichierPointé, ƒ Path createTempFile(String suffixe,
FileAttribute<?>...) FileAttribute<?>...) : crée le fichier
temporaire dans le répertoire par défaut des
fichiers temporaires (/tmp ou /var/tmp sous
Unix, C:\WINNT\TEMP sous Windows)
R. Grin Java : entrées-sorties 45 R. Grin Java : entrées-sorties 46

Gérer les métadonnées sur les fichiers


Exemple
ƒ La classe Files a aussi des méthodes pour
Path tmp = Files.createTempFile(null, null); obtenir ou modifier les métadonnées sur les
try (OutputStream os = Files.newOutputStream( fichiers et les répertoires
tmp, StandardOpenOption.DELETE_ON_CLOSE) {
// Utilise le fichier temporaire avec os
...
}
catch (IOException ex) {
...
}

R. Grin Java : entrées-sorties 47 R. Grin Java : entrées-sorties 48

8
Lire les métadonnées sur les fichiers Exemple
ƒ Les méthodes pour obtenir les métadonnées Path fichier = ...;
sont nombreuses et ne seront pas détaillées BasicFileAttributes attr =
ici : size, isDirectory, isRegularFile, Files.readAttributes(
isSymbolicLink, isHidden, getOwner, fichier,
getLastModifiedTime, BasicFileAttributes.class);
getPosixFilepermissions, System.out.println("creationTime: " +
getAttribute,… attr.creationTime());
System.out.println("lastAccessTime: " +
ƒ Il est possible de récupérer des groupes
attr.lastAccessTime());
d’attributs en une seule fois avec les
méthodes readAttributes
R. Grin Java : entrées-sorties 49 R. Grin Java : entrées-sorties 50

Exemple 1
Modifier des métadonnées
Path sourceFile = ...;
Path newFile = ...;
ƒ Certaines métadonnées peuvent être PosixFileAttributes attrs =
modifiées avec les méthodes suivantes : Files.readAttributes(
setLastModifiedTime, setOwner, sourceFile,
setAttribute PosixFileAttributes.class);
FileAttribute<Set<PosixFilePermission>> attr =
ƒ Consultez le tutoriel en ligne d’Oracle pour PosixFilePermissions
plus de détails : .asFileAttribute(attrs.permissions());
http://docs.oracle.com/javase/tutorial/essent Files.createFile(file, attr);

ial/io/fileAttr.html
R. Grin Java : entrées-sorties 51 R. Grin Java : entrées-sorties 52

Exemple 2 Glob
Path file = ...;
ƒ Modèle qui ressemble à une expression
Set<PosixFilePermission> perms = régulière, mais pour filtrer les noms de
PosixFilePermissions fichiers (attention, la signification des
.fromString("rw-------"); caractères spéciaux est différente de celle des
FileAttribute<Set<PosixFilePermission>> attr =
expressions régulières)
PosixFilePermissions
.asFileAttribute(perms); ƒ Correspond aux expressions qu’on peut
Files.setPosixFilePermissions(file, perms); rencontrer dans les commandes Unix comme
« ls l* »

R. Grin Java : entrées-sorties 53 R. Grin Java : entrées-sorties 54

9
Exemples de Glob Exemples de Glob
ƒ * : de 0 à n caractères ƒ {abc, ABC, xyz} ou {temp*, tmp*} :
collection de sous-modèles
ƒ ** : idem * mais traverse les répertoires
ƒ \ : pour enlever la signification spéciale du
ƒ ? : un seul caractère
caractère suivant (\[ ou \\ par exemple) ; à
ƒ [abx] : un des caractères entre crochets l’intérieur des crochets ([) les caractères *, ?
ƒ [a-g] : un des caractères compris entre les et \ n’ont pas de signification particulière
extrémités ƒ Détails de la syntaxe dans la javadoc de la
ƒ [a-g,A-G] : on peut donner plusieurs méthode getPathMatcher de la classe
segments java.nio.file.FileSystem

R. Grin Java : entrées-sorties 55 R. Grin Java : entrées-sorties 56

Interface PathMatcher Liste des fichiers d’un répertoire


ƒ PathMatcher compare une String à un glob
ou à une expression régulière (voir partie 2 de ƒ Pour obtenir des performances correctes,
ce support de cours) : même pour les répertoires qui contiennent de
FileSystems.getDefault() nombreux fichiers, les fichiers d’un répertoire
.getPathMatcher("glob:" + modele); sont fournis sous la forme d’un flot avec
(on peut remplacer glob par regex) l’interface : DirectoryStream<Path> (ne
ƒ Cette interface contient la méthode pas oublier de fermer le flot après usage)
boolean matches(Path chemin) ƒ Cette interface hérite de Iterable<Path>, ce
qui renvoie true si le chemin correspond au qui simplifie son utilisation (utilisation d’une
modèle boucle for-each)
R. Grin Java : entrées-sorties 57 R. Grin Java : entrées-sorties 58

Méthodes pour lister un répertoire


ƒ 3 méthodes de Files fournissent un tel flot, Méthodes pour lister un répertoire
suivant que l’on veut avoir tous les fichiers ou
seulement des fichiers sélectionnés ; ces ƒ newDirectoryStream(Path rep) : avoir tous les
méthodes peuvent lancer une IOException fichiers du répertoire
ƒ L’interface interne DirectoryStream.Filter ƒ newDirectoryStream(Path rep, String glob) :
permet de sélectionner les fichiers sur un critère tous les fichiers dont le nom correspond au glob
ƒ newDirectoryStream(Path rep,
quelconque ; il suffit d’implémenter la méthode
DirectoryStream.Filter<? super Path> filtre)
boolean accept(Path fichier) pour
: tous les fichiers sélectionnés par le filtre
qu’elle renvoie true pour les fichiers qu’on
veut sélectionner
R. Grin Java : entrées-sorties 59 R. Grin Java : entrées-sorties 60

10
Exemple d’utilisation du flot Exemple avec glob
Path rep = ...;
try (DirectoryStream<Path> flot = Path rep = ...;
Files.newDirectoryStream(rep)) { try (DirectoryStream<Path> flot =
for (Path fich: flot ) { Files.newDirectoryStream(
System.out.println(fich.getFileName()); rep, "*.{class,jar}")) {
} for (Path fich : flot) {
} catch (IOException | System.out.println(fich.getFileName());
DirectoryIteratorException x) { }
// IOException ne peut être lancée que } catch (IOException x) {
// par newDirectoryStream ...
... }
}
R. Grin Java : entrées-sorties 61 R. Grin Java : entrées-sorties 62

Exemple avec filtre – le filtre Exemple avec filtre (suite)


DirectoryStream.Filter<Path> filtre = Path rep = ...;
newDirectoryStream.Filter<Path>() { try (DirectoryStream<Path> flot =
public boolean accept(Path fich) Files.newDirectoryStream(
throws IOException { rep, filtre)) {
try { for (Path fich : flot) {
return (Files.isDirectory(fich)); System.out.println(fich.getFileName());
} catch (IOException x) { }
... } catch (IOException x) {
} ...
} }
};
R. Grin Java : entrées-sorties 63 R. Grin Java : entrées-sorties 64

Méthodes diverses de Files


ƒ String probeContentType(Path) tente Obtenir tous les répertoires racines
de déterminer le type MIME d’un fichier
Iterable<Path> dirs =
ƒ FileStore getFileStore(Path) FileSystems.getDefault()
retourne le système de fichiers qui contient .getRootDirectories();
le fichier passé en paramètre for (Path name: dirs) {
ƒ La classe FileStore peut fournir des System.out.println(name);
}
informations sur le système de fichiers
(valeurs en octets) : getTotalSpace(),
getUsableSpace() (évaluation non sûre)
et getUnallocatedSpace()
R. Grin Java : entrées-sorties 65 R. Grin Java : entrées-sorties 66

11
Visiter une arborescence de fichiers
ƒ La classe Files contient 2 méthodes
walkFileTree pour parcourir une
arborescence de fichiers, en lançant des
Parcourir une arborescence de fichiers actions sur les répertoires ou les fichiers
Classe Files ordinaires rencontrés
ƒ Une instance de FileVisitor<T> définit
les actions exécutées pendant la visite (T
correspond au type qui permet de désigner
les fichiers ou répertoires rencontrés ; Path
le plus souvent)
R. Grin Java : entrées-sorties 67 R. Grin Java : entrées-sorties 68

Démarrer la visite Interface FileVisitor<T>


ƒ Path walkFileTree(Path, FileVisitor<? ƒ preVisitDirectory(T rep,
super Path>) : visite toute l’arborescence BasicFileAttributes attrs) : appelée juste avant
placée sous le 1er paramètre ; ne suit pas les la visite des fichiers d’un répertoire
ƒ visitFile(T fichier, BasicFileAttributes
liens symboliques ; retourne le 1er paramètre
attrs) : appelée pour tous les fichiers rencontrés
ƒ Path walkFileTree(Path,
Set<FileVisitOption>, int, ƒ visitFileFailed (T rep, IOException ex) :
FileVisitor<? super Path>) : on peut appelée lorsqu’un fichier (ordinaire ou répertoire) ne
indiquer par une option si on suite les liens peut être visité à cause de l’exception ex
symboliques et indiquer aussi la profondeur ƒ postVisitDirectory (T rep, IOException ex) :
appelée juste après la visite des fichiers d’un
des sous-répertoires à visiter (0 = on ne visite
répertoire ; ex est l’exception qui a interrompu la
que le fichier indiqué par le 1er paramètre) visite de ce répertoire (null si pas de problème)
R. Grin Java : entrées-sorties 69 R. Grin Java : entrées-sorties 70

Énumération FileVisitResult
ƒ Toutes les méthodes de l’interface FileVisitor SimpleFileVisitor<T>
renvoient une valeur de cette énumération pour
indiquer comment la visite doit se poursuivre ƒ Classe qui implémente FileVisitor<T> ; il
suffit au développeur de redéfinir une ou
ƒ Les valeurs :
plusieurs des méthodes
– CONTINUE : continuer normalement
ƒ preVisitDirectory : retourne CONTINUE
– SKIP_SIBLINGS : sauter les fichiers ordinaires situés
dans le même répertoire ƒ visitFile : retourne CONTINUE
– SKIP_SUBTREE : sauter toutes les entrées de ce ƒ visitFileFailed : relance ex
répertoire (y compris les répertoires) ƒ postVisitDirectory : retourne CONTINUE
– TERMINATE : fin de la visite… ou relance ex si elle n’est pas null
R. Grin Java : entrées-sorties 71 R. Grin Java : entrées-sorties 72

12
Exemple (1/2) Exemple (2/2)
Path repertoire = Paths.get(…); @Override
FileVisitor<Path> visiteur = public FileVisitResult visitFile(
new Finder("*.class"); Path path,
Files.walkFileTree(repertoire, visiteur); BasicFileAttributes attributs)
throws IOException {
class Finder extends SimpleFileVisitor<Path>{ if (matcher.matches(path.getFileName())){
private PathMatcher matcher; System.out.println(path);
public Finder(String modele) { }
matcher = FileSystems.getDefault() return FileVisitResult.CONTINUE;
.getPathMatcher("glob:" + modele); }
} }
R. Grin Java : entrées-sorties 73 R. Grin Java : entrées-sorties 74

Surveiller un répertoire
ƒ Il peut être intéressant d’être prévenu si un
répertoire est modifié (fichier ajouté,
modifié ou supprimé)
Surveiller un répertoire ƒ Par exemple, une application peut utiliser
Classe Files des plugins sous la forme de fichiers jar qui
sont déposés dans un répertoire ; quand un
nouveau plugin est déposé, il doit être pris
en compte par l’application
ƒ L’interface WatchService sert à surveiller
des objets (un répertoire pour cet exemple)
R. Grin Java : entrées-sorties 75 R. Grin Java : entrées-sorties 76

Surveiller un répertoire Indiquer le répertoire à surveiller


1. Créer un surveillant : ƒ WatchKey register(
WatchService watcher = WatchService watcher,
FileSystems.getDefault() WatchEvent.Kind<?>... events)
.newWatchService(); throws IOException
2. Enregistrer les objets à surveiller ; ils ƒ Le 2ème paramètre indique quel type
doivent implémenter l’interface d’événement le watcher va surveiller ; pour
Watchable, ce qui est le cas de Path qui cela la classe StandardWatchEventKinds
contient 2 méthodes register pour définit 4 constantes de type
s’enregistrer (nous étudierons la plus WatchEvent.Kind<Path> : ENTRY_CREATE,
simple qui est suffisante) ENTRY_DELETE, ENTRY_MODIFY, OVERFLOW

R. Grin Java : entrées-sorties 77 R. Grin Java : entrées-sorties 78

13
Être prévenu d’une modification
Être prévenu d’une modification
ƒ Il faut interroger le surveillant à intervalles
ƒ La clé renvoyée par la méthode register réguliers avec une des méthodes suivantes :
sert à identifier l’enregistrement – WatchKey poll() : récupère une clé qui
ƒ Au départ elle est dans l’état « ready » ; si représente une modification ; retourne
l’événement enregistré survient, elle passe à immédiatement la valeur null si aucune
l’état « signaled » et elle est mise en file modification ; on peut passer 2 paramètres
d’attente pour être retrouvée par une des (valeur et unité) pour indiquer un timeout
méthode poll ou take de WatchService – WatchKey take() : attend une modification

R. Grin Java : entrées-sorties 79 R. Grin Java : entrées-sorties 80

Exemple schématique
Analyser un événement
for (;;) {
WatchKey key = watcher.take(); ƒ key.pollEvents() retourne une
for (WatchEvent<?> evenement: List<WatchEvent<?>>
key.pollEvents()) {
... // traiter l’événement
ƒ La classe WatchEvent contient les méthodes
} – kind() : renvoie le type d’événement
// réinitialise la clé – count() : événement répété si > 1
boolean valid = key.reset();
– context() : dans le cas d’un
if (!valid) {
// l’objet n’est plus enregistré
WatchEvent<Path> c’est un Path qui
} désigne le fichier qui a provoqué
} l’événement
R. Grin Java : entrées-sorties 81 R. Grin Java : entrées-sorties 82

Exemple
// Génère un avertissement à la compilation
WatchEvent<Path> ev =
(WatchEvent<Path>)evenement;
Path nom = ev.context(); Lire et écrire le contenu d’un fichier
Path fichier = dir.resolve(name); Classe Files
// Traite la modification sur le fichier
...

R. Grin Java : entrées-sorties 83 R. Grin Java : entrées-sorties 84

14
Lire et écrire le contenu d’un fichier 2 types de fichiers
ƒ L’API pour lire et écrire le contenu des
ƒ Plusieurs méthodes de Files facilitent (par
fichiers distingue nettement 2 types de
rapport au JDK 6) la lecture et l’écriture de
fichier :
fichiers pour les cas les plus courants
– les fichiers contenant des octets « bruts »
– les fichiers contenant du texte : des octets
évidemment, mais qui représentent des
caractères, codés avec un certain codage
(charset ; ASCII, UTF-8, ISO-8859-1,...)
R. Grin Java : entrées-sorties 85 R. Grin Java : entrées-sorties 86

Lire et écrire des petits fichiers Lire et écrire des petits fichiers –
ƒ Des méthodes de Files permettent de lire ou précisions
d’écrire le contenu d’un fichier en une fois, en ƒ Pour la lecture, une exception est lancée si le
prenant en charge l’ouverture et la fermeture fichier n’existe pas
des fichiers ƒ Pour l’écriture, le fichier est créé s’il n’existe
ƒ Elles sont très pratiques pour lire ou écrire les pas
petits fichiers en une fois mais ne peuvent ƒ Le fichier est fermé à la fin de la méthode
être utilisées pour les très gros fichiers, (qu’il y ait eu une exception ou pas)
puisque le contenu du fichier doit être
enregistré dans la mémoire centrale
R. Grin Java : entrées-sorties 87 R. Grin Java : entrées-sorties 88

Lire des petits fichiers Écrire des petits fichiers


ƒ byte[] readAllBytes(Path) throws ƒ Path write(Path, byte[],
IOException : lit tous les octets d’un fichier OpenOption…) : écrit tous les octets du
ƒ List<String> readAllLines(Path, tableau dans un fichier
Charset) throws IOException : lit toutes ƒ Path write(Path fichier,
les lignes d’un fichier texte Iterable<? extends CharSequence>
Le Charset indique le codage des caractères ; lignes, CharSet, OpenOption…) : écrit
Charset.defaultCharset() renvoie le toutes les lignes de texte dans un fichier
codage par défaut ; StandardCharsets
contient des constantes pour les charsets ; voir
annexe du support de cours « Java de base »
R. Grin Java : entrées-sorties 89 R. Grin Java : entrées-sorties 90

15
Options pour écrire Interface CharSequence
ƒ L’interface java.lang.CharSequence,
ƒ Les options par défaut sont CREATE (crée le ajoutée depuis le JDK 1.4, est implémentée
fichier s’il n’existe pas déjà), par les classes String, StringBuilder et
TRUNCATE_EXISTING (un fichier existant StringBuffer (et java.nio.CharBuffer)
sera écrasé) et WRITE
ƒ Elle représente une suite de caractères
ƒ Pour ajouter à la fin du fichier, s’il existe lisibles (char) dont on peut extraire une
déjà : sous-suite
Files.write(path, bytes,
StandardOpenOption.APPEND); ƒ Elle contient les méthodes charAt, length
et subSequence (pour extraire une sous-
suite)
R. Grin Java : entrées-sorties 91 R. Grin Java : entrées-sorties 92

Exemple d’écriture Pour les plus gros fichiers


dans un fichier texte ƒ On peut se trouver dans un cas particulier
List<String> liste = new ArrayList<>(); où il peut ne pas être possible ou intéressant
// Remplit la liste d’avoir tout le contenu d’un fichier en
... mémoire centrale
Files.write(
Paths.get(cheminFichier),
liste,
Charset.defaultCharset());

R. Grin Java : entrées-sorties 93 R. Grin Java : entrées-sorties 94

Pour les plus gros fichiers Fichiers texte


ƒ En ce cas, d’autres classes permettent ƒ newBufferedReader(Path, Charset)
d’écrire ou de lire les fichiers d’une façon renvoie un BufferedReader qui permet de
plus souple (mais plus complexe) lire un fichier ligne à ligne
ƒ newBufferedWriter(Path, Charset,
ƒ Les transparents suivants montrent
OpenOption…)
comment obtenir des objets de ces classes,
renvoie un BufferedWriter qui permet
avec des méthodes de la classe Files
d’écrire dans un fichier
ƒ Les détails sur l’utilisation de ces classes
seront fournis dans la section suivante
(« Les flots ») de ce support de cours
R. Grin Java : entrées-sorties 95 R. Grin Java : entrées-sorties 96

16
Fichiers d’octets Fichiers d’octets - options
ƒ newInputStream(Path, OpenOption…) ƒ newInputStream(Path, OpenOption…)
renvoie un InputStream pour lire dans un le plus souvent pas d’option, ce qui revient
fichier d’octets à l’option READ
ƒ newOutputStream(Path, OpenOption…) ƒ newOutputStream(Path, OpenOption…)
renvoie un OutputStream pour écrire dans par défaut les options sont CREATE,
un fichier d’octets TRUNCATE_EXISTING et WRITE
ƒ Ces classes n’utilisent pas de buffer et elles
sont le plus souvent décorées avec un
BufferedInputStream ou un
BufferedOutputStream
R. Grin Java : entrées-sorties 97 R. Grin Java : entrées-sorties 98

Fichiers à accès direct


Exemples
ƒ Avec l’utilisation courante des bases de
ƒ BufferedInputStream bis = données relationnelles ils sont moins utilisés
new BufferedInputStream( qu’avant mais peuvent encore être utiles
Files.newInputStream(path));
ƒ try (OutputStream os =
ƒ Ils permettent un accès direct (non
Files.newOutputStream(path, séquentiel), en lecture, écriture ou
StandardOpenOption.APPEND)) { lecture/écriture à une partie d’un fichier
os.write(123);
} catch (IOException e) {
...
}

R. Grin Java : entrées-sorties 99 R. Grin Java : entrées-sorties 100

Fichiers à accès direct Interface SeekableByteChannel


ƒ 2 méthodes surchargées newByteChannel
ƒ long position() : retourne la position
de la classe Files renvoient un
dans le canal (un fichier pour ce cas)
SeekableByteChannel qui permet un
ƒ SeekableByteChannel position(long) :
accès direct à un fichier
change la position dans le fichier
ƒ La classe FileChannel implémente cette
ƒ int read(ByteBuffer) : lit des octets du
interface ; pour le système de fichiers par
défaut, il est possible de caster en fichier pour les mettre dans le buffer
FileChannel ce que renvoie ƒ int write(ByteBuffer) : écrit dans le
newByteChannel fichier des octets du buffer

R. Grin Java : entrées-sorties 101 R. Grin Java : entrées-sorties 102

17
Interface SeekableByteChannel FileChannel
ƒ long size() : retourne la taille du fichier ƒ Permet de lire et d’écrire n’importe où dans
ƒ SeekableByteChannel truncate(long) : un fichier
tronque le fichier à la taille passée en ƒ Une région du fichier peut être bloquée pour
paramètre empêcher l’accès aux autres programmes
ƒ Voir javadoc pour plus de détails
ƒ Le transparent suivant donne un exemple de
traitement d’un fichier qui contient la ligne
suivante : « Bonjour monsieur »

R. Grin Java : entrées-sorties 103 R. Grin Java : entrées-sorties 104

Exemple (début) Exemple (fin)


Path fichier = Paths.get("…"); // Lit les 5 premiers octets du fichier
ByteBuffer bb1 = fc.position(0);
ByteBuffer.wrap("W".getBytes()); fc.read(bb2);
ByteBuffer bb2 = ByteBuffer.allocate(5); // Prépare bb2 pour l’écriture
try (FileChannel fc = bb2.flip();
(FileChannel)Files.newByteChannel( // Ajoute les 5 octets à la fin du fichier
fichier, StandardOpenOption.READ,
StandardOpenOption.WRITE)) { fc.position(fc.size() - 1);
fc.position(8); fc.write(bb2);
// La ligne : Bonjour WonsieurBonjo
fc.write(bb1);
}
// La ligne: Bonjour Wonsieur

R. Grin Java : entrées-sorties 105 R. Grin Java : entrées-sorties 106

Classe FileSystem
ƒ Fournit une interface pour travailler avec un
système de fichiers ; hérite de Closeable
Classe FileSystem ƒ Le système de fichiers qui est accessible par
la JVM (celui du système d’exploitation)
peut être récupéré par
FileSystems.getDefault()
ƒ Cette classe est une fabrique pour plusieurs
types d’objets (getPathMatcher,
newWatchService,…)
R. Grin Java : entrées-sorties 107 R. Grin Java : entrées-sorties 108

18
Exemple
FileSystem et fichier zip try (FileSystem zip =
FileSystems.newFileSystem(
ƒ Il est possible de créer un nouveau système de Paths.get("src.zip"), null)) {
fichiers à partir d’un fichier zip (ou jar) : Path source =
FileSystems zip.getPath("src/fichier.txt");
.newFileSystem(Path.get("rep/fich.zip")); Path copie =
ƒ On peut ensuite faire des opérations de gestion zip.getPath("src/nouveaufichier.txt");
de fichiers ou de lecture/écriture comme dans un Files.copy(source, copie);
système de fichiers « ordinaire » } catch (IOException e) {
...
}
R. Grin Java : entrées-sorties 109 R. Grin Java : entrées-sorties 110

Introduction
ƒ Le JDK 7 a introduit la classe Files qui
facilitent l’écriture et la lecture dans les fichiers
pour les cas les plus simples
Les flots ƒ Il est conseillé d’utiliser la classe Files si c’est
possible, mais dans le cas qui ne sont pas pris en
compte par cette classe, par exemple si on veut
travailler avec des flots qui ne sont pas liés à des
fichiers ou si on veut plus de souplesse durant la
lecture ou l’écriture des flots, il faut utiliser l’API
de base sur les flots
R. Grin Java : entrées-sorties 111 R. Grin Java : entrées-sorties 112

Plan de la section sur les flots Flots (streams) - définition


ƒ Généralités sur les flots ƒ Les flots de données permettent d’échanger de
ƒ Survol du paquetage java.io données entre un programme et l’extérieur
ƒ Flots d’octets – lecture, décoration, écriture ƒ Le plus souvent un flot permet de transporter
séquentiellement des données : les données sont
ƒ Exceptions
transportées une par une (ou groupe par groupe),
ƒ Flots de caractères – lecture, écriture de la première à la dernière donnée
ƒ Lire et écrire des caractères dans un fichier,
codage des caractères

R. Grin Java : entrées-sorties 113 R. Grin Java : entrées-sorties 114

19
Flot - utilisation Sources ou destinations de flots
ƒ Fichier
ƒ Le cycle d’utilisation de lecture ou écriture
ƒ Socket pour échanger des données sur un réseau
séquentielle d’un flot de données est le
suivant : ƒ URL (adresse Web)
ƒ Données de grandes tailles dans une base de
1) Ouvrir le flot
données (images, par exemple)
2) Tant qu’il y a des données à lire (ou à ƒ Pipe entre 2 files d’exécution (threads)
écrire), lire (ou écrire) la donnée suivante
ƒ Tableau d’octets
dans le flot
ƒ Chaîne de caractères
3) Fermer le flot ƒ etc...
R. Grin Java : entrées-sorties 115 R. Grin Java : entrées-sorties 116

Paquetage java.io
ƒ Il contient la plupart des classes liées aux flots
ƒ Il prend en compte un grand nombre de flots :
Survol du paquetage java.io – 2 types de flots (octets et caractères)
– différentes sources et destinations
– « décorations » diverses
ƒ Le grand nombre de classes de ce paquetage
peut effrayer le débutant

R. Grin Java : entrées-sorties 117 R. Grin Java : entrées-sorties 118

2 types de flots Types de classes


ƒ Les flots d’octets servent à lire ou écrire des ƒ Dans les 2 hiérarchies pour les flots d’octets et
octets « bruts » qui représentent des données de caractères, on trouve :
manipulées par un programme – des classes de base, qui sont associées à une
ƒ Les flots de caractères servent à lire ou écrire source ou une destination « concrète »
des données qui représentent des caractères Exemple : FileReader pour lire un flot de
lisibles par un homme, codés avec un certain caractères depuis un fichier
codage (ISO 8859-1, UTF 8,…) – des classes qui « décorent » une autre classe
Exemple : BufferedReader qui ajoute un
buffer pour lire un flot de caractères
R. Grin Java : entrées-sorties 119 R. Grin Java : entrées-sorties 120

20
Décorations des flots Exemple de décoration
ƒ Les fonctionnalités de base d’un flot sont la FileReader fr =
lecture ou l’écriture (méthodes read ou new FileReader(fichier);
// br « décore » fr avec un buffer
write)
BufferedReader br =
ƒ Selon les besoins, on peut lui ajouter new BufferedReader(r);
d’autres fonctionnalités/décorations : int c; // code Unicode du caractère lu
try {
– Utilisation d’un buffer pour réduire les lectures
while ((c = br.read()) != -1)
ou écritures « réelles »
. . .
– Codage ou décodage des données manipulées } Grâce au buffer la plupart
– Compression ou décompression de ces données des br.read() n’entraîneront
pas une lecture réelle sur le disque
– etc...
R. Grin Java : entrées-sorties 121 R. Grin Java : entrées-sorties 122

Classes de base du paquetage


java.io Sources et destinations concrètes
ƒ Fichiers :
Lecture et écriture de flots d’octets – File{In|Out}putStream
– File{Reader|Writer}

Lecture et écriture de flots de ƒ Tableaux


caractères Unicode – ByteArray{In|Out}putStream Lit ou écrit un
Buffer d’octets ou de
– CharArray{Reader|Writer} char avec un flot
Pour représenter les fichiers et répertoires ƒ Chaînes de caractères Lit ou écrit
Analyse lexicale d’un flot d’entrée – String{Reader|Writer} une String
avec un flot
R. Grin Java : entrées-sorties 123 R. Grin Java : entrées-sorties 124

Décorateurs (ou filtres) Décorateurs (suite)


ƒ Pour buffériser les entrées-sorties : ƒ Pour écrire dans un flot tous les types de données
– Buffered{In|Out}putStream sous forme de chaînes de caractères :
– Buffered{Reader|Writer} – PrintStream
– PrintWriter
ƒ Pour permettre lecture et écriture des types
primitifs sous une forme binaire : ƒ Pour permettre de replacer un caractère lu dans le
– Data{In|Out}putStream
flot :
– PushbackInputStream
ƒ Pour compter les lignes lues : – PushbackReader
– LineNumberReader

R. Grin Java : entrées-sorties 125 R. Grin Java : entrées-sorties 126

21
Lecture et écriture de flots d’octets Lecture et écriture
de flots de caractères

R. Grin Java : entrées-sorties 127 R. Grin Java : entrées-sorties 128

Quelques classes associées à la


lecture d’un flot d’octets
Doit être sous-
classée InputStream
Lecture de flots d’octets Classe abstraite de base

FilterInputStream FileInputStream ObjectInputStream


Décorateur Lecture des octets d'un fichier Lecture d'un objet sérialisé

BufferedInputStream
Entrées bufférisées
DataInputStream
Lecture des types primitifs

R. Grin Java : entrées-sorties 129 R. Grin Java : entrées-sorties 130

Classe InputStream Méthodes de la classe InputStream


ƒ Interface publique de cette classe :
ƒ Classe abstraite abstract int read() throws IOException
ƒ C’est la racine des classes liées à la lecture int read(byte[] b) throws IOException
int read(byte[] b, int début, int nb)
d’octets depuis un flot de données throws IOException
ƒ « Interface » selon laquelle sont vues toutes long skip(long n) throws IOException
int available() throws IOException
les classes de flot qui lisent des octets (cf.
void close() throws IOException
modèle de conception « décorateur »)
ƒ Elle possède un constructeur sans paramètre synchronized void mark(int nbOctetsLimite)
synchronized void reset() throws IOException
public boolean markSupported()
R. Grin Java : entrées-sorties 131 R. Grin Java : entrées-sorties 132

22
Description des méthodes Description des méthodes
ƒ int read() ƒ int read(byte[] b)
– renvoie l’octet lu dans le flot (sous forme d'un – essaie de lire assez d’octets pour remplir le
entier compris entre 0 et 255), ou -1 si elle a tableau b
rencontré la fin du flot – renvoie le nombre d’octets réellement lus (elle est
débloquée par la disponibilité d’au moins un
– bloque jusqu’à la lecture d’un octet, ou la octet), ou -1 si elle a rencontré la fin du flot
rencontre de la fin du flot, ou d’une exception
– implémentée en utilisant la méthode read() (à
(comme toutes les autres méthodes read)
redéfinir dans les classes filles pour de meilleures
– abstraite performances)
ƒ Il est tout à fait possible que la méthode
retourne avant d’avoir rempli tout le tableau b
R. Grin Java : entrées-sorties 133 R. Grin Java : entrées-sorties 134

Description des méthodes (2) Description des méthodes (3)


ƒ int read(byte[] b, int début, int nb) ƒ boolean markSupported()
– lit nb octets et les place dans le tableau b à partir de – indique si le flot supporte la notion de marque pour
l’indice début (de début à début + nb - 1) revenir en arrière durant la lecture
– renvoie le nombre d'octets lus, ou -1 si elle a ƒ void mark(int readlimit)
rencontré la fin du flot – marque la position actuelle pour un retour ultérieur
Toutes ces méthodes sont
ƒ long skip(long n) à redéfinir dans les classes filles éventuel à cette position avec reset
– saute n octets dans le flot (pour de meilleures performances) – readlimit indique le nombre d’octets lus après
– renvoie le nombre d’octets réellement sautés lequel la marque peut être « oubliée »
ƒ int available() ƒ void reset()
– renvoie le nombre d’octets prêts à être lus – positionne le flot à la dernière marque
R. Grin Java : entrées-sorties 135 R. Grin Java : entrées-sorties 136

Description des méthodes (4) Sous-classes de InputStream


ƒ void close()
Lire depuis un tableau Mère des classes de filtre
– ferme le flot. Il est important de fermer les flots qui
ne sont plus utilisés (sauf exceptions signalées dans
la javadoc). En effet, des données du flot peuvent
être perdues si le flot n’est pas fermé. De plus les
flots ouverts sont souvent des ressources qu’il faut
économiser.
Concaténer des flots
d’entrée
Désérialiser des objets

En grisé : classes associées à des sources et destination « concrètes »


R. Grin Java : entrées-sorties 137 R. Grin Java : entrées-sorties 138

23
readFully(byte[] b)
ƒ Méthode définie dans l’interface
java.io.DataInput qu’implémente
DataInputStream ƒ Pour la lecture et l’écriture (d’octets ou de
ƒ Bloque jusqu’au remplissage du tableau b, caractères) dans un flot le JDK utilise
ou la rencontre de la fin de fichier abondamment le modèle de conception
(EOFException renvoyée), ou une erreur « décorateur » que nous allons étudier dans
d’entrée/sortie (IOException renvoyée) la section suivante
ƒ La variante readFully(byte[] b, int ƒ Les exemples porteront sur la lecture des
debut, int fin) permet de ne remplir octets
qu’une partie du tableau
R. Grin Java : entrées-sorties 139 R. Grin Java : entrées-sorties 140

Principe
ƒ Un objet « décorateur » ajoute une
Modèle de conception fonctionnalité à un objet décoré
ƒ Le constructeur du décorateur prend en
(design pattern) paramètre l’objet qu’il décore
« décorateur » ƒ Quand un décorateur reçoit un message, il
remplit sa fonctionnalité (la « décoration ») ;
si besoin est, il fait appel à l’objet décoré
pour remplir les fonctionnalités de base

R. Grin Java : entrées-sorties 141 R. Grin Java : entrées-sorties 142

Exemple On peut décorer un décorateur


ƒ Décoration d’un InputStream par un
InputStreamBuffer ƒ L’exemple à suivre montre qu’un
décorateur peut décorer un autre décorateur
ƒ isb, un InputStreamBuffer, ajoute un
buffer (disons de 512 octets) à un ƒ C’est possible parce que, selon le pattern
InputStream is décorateur,
ƒ isb.read() – le décorateur décore un InputStream
va chercher un octet dans le buffer rempli – et que le décorateur et le décoré « sont-
par une précédente lecture des » InputStream (par héritage)
Si le buffer est vide, isb demande d’abord à
is de remplir le buffer (avec 512 octets)
R. Grin Java : entrées-sorties 143 R. Grin Java : entrées-sorties 144

24
Classes de l’exemple Lire des types primitifs
ƒ L’exemple utilise depuis un fichier
– FileInputStream : classe de base pour FileInputStream fis =
la lecture d’un fichier ; décorée par un new FileInputStream("fichier");
– BufferedInputStream : décorateur qui BufferedInputStream bis =
new BufferedInputStream(fis);
ajoute un buffer pour la lecture du flot ; DataInputStream dis =
décoré par un new DataInputStream(bis);
– DataInputStream : décorateur qui double d = dis.readDouble(); Codage UTF-8
décode les types primitifs Java codés String s = dis.readUTF(); (Unicode Text Format)
dans un format standard, indépendant du int i = dis.readInt(); pour les String
système dis.close();
A mettre dans un finally ou utiliser try avec ressource
R. Grin Java : entrées-sorties 145 R. Grin (voir section « Exceptions » plus loin dans ce cours)146
Java : entrées-sorties

Variante de l’exemple
Intérêt du pattern décorateur
„ En fait, comme on n’utilisera que le flot
décoré dis, on n’a pas besoin des variables
ƒ L’héritage permet aussi d’ajouter des
intermédiaires et on écrira : fonctionnalités
DataInputStream dis =
ƒ Quand choisir le pattern décorateur plutôt
new DataInputStream(
new BufferedInputStream(
que l’héritage ?
new FileInputStream("fichier"));

Ce code se trouvera le plus souvent dans


un try avec ressource pour que les flots
soient fermés automatiquement
R. Grin Java : entrées-sorties 147 R. Grin Java : entrées-sorties 148

Intérêt du pattern décorateur


ƒ Il est utile quand un objet de base peut être Les filtres
décoré de multiples façons
ƒ Si on utilisait l’héritage, on aurait de ƒ Dans le JDK, les décorateurs sont appelés
nombreuses classes, chacune représentant filtres
l’objet de base, décoré d’une ou plusieurs ƒ On va étudier l’implémentation du pattern
décorations décorateur avec ces filtres
ƒ Avec ce pattern, on a seulement une classe ƒ Les décorateurs de flots d’entrée héritent de
par type de décoration la classe FilterInputStream
ƒ De plus on peut décorer un objet
dynamiquement pendant l’exécution
R. Grin Java : entrées-sorties 149 R. Grin Java : entrées-sorties 150

25
Constructeur des filtres Mécanisme des filtres
ƒ Le constructeur protected de ƒ Quand on demande au filtre de lire une
FilterInputStream garde le flot à décorer donnée,
dans une variable d’instance in (protected, – le filtre fait son traitement (par exemple,
de type InputStream) : chercher s’il a déjà la donnée dans son buffer)
FilterInputStream(InputStream in) { – fait appel à in s’il a besoin du flot qu’il décore
this.in = in;
} (par exemple s’il a besoin d’une lecture réelle)
ƒ Ce constructeur est appelé par les constructeurs ƒ in peut lui-même être un filtre car les classes
des classes de décorateurs; par exemple : des filtres sont des sous-classes de
BufferedInputStream(InputStream in) { InputStream (design pattern décorateur)
super(in);
. . .
R. Grin Java : entrées-sorties 151 R. Grin Java : entrées-sorties 152

Lire les octets d’un fichier


Fermeture des filtres
ƒ Pour lire un fichier qui contient des octets
(images, vidéo, etc…) qu’on ne peut lire sous ƒ La fermeture d’un filtre du JDK ferme le
la forme de types Java particuliers : flot qu’il décore
File f = new File("fichier");
int tailleFichier = (int)f.length(); ƒ Dans l’exemple du transparent précédent, la
byte[] donnees = new byte[tailleFichier]; fermeture de dis suffit pour fermer les flots
DataInputStream dis =
new DataInputStream( qu’il décore
new FileInputStream(f));
dis.readFully(donnees);
dis.close(); A mettre dans un finally (voir section
« Exceptions » plus loin dans ce cours)
R. Grin Java : entrées-sorties 153 R. Grin Java : entrées-sorties 154

Quelques classes associées à


l’écriture d’un flot d’octets
Doit être sous-
classée OutputStream
Classe abstraite de base
Écriture de flots d’octets FilterOutputStream FileOutputStream ObjectOutputStream
Décorateur Ecriture des octets d'un fichier Ecriture d'un objet sérialisé
BufferedOutputStream
Sorties bufférisées
DataOutputStream
Ecriture de types primitifs
PrintStream
Utilisé par System.out
Ne pas utiliser autrement

R. Grin Java : entrées-sorties 155 R. Grin Java : entrées-sorties 156

26
Classe OutputStream
Sous-classes de OutputStream
ƒ Interface publique de cette classe (ajouter
throws IOException à toutes les méthodes) :
abstract void write(int b)
void write(byte[] b)
void write(byte[] b, int début, int nb)
void flush()
void close()
ƒ Remarque : avec la méthode write(int b), seul Sérialiser des objets
l’octet de poids faible de b est écrit dans le flot
R. Grin Java : entrées-sorties 157 R. Grin Java : entrées-sorties 158

Particularités de PrintStream Utilisation de


ByteArrayOutputStream
ƒ Cette classe possède les 2 méthodes print() et
println() qui écrivent tous les types de ƒ Utile lorsque l’on veut ranger des octets dans
un tableau octets, sans connaître au départ le
données sous forme de chaînes de caractères
nombre d'octets :
ƒ Aucune des méthodes de PrintStream ne lève ByteArrayOutputStream out =
new ByteArrayOutputStream();
d’exception ; on peut savoir s’il y a eu une // On envoie des octets dans le flot
erreur en appelant la méthode checkError() int b;
while ((b = autreValeur()) > 0) {
ƒ Attention, println() n’effectue un flush() out.write(b);
(vidage des buffers) que si le PrintStream a }
// On récupère les octets dans un tableau
été créé avec le paramètre « autoflush » byte[] octets = out.toByteArray();
R. Grin Java : entrées-sorties 159 R. Grin Java : entrées-sorties 160

Utilisation de Écrire des types primitifs


ByteArrayOutputStream dans un fichier
DataOutputStream dos =
ƒ On peut aussi récupérer les octets sous la
new DataOutputStream(
forme d’une String
new BufferedOutputStream(
ƒ La méthode toString() de new FileOutputStream("fichier")));
ByteArrayOutputStream utilise pour cela le dos.writeDouble(12.5);
codage par défaut des caractères dos.writeUTF("Dupond");
dos.writeInt(1254);
ƒ On peut choisir un autre codage avec la
dos.close(); Ce code se trouvera le plus souvent dans
méthode toString(String codage) :
byte[] octets = out.toString("UTF8"); un try avec ressource pour que les flots
soient fermés automatiquement
R. Grin Java : entrées-sorties 161 R. Grin Java : entrées-sorties 162

27
Écrire des types primitifs
à la fin d’un fichier
ƒ Le constructeur
FileOutputStream(String nom,
boolean append) Exceptions
permet d’ajouter à la fin du fichier
ƒ Sinon, le contenu du fichier est effacé à la
création du flot

R. Grin Java : entrées-sorties 163 R. Grin Java : entrées-sorties 164

Principales exceptions liées aux Traitement des exceptions


entrées-sorties ƒ Un traitement des exceptions correct est indispensable
lors des traitements des entrées-sorties
Exception
ƒ Nombreuses variantes dans le traitement des exceptions
IOException
suivant ce que l’on veut faire
Exception durant une entrée-sortie ƒ Attention, pour simplifier leur lecture, quelques
exemples de ce cours, ne comportent pas le traitement
EOFException FileNotFoundException
des exceptions
Lecture d’une fin de fichier Fichier n’existe pas ƒ Les 3 transparents qui suivent sont des exemples
complets de traitement des exceptions
ObjectStreamException ƒ Par manque de place, la lecture est effectuée sans buffer ; il
Problème lié à la sérialisation faudrait décorer avec un BufferedInputStream

R. Grin Java : entrées-sorties 165 R. Grin Java : entrées-sorties 166

Lire des types primitifs dans une Traitement des exceptions ; variante
boucle ; traitement des exceptions DataInputStream dis;
try {
try {
DataInputStream dis = dis =
new DataInputStream(new FileInputStream("fich")); new DataInputStream(new FileInputStream("fich"));
try { while (true) {
while (true) { double d = dis.readDouble();
double d = dis.readDouble();
Remarquez . . . 1 seul bloc try
. . . l’emplacement }
Vide ; juste pour sortir des blocs catch }
}
de la boucle while et finally catch(EOFException e) {}
}
catch(EOFException e) {} catch(FileNotFoundException e) { . . }
catch(IOException e) { . . . } catch(IOException e) { . . . }
finally { try {dis.close();} catch (IOException) {…} } finally {
} if (dis != null)
catch(FileNotFoundException e) { . . . } try {dis.close();} catch (IOException) {…}
}
R. Grin Java : entrées-sorties 167 R. Grin Java : entrées-sorties 168

28
Traitement des exceptions ; JDK 7 Cas où les exceptions ne sont pas
try ( traitées mais renvoyées par la méthode
DataInputStream dis =
public void lire(String fichier) throws IOException {
new DataInputStream(new FileInputStream("fich"))) {
while (true) { DataInputStream dis =
double d = dis.readDouble(); new DataInputStream(new FileInputStream(fichier));
. . . try {
} while (true) {
} double d = dis.readDouble();
catch(EOFException e) {} . . .
catch(FileNotFoundException e) { . . } }
catch(IOException e) { . . . } }
Peut-on/Doit-on
catch(EOFException e) {}
enlever cette ligne ?
finally {
Le JDK 7 fournit le try-avec-ressources if (dis != null) dis.close();
(voir cours sur les exceptions) }
R. Grin Java : entrées-sorties 169 R. Grin Java : entrées-sorties 170

Cas où les exceptions ne sont pas


traitées par la méthode – JDK 7
public void lire(String fichier) throws IOException {
try (
DataInputStream dis =

) {
new DataInputStream(new FileInputStream(fichier)) Lecture d’un flot de caractères
while (true) {
double d = dis.readDouble();
. . .
}
}
catch(EOFException e) {}
}
R. Grin Java : entrées-sorties 171 R. Grin Java : entrées-sorties 172

Particularité des flots de caractères


ƒ La différence avec les flots d’octets est que la
représentation des caractères nécessite le choix ƒ Cette section et la suivante étudient les
d’un codage (ISO-8859-1, UTF-8,…) classes de base pour la lecture et l’écriture
des flots de caractères
ƒ Des méthodes et des classes de l’API
(FileReader et FileWriter par exemple) ƒ La section qui vient après, montre comment
utilisent le codage par défaut définit par prendre en compte le codage des caractères
l’environnement (le système d’exploitation). Il
vaut mieux éviter ces classes et méthodes pour
ne pas dépendre du codage par défaut (voir
section sur le codage des caractères)
R. Grin Java : entrées-sorties 173 R. Grin Java : entrées-sorties 174

29
Hiérarchie des principales classes
Classes de base
de lecture d’un flot de caractères
ƒ La classe Reader lit des caractères dans un
flot sous la forme de int ou de char[]
ƒ Writer envoie des caractères dans un flot
ƒ Ces 2 classes sont abstraites

R. Grin Java : entrées-sorties 175 R. Grin Java : entrées-sorties 176

Méthodes publiques
de la classe Reader Détails sur les méthodes
int read() throws IOException ƒ read() renvoie un entier compris entre 0 et
int read(char[] b) throws IOException 65535, ou -1 si la fin du flot a été atteinte
abstract int read(char[] b, int début, int nb)
ƒ read qui prend un char[] en paramètre
throws IOException
long skip(long n) throws IOException remplit le tableau avec les caractères lus et
boolean ready() throws IOException renvoie le nombre de caractères lus, ou -1
abstract void close() throws IOException si la fin du flot a été atteinte
synchronized void mark(int nbOctetsLimite) ƒ Ces méthodes bloquent jusqu’à la lecture
synchronized void reset() throws IOException
d’un caractère, l’arrivée d’une exception ou
boolean markSupported()
de la fin du flot
R. Grin Java : entrées-sorties 177 R. Grin Java : entrées-sorties 178

Sous-classes de Reader Lecture d’un flot composé de


lignes de texte
Comptabilise les lignes lues
ƒ On utilise la classe BufferedReader qui
comprend la méthode
Lire depuis un tableau
String readLine()

Possibilité de remettre dans


le flot un caractère déjà lu

Pour connecter des threads


Lire depuis une chaîne avec des pipes (lecture)
R. Grin Java : entrées-sorties 179 R. Grin Java : entrées-sorties 180

30
Séparateurs des données Relire des données avec séparateurs
ƒ Pour les flots d’octets, il suffit de relire les ƒ Le plus simple est d’utiliser la méthode
données dans l’ordre dans lequel elles ont été String[] split(String exprReg)
écrites de la classe String pour décomposer les
ƒ Pour les flots de caractères, on doit lignes du flot (voir section sur les expressions
explicitement mettre des séparateurs entre les régulières dans la 2ème partie de ce cours sur
données ; par exemple, pour distinguer un les entrées-sorties)
nom d’un prénom

R. Grin Java : entrées-sorties 181 R. Grin Java : entrées-sorties 182

Hiérarchie des principales classes


d’écriture d’un flot de caractères

Écriture dans un flot de caractères


FileWriter
Ecriture de caractères Unicode
dans un fichier, sous forme d’octets

R. Grin Java : entrées-sorties 183 R. Grin Java : entrées-sorties 184

Méthodes publiques
de la classe Writer Sous-classes de Writer
„ (Ajouter throws IOException à toutes les Ecrire dans un tableau (semblable
méthodes) à ByteArrayOutputStream)
N’écrit que les 2 octets
void write(int c) de poids faibles
void write(char[] b) Pour connecter des threads
abstract void write(char[] b, int déb, int nb) avec des pipes (écriture)
void write(String s)
Écrire dans une chaîne
void write(String s, int déb, int nb)
abstract void flush(long n)
abstract void close()
R. Grin Java : entrées-sorties 185 R. Grin Java : entrées-sorties 186

31
PrintWriter Constructeurs de PrintWriter
ƒ Cette classe est un décorateur pour un ƒ Le JDK 5 a offert des facilités pour créer
OutputStream ou un Writer directement un PrintWriter qui écrit dans
ƒ Elle contient les méthodes print, println ou un fichier dont on donne le nom, sans créer
printf qui permettent d’écrire dans une flot explicitement les flots sous-jacents décorés
comme si on affichait sur l’écran (le fameux ƒ Un buffer est utilisé mais les println ne
System.out.println) provoquent pas de flush automatique
ƒ Ces méthodes ne lancent jamais d’exception ;
la méthode boolean checkError() renvoie
true s’il y a eu une exception en interne
R. Grin Java : entrées-sorties 187 R. Grin Java : entrées-sorties 188

Constructeurs de PrintWriter Constructeurs de PrintWriter


ƒ PrintWriter(String nomFichier[,
String nomCharset])
ƒ Avant le JDK 5, il fallait passer un
OutputStream ou un Writer en paramètre
Si le fichier existe déjà, il est écrasé ; pas de
flush automatique à chaque println ƒ On doit encore utiliser cette décoration
explicite (voir exemples plus loin dans cette
ƒ Un 2ème paramètre optionnel permet de
section)
donner le nom d’un autre Charset que celui
par défaut (recommandé): – si on veut ne pas écraser un fichier existant
PrintWriter(File fichier [, String (mais écrire à la fin du fichier)
nomCharset]) – si on veut un flush automatique à chaque
println
R. Grin Java : entrées-sorties 189 R. Grin Java : entrées-sorties 190

Séparer les lignes


ƒ La façon de séparer les lignes dépend du
système d’exploitation
ƒ Pour être portable utiliser
– println de PrintWriter (le plus simple) Lecture et écriture de caractères
– writeLine ou newLine de dans des fichiers –
BufferedWriter
– ou la propriété système line.separator codage des caractères
(System.getProperty("line.separator"))
ƒ Ne pas utiliser le caractère \n qui ne convient
pas, par exemple, pour Windows
R. Grin Java : entrées-sorties 191 R. Grin Java : entrées-sorties 192

32
Codage Codage par défaut
ƒ En Java les caractères sont codés en Unicode ƒ Un codage par défaut est automatiquement
ƒ Ce n’est souvent pas le cas sur les installé par le JDK, conformément à
périphériques source ou destination des flots l’environnement (le plus souvent selon la
(ASCII étendu ISO 8859-1 pour les français, locale et le système d’exploitation ; l’IDE peut
mais le codage UTF-8 tend à se généraliser) aussi installer son propre codage)
ƒ Des classes spéciales permettent de faire les ƒ On l’obtient par la méthode static
traductions entre le codage Unicode et un Charset Charset.defaultCharset()
autre codage
ƒ Les codages sont représentés par la classe
java.nio.charset.Charset
R. Grin Java : entrées-sorties 193 R. Grin Java : entrées-sorties 194

Codages supportés par Java


Code pour afficher tous les codages
ƒ SortedMap<String,Charset>
Charset.availableCharsets() donne tous SortedMap<String,Charset> charsets =
les codage supportés Charset.availableCharsets();
Set<String> nomsCharsets =
ƒ Par exemple, le codage pour les langues charsets.keySet();
d’Europe de l’Ouest est le codage ISO 8859-1, for (String nom : nomsCharsets) {
représenté en Java par le nom « ISO-8859-1 » System.out.println(nom);
(ou par StandardCharsets.ISO_8859_1) }
ƒ On peut aussi trouver les codages de noms
UTF-8, UTF-16, et de très nombreux autres
codages
R. Grin Java : entrées-sorties 195 R. Grin Java : entrées-sorties 196

Conseils à propos du codage


Symptômes d’un mauvais codage
ƒ Ne pas s’appuyer sur le codage par défaut,
ƒ Si des caractères de ce type apparaissent : surtout pour les applications qui doivent
« é », ou « î », c’est que les données fonctionner pour de nombreuses années : ce
sont au format UTF-8 mais que le codage par défaut peut changer et l’application
programme pense avoir à faire à de l’ISO- peut être portée sur d’autres systèmes ; il est
8859-1 donc préférable d’indiquer explicitement le
ƒ Inversement, si des « � » apparaissent, les codage
données sont enregistrées en ISO-8859-1 ƒ Privilégier le codage UTF-8 à toutes les
alors que le programme pense avoir à faire à occasions : contenu des fichiers, du code des
de l’UTF-8 classes, des pages Web, base de données,…
R. Grin Java : entrées-sorties 197 R. Grin Java : entrées-sorties 198

33
Ponts entre les flots de caractères
Remarque
et les flots d’octets (1/2)
ƒ Le JDK 7 fournit la classe Files (étudiée au ƒ InputStreamReader et
début de ce support) qu’il faut utiliser dans les OutputStreamWriter sont des classes filles
cas les plus simples où le contenu du fichier de Reader et Writer
peut être enregistré dans la mémoire centrale ƒ InputStreamReader lit des caractères dans
et lorsqu’on ne souhaite pas faire de traitement un flot ; ces caractères, codés dans le flot
suivant un codage particulier, sont décodés en
spécial lors de la lecture ou de l’écriture
caractères Unicode
ƒ Les transparents suivants montrent comment ƒ OutputStreamWriter écrit des caractères
lire et écrire des caractères dans un fichier Unicode en les codant sous forme d’octets en
dans les autres cas utilisant un codage particulier
R. Grin Java : entrées-sorties 199 R. Grin Java : entrées-sorties 200

Ponts entre les flots de caractères Ponts entre les flots de caractères
et les flots d’octets (2/2) et les flots d’octets
ƒ Leur constructeur prend en paramètre un flot
d’octets ; par exemple, ƒ On peut préciser un codage particulier en
public InputStreamReader(InputStream in) paramètre du constructeur (idem pour
ƒ Les octets sont lus dans le flot in et sont OutputStreamWriter) si on ne veut pas le
décodés en caractères Unicode par le codage codage par défaut :
public InputStreamReader(InputStream in,
associé à InputStreamReader Charset cs)
ƒ Exemple :
new InputStreamReader(
in, Charset.forName("UTF-8"))
R. Grin Java : entrées-sorties 201 R. Grin Java : entrées-sorties 202

Lecture-écriture
Exemple
dans un fichier de texte
ƒ File{Reader|Writer} sont des classes filles de
FileInputStream fis =
InputStreamReader et OutputStreamWriter new FileInputStream("fichier");
ƒ Elles permettent de lire et d’écrire des caractères Reader reader =
Unicode dans un fichier, suivant le codage par new InputStreamReader(
défaut (rappel : pas recommandé !) fis,
Charset.forName("UTF-8"));
ƒ Utiliser leur classe mère pour un autre codage ; // ou StandardCharsets.UTF_8
par exemple, InputStreamReader, pour décorer
un FileInputStream
ƒ PrintWriter permet aussi de préciser un codage
R. Grin Java : entrées-sorties 203 R. Grin Java : entrées-sorties 204

34
Travail avec un fichier composé Remarque
de lignes de texte ƒ Dans les 3 exemples de code suivants on
n’attrape pas les IOException
ƒ En lecture, on utilise la classe BufferedReader
ƒ L’en-tête de la méthode qui contient le code
qui comprend la méthode readLine
devra donc comporter
ƒ En écriture, on utilise la classe PrintWriter throws IOException
qui comprend les méthodes print, println et ƒ Dans la pratique ça sera le plus souvent le cas
printf
car la méthode qui fait les entrées-sorties sait
rarement comment réparer en cas de
IOException

R. Grin Java : entrées-sorties 205 R. Grin Java : entrées-sorties 206

Lire les lignes de texte d’un fichier Lire les lignes de texte –
BufferedReader br = try avec ressource du JDK 7
new BufferedReader(
try (
new FileReader("fichier"));
BufferedReader br =
try {
new BufferedReader(
String ligne;
new FileReader("fichier"));
while ((ligne = br.readLine()) != null) {
) {
// Traitement de la ligne
String ligne;
. . .
On n’utilise pas while ((ligne = br.readLine()) != null) {
}
EOFException // Traitement de la ligne
} pour repérer la fin du fichier . . .
finally {
}
if (br != null) br.close();
}
}
R. Grin Java : entrées-sorties 207 R. Grin Java : entrées-sorties 208

Écrire une ligne de texte (JDK 7) Écrire une ligne de texte (JDK 1.4)
PrintWriter pw = Un paramètre optionnel
try (PrintWriter pw = new PrintWriter("f")) { permettrait d’ajouter à la
new PrintWriter(
fin du fichier
String ligne; new BufferedWriter(
. . . new FileWriter("fichier")),
pw.println(ligne); true);
Un buffer est Si on veut un vidage
. . . String ligne;
automatiquement utilisé des buffers après
} . . . chaque println()
pw.println(ligne);
Un buffer n’est pas
. . .
automatiquement utilisé

Ne pas oublier la
fermeture du flot !
R. Grin Java : entrées-sorties 209 R. Grin Java : entrées-sorties 210

35
Recopier un fichier texte Recopier un fichier texte –
BufferedReader in = null;
BufferedWriter out = null;
try avec ressource du JDK 7
try { try (
in = BufferedReader in =
new BufferedReader(new FileReader("f1")); new BufferedReader(new FileReader("f1"));
out = BufferedWriter out =
new BufferedWriter(new FileWriter("f2")); new BufferedWriter(new FileWriter("f2"));
int c; // code Unicode du caractère lu ) {
while ((c = in.read()) != -1) int c; // code Unicode du caractère lu
out.write(c); while ((c = in.read()) != -1)
} Test de fin de fichier out.write(c);
finally { }
if (in != null) in.close();
if (out != null) out.close(); Encore plus simple si on utilise Files.copy !
}
R. Grin Java : entrées-sorties 211 R. Grin Java : entrées-sorties 212

Recopier en changeant de codage


try (BufferedReader reader =
new BufferedReader(new InputStreamReader(
new FileInputStream("f-iso8859-1.txt"),
StandardCharsets.ISO_8859_1));
PrintWriter writer =
new PrintWriter("f-utf-8.txt", "UTF-8")) {
URL et URI
String ligne;
while ((ligne = reader.readLine()) != null) {
writer.println(ligne);
} Un fichier codé en
} ISO-8859-1 est copié
catch(IOException) { ... } en un autre fichier
codé en UTF-8
R. Grin Java : entrées-sorties 213 R. Grin Java : entrées-sorties 214

URI et URL URL


ƒ Un URI (Uniform Resource Identifier) est un ƒ Un URL peut être représenté par une chaîne
identificateur d’une ressource accessible de caractères du type
localement ou sur Internet protocole:nomRessource
ƒ Un URL (Uniform Resource Locator) est un ƒ Le format pour le nom de la ressource dépend
type d’URI qui identifie une ressource par son du protocole
emplacement sur le réseau ƒ Exemple avec nom absolu :
ƒ Il existe aussi un autre type d’URI, rarement http://deptinfo.unice.fr/~toto/inde
utilisé, les URN (N pour Name) qui identifient x.html
une ressource par un nom, indépendant de son ƒ Exemple avec nom relatif : Le séparateur est
emplacement sur le réseau ; par exemple file:rep1/rep2 toujours « / »,
« urn:ietf:rfc:2141 » pour tous les
R. Grin Java : entrées-sorties 215 R. Grin Java : entrées-sorties systèmes 216

36
Caractères d’un URL Caractères spéciaux
ƒ Un URL ou URI ne peut contenir qu’un sous- ƒ Les autres caractères sont encodés dans des
ensemble des caractères du code ASCII : octets ; chaque octet est représenté par un %
lettres, chiffres et les caractères suivi du code hexadécimal de l’octet
- _ . ! ~ * ‘ ,
ƒ Par exemple, un espace est représenté par
ƒ Les caractères %20 (car le code hexadécimal de l’espace est
? & @ % / # ; : $ + =
20 : 32 en décimal) ; pour faciliter l’usage
servent pour séparer les différentes parties des espaces, ils sont aussi encodés par un
d’un URL ; par exemple, ? sert à séparer une « + » (+ est lui-même encodé par %2B)
adresse d’une chaîne passée en paramètre
R. Grin Java : entrées-sorties 217 R. Grin Java : entrées-sorties 218

Problèmes de codage Problèmes de codage


ƒ Cette façon d’encoder certains caractères pose ƒ Les classes URLEncoder et URLDecoder du
des problèmes en environnement hétérogène paquetage java.net sont des méthodes
ƒ Ainsi « é » n’est pas codé de la même façon static qui effectuent le codage/décodage :
sur un Macintosh et sur un système Windows – URLEncoder.encode(String, String)
transforme tous les caractères interdits en
ƒ Il faut donc indiquer le codage des caractères caractères autorisés
que l’on veut utiliser ; il est conseillé – URLDecoder.decode(String, String) fait
d’utiliser au maximum UTF-8 pour éviter les l’inverse
problèmes de portabilité ƒ Le 2ème paramètre indique le codage des
caractères
R. Grin Java : entrées-sorties 219 R. Grin Java : entrées-sorties 220

URL sous Windows


Exemple ƒ Ne pas utiliser « \ » mais « / » comme
séparateur dans les noms de fichiers
ƒ URL url = getclass().getResource(
"/un répertoire/fichier.txt"); ƒ Plusieurs formats sont acceptés pour un
System.out.println(url); même emplacement
System.out.println(
URLDecoder.decode(url.toString(), ƒ Noms absolus (le 1er est préférable) :
"UTF-8")); file:/C:/autoexec.bat
ƒ affiche file:C:/autoexec.bat
file:/…/un%20r%c3%a9pertoire/fichier.txt ƒ Noms relatifs :
file:/…/un répertoire/fichier.txt file:C:truc.txt
file:truc.txt (si C est le disque courant)
R. Grin Java : entrées-sorties 221 R. Grin Java : entrées-sorties 222

37
Principaux protocoles Adresse Web
ƒ Format des adresses http :
ƒ http pour le protocole HTTP (pages HTML) http://machine[:port]/cheminPage[#ancre]
ƒ file pour les fichiers locaux (file:chemin) ƒ Exemples
ou les fichiers sur une autre machine http://dept.unice.fr/~toto/index.html
http://dept.unice.fr:8080/~toto/index.html
(file://hote/chemin) http://dept.unice.fr/~grin/index.html#intro
ƒ ftp pour FTP (transfert de fichiers)
ƒ telnet pour une connexion par telnet
ƒ news pour les news

R. Grin Java : entrées-sorties 223 R. Grin Java : entrées-sorties 224

Classe URL Constructeurs de la classe URL


ƒ On peut créer un URL avec une String,
ƒ La classe java.net.URL représente un URL
ou à partir des éléments de base (machine,
ƒ Elle fournit de nombreux constructeurs port, etc.) passés comme des String
ƒ Si la String passée à un constructeur ne ƒ On peut aussi créer un URL en passant une
correspond pas à la syntaxe des URL, le adresse relative à un URL (le contexte) ; par
constructeur lance l’exception contrôlée exemple, si url correspond à l’URL
java.net.MalformedURLException, fille http://deptinfo.unice.fr/tp/tp1/index.html,
de IOException new URL(url, "../tp2/index.html")
correspond à l’URL
http://deptinfo.unice.fr/tp/tp2/index.html
R. Grin Java : entrées-sorties 225 R. Grin Java : entrées-sorties 226

Méthode de la classe URL Lire le code HTML


d’une page Web
ƒ On peut extraire les éléments de base à
ƒ La classe URL fournit la méthode
partir d’un URL (getPort, getHost, etc.) InputStream openStream() throws IOException
ƒ Les données associées à l’URL peuvent être qui permet de lire le contenu d’un URL :
URL url = new URL(
obtenues par les méthodes "http://www.unice.fr/index.html");
openConnection et openStream ou par la InputStream is = url.openStream();
// Pour lire ligne à ligne
méthode getContent BufferedReader br = new BufferedReader(
new InputStreamReader(is));
while ((ligne = br.readLine()) != null) {
System.out.println(ligne);
}
R. Grin Java : entrées-sorties 227 R. Grin Java : entrées-sorties 228

38
URL relatif dans une applet Classe URLConnection
ƒ Obtenir l’URL d’un fichier placé dans le même ƒ La méthode openConnection() de la classe
répertoire que le document HTML contenant une URL fournit une instance de URLConnection
applet (getCodeBase pour une position relative à
la classe de l’applet) : ƒ La classe URLConnection permet d’obtenir
URL urlDoc = getDocumentBase(); des informations sur l’URL (type, codage, date
String nomFichier = getParameter("fichier"); de dernière modification, etc.)
try {
urlFichier = new URL(urlDoc, nomFichier);
ƒ On peut aussi obtenir un flot en lecture ou en
} écriture (getInputStream/getOuputStream)
catch(MalformedURLException e) { vers l’URL (si le protocole le permet)
. . .
}
R. Grin Java : entrées-sorties 229 R. Grin Java : entrées-sorties 230

Classe URI URI – URL – Path


ƒ La classe java.net.URI contient (entre
autres) des méthodes resolve et ƒ Il est facile de passer d’une classe à l’autre
relativize qui facilitent le passage entre pour profiter des fonctionnalités de chacune
noms relatifs par rapport à un URI de base, et ƒ Passer de URL à URI : toURI() (classe URL)
noms absolus des fichiers ƒ Passer de URI à URL : toURL() (classe URI)
ƒ Elle permet aussi d’obtenir un Path à partir ƒ Passer de URI à Path (et donc de URL à Path
d’un URL par la méthode Paths.get(URI): avec toURI() avant) : Paths.get(URI)
Path path = Paths.get(url.toURI());
ƒ Passer de Path à URI : toURI() (interface
Path)
R. Grin Java : entrées-sorties 231 R. Grin Java : entrées-sorties 232

Classe URI Obtenir un nom de fichier


ƒ Les caractères interdits sont automatiquement
à partir d’un URL
traités si on passe de URI à URL : ƒ Il n’est pas évident d’obtenir un nom de
URI uri = new URI( fichier à partir d’un URI (ou d’un URL) :
"http", "//m1.com/un url", null);
URL url = uri.toURL(); l’espace sera extraction du chemin du fichier, puis
// Affiche « /un url » remplacé par %20 décodage de ce nom pour enlever les
System.out.println(uri.getPath()); caractères interdits dans un URL ; de plus il
// Affiche « /un%20url »
System.out.println(url.getPath()); faut tenir compte du séparateur dans les
noms de fichier, qui dépend du système
d’exploitation
ƒ Il faut utiliser la méthode Paths.get(URI)
R. Grin Java : entrées-sorties 233 R. Grin Java : entrées-sorties 234

39
Exemple : nom du répertoire qui
Paths.get(URI)
contient le jar exécutable
ƒ Cette méthode permet d’obtenir un Path à ƒ Si l’application est dans un jar, il peut être
partir d’un URI utile de connaître le nom du répertoire qui
ƒ Si on veut le nom du fichier correspondant, contient le jar (par exemple pour voir s’il
il suffit d’utiliser la méthode toString() contient des fichiers de configuration) :
URL urlRep =
de Path ClassLoader.getSystemClassLoader()
.getResource(".");
// ou this.getClass().getResource("/");
Path rep = Paths.get(urlRep.toURI());
String nomRep = rep.toString();
R. Grin Java : entrées-sorties 235 R. Grin Java : entrées-sorties 236

Un problème fréquent
ƒ Un projet fonctionne dans l’environnement
de développement mais ne fonctionne plus
dès que l’application est distribuée sous la
Noms de fichiers, forme d’un fichier jar
ressources ƒ En effet les images ou les fichiers divers
utilisés par l’application ne sont alors plus
trouvés par l’application
ƒ La solution est d’utiliser des noms de
ressource et pas des noms de fichier pour
désigner les images ou les fichiers divers
R. Grin Java : entrées-sorties 237 R. Grin Java : entrées-sorties 238

Le problème avec les noms de fichiers Le problème avec les noms relatifs
ƒ Certaines classes étudiées dans cette partie
ƒ Les noms relatifs sont relatifs au répertoire
du cours (Files en particulier) désignent
courant (propriété système user.dir)
un fichier par son nom relatif ou absolu
ƒ Le répertoire courant est généralement le
ƒ Si on utilise un nom absolu, il faudra répertoire dans lequel la JVM a été lancée
recompiler l’application dès que le fichier (commande java)
changera de place ƒ Mais on ne connaît pas à l’avance ce
ƒ L’utilisation de noms relatifs pose aussi des répertoire
problèmes ƒ De plus, le répertoire courant est difficile à
ƒ De plus le fichier peut se trouver dans un maîtriser dans certains environnements
fichier jar complexes d’exécution (EJB par exemple)
R. Grin Java : entrées-sorties 239 R. Grin Java : entrées-sorties 240

40
Une meilleure solution Nom d’une ressource
ƒ Il est préférable d’utiliser un nom de
ressource pour désigner le fichier et ƒ Cet endroit dépend de la façon dont la
d’utiliser les méthodes de la classe Class classe a été chargée en mémoire
URL getResource(String nom) ƒ Le plus souvent (voir cours sur l’interface
ou avec le système),
InputStream – si le nom est relatif, il est relatif au
getResourceAsStream(String nom)
répertoire où se trouve le fichier .class qui
ƒ Le nom de la ressource passé en paramètre contient le code
à ces 2 méthodes indique l’endroit où la – si le nom est absolu (commence par
ressource sera recherchée (voir transparents « / »), il est relatif au classpath
suivants)
R. Grin Java : entrées-sorties 241 R. Grin Java : entrées-sorties 242

Lire le contenu d’un fichier ressource Cas d’une méthode static


ƒ Le plus simple est d’utiliser la méthode ƒ Le code précédent ne fonctionnera pas dans
getResourceAsStream qui renvoie un une méthode static à cause de
InputStream (renvoie null si la ressource n’a « this.getClass() »
pas été trouvée) : ƒ En ce cas, il faut remplacer
InputStream in = this.getClass()
.getResourceAsStream("/rep/fich"); « this.getClass() » par l’instance d’une
des classes de l’application (la classe qui
fich recherché dans un sous répertoire rep du classpath contient le code ; pour faire simple, appelons-
la « p.C1 ») :
p.C1.class.getResourceAsStream(…)
R. Grin Java : entrées-sorties 243 R. Grin Java : entrées-sorties 244

Lire un fichier ressource texte Écrire dans une ressource (1/2)


ƒ Si la ressource contient du texte, il faut utiliser
ƒ Si on veut écrire dans un fichier qui existe
InputStreamReader pour obtenir un Reader ;
déjà en utilisant un nom de ressource pour
par exemple (in obtenu par getResourceAsStream) :
BufferedReader br = désigner le fichier, il est préférable de passer
new BufferedReader( par la classe URL (si le fichier n’existe pas
new InputStreamReader(in)); déjà, il faut s’arranger autrement ; le plus
String ligne;
while ((ligne = br.readLine()) != null) {
simple est souvent de créer un fichier vide dès
le déploiement de l’application)
ƒ On peut spécifier le codage des caractères de la
ressource si ça n’est pas le codage par défaut : ƒ On récupère d’abord l’URL du fichier par
new InputStreamReader( url = getClass().getResource("/fichier");
in, Charset.forName("UTF-8")) ƒ On ouvre ensuite un flot en écriture sur l’URL
R. Grin Interface système 245 R. Grin Java : entrées-sorties 246

41
Écrire dans une ressource (2/2) Cas particulier d’un jar
ƒ Pour ouvrir une ressource en écriture, le ƒ Si l’application est distribuée dans un jar et
plus simple est de récupérer le nom du lancé par l’option –jar de java, un chemin
fichier à partir de l’URL absolu désignera un emplacement dans le jar
ƒ Exemple (pour un fichier au format texte) : relatif à la racine du jar
URL url =
getClass().getResource("/fichier");
PrintWriter pw = new PrintWriter(
Paths.get(url.toURI()).toString());

R. Grin Java : entrées-sorties 247 R. Grin Java : entrées-sorties 248

Avantage de cette solution


ƒ Si on déplace le répertoire de l’application,
les fichiers de ressources suivent et il n’est
pas besoin de modifier le code
Sérialisation

R. Grin Java : entrées-sorties 249 R. Grin Java : entrées-sorties 250

Qu’est-ce qui est sérialisé ?


Définition
ƒ Les valeurs des variables d’instance (pas des
ƒ Sérialiser un objet c’est transformer l’état variables de classe) des instances sérialisées
(les valeurs des variables d’instance) de ƒ Des informations sur les classes des objets
l’objet en une suite d’octets sérialisés ; en particulier :
ƒ On peut ainsi conserver l’état de l’objet – nom de la classe
pour le retrouver ensuite et reconstruire un – noms, types, modificateurs des variables à sauvegarder
autre objet avec le même état – des informations qui permettent de savoir si une classe
a été modifiée entre la sérialisation et la désérialisation
ƒ Mais le code des classes n’est pas sérialisé !
R. Grin Java : entrées-sorties 251 R. Grin Java : entrées-sorties 252

42
Utilisation de la sérialisation
ƒ Conserver un objet dans un fichier ou une base
ObjectOutputStream
de données pour le récupérer plus tard
ƒ Classe qui permet de sérialiser des objets ou
ƒ Conserver la configuration d’un composant,
des types primitifs
pour pouvoir le réutiliser plus tard dans une
application (JavaBean) ƒ Les méthodes writeObject, writeInt,
writeDouble,… peuvent lancer une
ƒ Transmettre les arguments (de types non primitifs) IOException
d'une méthode appelée sur un objet distant
(RMI) : l’argument est sérialisé, les octets sont
transmis sur le réseau et l’objet est reconstruit
sur la machine distante
R. Grin Java : entrées-sorties 253 R. Grin Java : entrées-sorties 254

ObjectOutputStream ObjectInputStream
HashMap<String,Article> map = new HashMap<> ();
// remplit la table de hachage avec des objets
. . .
ƒ Classe qui permet de désérialiser des objets
try (ObjectOutputStream oos = ou des types primitifs
new ObjectOutputStream(
new FileOutputStream("fichier.ser")))
ƒ Les méthodes readObject, readInt,
{ readDouble,… peuvent lancer une
oos.writeObject(map); IOException ou une EOFException
oos.writeInt(125); // on peut écrire type primitif
} ƒ De plus la méthode readObject peut
Sérialise la map, lancer divers autres IOException ou une
et tous les objets ClassNotFoundException
qu’elle contient
R. Grin Java : entrées-sorties 255 R. Grin Java : entrées-sorties 256

ObjectInputStream
Fin de fichier
try (ObjectInputStream ois =
new ObjectInputStream( ƒ Si on lit par boucle plusieurs objets
new FileInputStream("fichier.ser"))) sérialisés, on doit tester la fin de fichier
{
avec EOFException (le test de la valeur
HashMap<String,Article> map =
(HashMap<String,Article>)ois.readObject(); null renvoyée par readObject ne marche
int i = ois.readInt(); pas)
} Récupère la map,
Renvoie un
et tous les objets
Object
qu’elle contenait
Il faut caster

R. Grin Java : entrées-sorties 257 R. Grin Java : entrées-sorties 258

43
Interface Serializable
Remarque importante
ƒ Pour pouvoir être sérialisé, un objet doit être
ƒ Si on sérialise un objet et qu’on le une instance d’une classe qui implémente
désérialise, on obtient un nouvel objet l’interface Serializable
ƒ Ce nouvel objet a le même état que l’objet ƒ Cette interface ne comporte aucune méthode ;
d’origine (sauf si on a fait un traitement elle sert seulement à marquer les classes
particulier pour le sérialiser) mais ça n’est pas sérialisables
l’objet d’origine

R. Grin Java : entrées-sorties 259 R. Grin Java : entrées-sorties 260

Classes Serializable Classes Serializable

ƒ La plupart des classes du JDK sont ƒ Le plus souvent rendre une classe
sérialisables sérialisable ne nécessite l’écriture d’aucune
ƒ Certaines classes ne peuvent pas être ligne de code
sérialisées (InputStream par exemple) ; ƒ Si la classe ne contient que des champs de
d’autres ne doivent pas l’être (par sécurité) types primitifs ou d’un type qui implémente
Serializable, ou des tableaux de ces
types, il suffit d’ajouter « implements
Serializable » dans l’en-tête de la classe

R. Grin Java : entrées-sorties 261 R. Grin Java : entrées-sorties 262

Ne pas sérialiser une variable : Sérialisation spéciale


transient ƒ On peut choisir sa propre façon de sérialiser les
objets d’une classe
ƒ Si on ne veut pas qu’une variable d’instance ƒ Dans la classe, on doit alors écrire les 2
soit sérialisée, on la déclare transient : méthodes qui doivent être private et lancer
private transient int val; IOException
void writeObject(ObjectOutputStream oos)
ƒ Quand l’objet sera désérialisé, la valeur de void readObject(ObjectInputStream ois)
cette variable devra être recalculée s’il en est ƒ Attention, ce ne sont pas les méthodes de même
besoin (dans une méthode readObject nom des classes Object{Out|In}putStream
privée ; voir transparents suivant), sinon elle ƒ Curieux des méthodes private qui sont
recevra la valeur par défaut de son type utilisées de l’extérieur, non ?
R. Grin Java : entrées-sorties 263 R. Grin Java : entrées-sorties 264

44
Sérialisation spéciale Sérialisation spéciale (2)
ƒ Les 2 méthodes readObject et writeObject ƒ Ces 2 méthodes peuvent utiliser les
n’ont à s’occuper que des variables de la classe méthodes default{Read|Write}Object()
ƒ Elles ne doivent pas s’occuper des classes Ces méthodes font une (dé)sérialisation
ancêtres normale
ƒ Si le traitement spécial ne concerne que des
variables transient, on utilise ces 2
méthodes et il ne reste plus alors qu’à traiter
d’une façon spéciale les variables
transient

R. Grin Java : entrées-sorties 265 R. Grin Java : entrées-sorties 266

Exemple de sérialisation spéciale


private void writeObject(ObjectOutputStream oos)
Empêcher la sérialisation
throws IOException {
temp = motDePasse.clone(); ƒ Pour écrire une classe non sérialisable alors
motDePasse = crypt(motDePasse);
oos.defaultWriteObject(); Le mot de passe est qu’elle hérite d’une classe sérialisable, il
motDePasse = temp; encrypté avant d’être suffit de lui ajouter des méthodes
} sérialisé readObject et writeObject qui lancent
private void readObject(ObjectInputStream ois)
une NotSerializableException
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
motDePasse = deCrypt(motDePasse);
}

R. Grin Java : entrées-sorties 267 R. Grin Java : entrées-sorties 268

Classe mère non sérialisable Sérialisation spéciale avec


writeReplace et readResolve
ƒ Une sous-classe d’une classe non
sérialisable peut être sérialisable ƒ Ces 2 méthodes n’ont pas de paramètre et renvoie
un Object
ƒ Dans ce cas c’est le rôle de la classe fille de
donner des valeurs aux variables de la ƒ writeReplace est utilisée pendant la
sérialisation et readResolve pendant la
classe mère non sérialisable (avec les
désérialisation, si elles sont accessibles (cf
méthodes private {read|write}Object) public, private,…)
ƒ La classe mère non sérialisable doit avoir un ƒ Si une classe contient ces méthodes, elles sont
constructeur sans paramètre qui sera utilisé utilisées pour remplacer l’objet qui va être
par la classe fille sérialisé/désérialisé par l’objet qu’elles renvoient
R. Grin Java : entrées-sorties 269 R. Grin Java : entrées-sorties 270

45
Utilisation de writeReplace et Sérialisation spéciale avec
readResolve writeReplace et readResolve
ƒ Les classes « d’énumération de constantes »
utilisent ces méthodes (surtout readResolve)
ƒ Le principe est de conserver les constantes
pour pouvoir conserver l’identité d’une constante dans un tableau static
ƒ En effet, si on sérialise une constante, la valeur ƒ Pour sérialiser une des constantes, on
désérialisée n’est pas égale (==) à la valeur sérialise l’indice du tableau qui permet de
sérialisée répérer la constante et on utilise cet indice
ƒ Depuis le JDK 1.5 il est plus simple et préférable au moment de la désérialisation pour
d’utiliser enum pour représenter une énumération renvoyer la bonne constante
et ces 2 méthodes ne sont alors plus utiles
R. Grin Java : entrées-sorties 271 R. Grin Java : entrées-sorties 272

Sérialisation et Sérialisation et
modification des classes (1) modification des classes (2)
ƒ Si on modifie une classe, on ne peut relire les ƒ Pour cela il est souvent conseillé de donner une
instances sérialisées avec l’ancienne version de valeur quelconque à la variable
la classe (java.io.InvalidClassException) serialVersionUID des classes que l’on écrit
ƒ On peut tout de même récupérer les valeurs des ƒ Ainsi il sera possible (si les modifications
variables qui n’ont pas changé en ajoutant effectuées dans les nouvelles versions le
private static final permettent) de récupérer les instances
long serialVersionUID = xxxxxxxxL; sérialisées avec des anciennes versions de la
comme variable de classe, en lui donnant la classe
valeur calculée sur l’ancienne version de la
classe (utiliser pour cela l’outil serialver)
R. Grin Java : entrées-sorties 273 R. Grin Java : entrées-sorties 274

Sérialisation et Sérialisation spéciale –


modification des classes Externalizable
ƒ Pour les cas plus complexes, on peut utiliser ƒ readObject et WriteObject permettent de
aussi la méthode get(String, ) personnaliser la sérialisation de la classe elle-
(surchargée pour le 2ème paramètre avec tous les même ; la sérialisation des classes ancêtres est
types primitifs et Object) de la classe interne encore effectuée automatiquement
ObjectInputStream.GetField
ƒ Les classes qui implémentent l’interface
ƒ Cette méthode permet de récupérer la valeur
Externalizable (hérite de Serializable)
d’une variable dont on donne le nom en 1er
paramètre (le 2ème paramètre indique une valeur permettent d’avoir un processus de
par défaut) sérialisation totalement personnalisé
R. Grin Java : entrées-sorties 275 R. Grin Java : entrées-sorties 276

46
Sérialisation spéciale – Sérialisation spéciale –
Externalizable Externalizable
ƒ Cette interface comporte les 2 méthodes ƒ Attention, readExternal et
void readExternal(ObjectInput in) et writeExternal sont publiques ! Il ne faut
void writeExternal(ObjectOutput out) donc pas les utiliser pour des données
ƒ ObjectInput et ObjectOutput sont 2
sensibles
interfaces qui permettent de lire et d’écrire les ƒ Il ne faut pas oublier de prendre en charge
la sérialisation des variables héritées des
types primitifs et les objets
classes ancêtres

R. Grin Java : entrées-sorties 277 R. Grin Java : entrées-sorties 278

Vérification après désérialisation (1) Vérification après désérialisation (2)


ƒ Si on veut lancer une vérification ƒ Le validateur est enregistré dans la méthode
automatique des instances d’une classe au private readObject(ObjectInputStream),
moment de la désérialisation (après la étudiée précédemment, en appellant la méthode
construction de tout le graphe des objets), il registerValidation(this, n)
faut que la classe implémente l’interface ƒ this est l’objet à valider et le 2ème paramètre n
java.io.ObjectInputValidation
est un ordre de priorité qui fixe l’ordre
ƒ Cette interface n’a qu’une seule méthode d’exécution au cas où plusieurs objets auraient
validateObject() qui doit renvoyer une des validateurs (0 est standard) ; les plus
InvalidObjectException s’il y a un grandes priorités sont exécutées en premier
problème, ce qui stoppera la désérialisation
R. Grin Java : entrées-sorties 279 R. Grin Java : entrées-sorties 280

47

Vous aimerez peut-être aussi