Dans la plupart des langages de programmation, les notions d'entrées/sorties sont considérées comme une technique de base, car les manipulations de fichiers, notamment, sont très fréquentes.
En Java, et pour des raisons de sécurité, on distingue deux cas :
java.io fournit les classes de flux et la classe File. Depuis Java 7, java.nio.file constitue l'API moderne, plus robuste et plus expressive. Les deux coexistent et se complètent.
FileLa gestion de fichiers proprement dite se fait par l'intermédiaire de la classe File. Cette classe possède des méthodes qui permettent d'interroger ou d'agir sur le système de gestion de fichiers du système d'exploitation.
Un objet de la classe File peut représenter un fichier ou un répertoire.
| Méthode / Constructeur | Description |
|---|---|
File(String name) | Crée un objet File à partir d'un chemin |
File(String path, String name) | Crée un objet File à partir d'un répertoire et d'un nom |
File(File dir, String name) | Crée un objet File à partir d'un objet répertoire |
boolean isFile() | Indique si l'objet représente un fichier |
boolean isDirectory() | Indique si l'objet représente un répertoire |
boolean mkdir() | Crée le répertoire désigné |
boolean exists() | Teste l'existence du fichier ou répertoire |
boolean delete() | Supprime le fichier ou répertoire |
boolean canWrite() / canRead() | Teste les droits d'accès |
File getParentFile() | Retourne le répertoire parent |
long lastModified() | Date de dernière modification |
import java.io.*;
public class Listeur {
public static void main(String[] args) {
litrep(new File(".")); // répertoire courant
}
public static void litrep(File rep) {
if (rep.isDirectory()) {
String[] t = rep.list();
for (String nom : t)
System.out.println(nom);
}
}
}
public static void litrep(File rep) {
if (rep.isDirectory()) {
String[] t = rep.list();
for (String nom : t) {
File r2 = new File(rep.getAbsolutePath() + "\\" + nom);
if (r2.isDirectory())
litrep(r2); // appel récursif
else
System.out.println(r2.getAbsolutePath());
}
}
}
Les E/S sont gérées de façon portable (selon les OS) grâce à la notion de flux (stream en anglais). Un flux est en quelque sorte un canal dans lequel l'information transite. L'ordre dans lequel l'information y est transmise est respecté.
Un flux peut être :
Certains flux de données peuvent être associés à des ressources : les fichiers, les tableaux de données en mémoire, les lignes de communication (connexion réseau), etc.
Tous les flux sont regroupés dans le paquetage java.io. Il existe de nombreuses classes représentant les flux — il n'est pas toujours aisé de s'y repérer. L'approche consiste à combiner différents types de flux pour réaliser la gestion souhaitée.
InputStream & OutputStream| Méthode | Description |
|---|---|
abstract int read() | Retourne l'octet lu ou -1 en fin de flux. Méthode de base à définir dans les sous-classes. |
int read(byte[] b) | Remplit un tableau d'octets et retourne le nombre d'octets lus. |
int read(byte[] b, int off, int len) | Remplit une portion de tableau à partir d'un offset sur une longueur donnée. |
int available() | Retourne le nombre d'octets prêts à être lus sans blocage. |
long skip(long n) | Ignore n octets du flux ; retourne le nombre effectivement ignorés. |
void close() | Ferme le flux et libère les ressources système associées. |
try-with-resources (Java 7+) pour garantir la fermeture automatique.
| Méthode | Description |
|---|---|
abstract void write(int b) | Écrit l'octet passé en paramètre. |
void write(byte[] b) | Écrit tous les octets du tableau. |
void write(byte[] b, int off, int len) | Écrit une portion du tableau à partir d'un offset. |
void flush() | Purge le tampon en cas d'écritures bufferisées. |
void close() | Ferme le flux après avoir vidé le tampon. |
DataInputStream — sous-classe de InputStream permettant de lire tous les types primitifs Java.DataOutputStream — sous-classe de OutputStream permettant d'écrire tous les types primitifs Java.ZipInputStream / ZipOutputStream — permettent de lire et d'écrire des flux au format de compression ZIP.En Java, chaque type de flux est destiné à réaliser une tâche précise. Lorsqu'on souhaite un flux au comportement plus complexe, on « empile », à la façon des poupées russes, plusieurs flux ayant des comportements plus élémentaires. On parle de flux filtrés.
Concrètement, il s'agit de passer, dans le constructeur d'un flux, un autre flux déjà existant pour combiner leurs caractéristiques.
// FileInputStream sait lire depuis un fichier mais uniquement octet par octet.
// DataInputStream sait lire des types Java de haut niveau mais pas depuis un fichier.
// On combine les deux :
FileInputStream fic = new FileInputStream("fichier.bin");
DataInputStream din = new DataInputStream(fic);
double d = din.readDouble();
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("monfichier")
)
);
ZipInputStream zin = new ZipInputStream(
new FileInputStream("monfichier.zip")
);
DataInputStream din = new DataInputStream(zin);
BufferedInputStream, BufferedOutputStream) peut améliorer considérablement les performances en réduisant le nombre d'appels système. Sans buffer, chaque appel à read() effectue un accès disque ; avec buffer, les données sont chargées par blocs.
RandomAccessFileLa classe RandomAccessFile permet de lire ou d'écrire dans un fichier à n'importe quel emplacement (par opposition aux fichiers à accès séquentiel). Elle implémente les interfaces DataInput et DataOutput, permettant de lire ou d'écrire tous les types Java de base, les lignes, les chaînes ASCII ou Unicode, etc.
| Mode | Description |
|---|---|
"r" | Lecture seule |
"rw" | Lecture et écriture |
Ces fichiers possèdent un pointeur de fichier qui indique constamment la donnée suivante à lire ou écrire :
long getFilePointer() — retourne la position courante du pointeur.void seek(long off) — déplace le pointeur à la position off.RandomAccessFile raf = new RandomAccessFile("data.bin", "rw");
raf.seek(100); // positionner à l'octet 100
double val = raf.readDouble(); // lire un double à cette position
raf.close();
Reader & WriterLes flux de caractères sont des sous-classes de Reader et Writer. Ils utilisent le codage de caractères Unicode et sont préférables dès qu'on manipule du texte, afin d'éviter les problèmes d'encodage.
import java.io.*;
try (BufferedReader br = new BufferedReader(
new FileReader("monfichier.txt"))) {
String ligne;
while ((ligne = br.readLine()) != null) {
System.out.println(ligne);
}
} catch (IOException e) {
System.err.println("Erreur : " + e.getMessage());
}
try (BufferedWriter bw = new BufferedWriter(
new FileWriter("sortie.txt"))) {
bw.write("Ceci est mon fichier");
bw.newLine();
bw.write("Il est à moi...");
// Le bloc try-with-resources ferme automatiquement le flux
}
// Lire un fichier encodé en ISO-2022-CN (chinois)
InputStreamReader in = new InputStreamReader(
new FileInputStream("chinois.txt"), "ISO2022CN"
);
Pour écrire des chaînes et des nombres sous forme de texte, on utilise la classe PrintWriter qui possède les méthodes print(...) et println(...). Pour lire des nombres sous forme de texte, il n'existe pas de solution toute faite : il faut passer par des chaînes de caractères et les convertir ensuite.
try-with-resources, la fermeture est garantie même en cas d'exception.
Scanner| Flux | Type | Description |
|---|---|---|
System.in | InputStream | Entrée standard (clavier) |
System.out | PrintStream | Sortie standard (console) |
System.err | PrintStream | Sortie d'erreurs standard |
La classe InputStream ne propose que des méthodes élémentaires. Pour une utilisation confortable du clavier, préférez BufferedReader couplé à InputStreamReader :
Reader reader = new InputStreamReader(System.in);
BufferedReader keyboard = new BufferedReader(reader);
System.out.print("Entrez une ligne de texte : ");
String line = keyboard.readLine();
System.out.println("Vous avez saisi : " + line);
La classe Scanner est une classe injustement méconnue du JDK qui offre des fonctionnalités très intéressantes pour parser des chaînes de caractères, en extraire et convertir les composants. Un Scanner peut se brancher sur à peu près n'importe quelle source : InputStream, Reader, File, ou encore une simple String.
// Lire un entier depuis le clavier
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
Les méthodes hasNext...() / next...() découpent la chaîne en tokens selon un délimiteur (espace blanc par défaut, configurable via useDelimiter(expression)). Elles fonctionnent sur le même principe qu'un Iterator.
String s = "Dalton;Joe;1.4\nDalton;Jack;1.6\nDalton;William;1.8\nDalton;Averell;2.0";
Scanner scan = new Scanner(s);
scan.useDelimiter(";|\n");
scan.useLocale(Locale.US); // pour les floats
while (scan.hasNextLine()) {
System.out.printf("%2$s %1$s : %3$.1f m%n",
scan.next(), scan.next(), scan.nextFloat());
}
// Joe Dalton : 1.4 m
// Jack Dalton : 1.6 m
// William Dalton : 1.8 m
// Averell Dalton : 2.0 m
java.nio.file (Java 7+)Introduit avec Java 7, java.nio.file constitue l'API moderne de Java pour la gestion des fichiers, des répertoires et des systèmes de fichiers. Il remplace progressivement les classes historiques de java.io en proposant une approche plus robuste, plus lisible et mieux adaptée aux systèmes actuels.
Ce package est centré autour de trois notions fondamentales :
Path)Files)FileSystem)Path représente le chemin vers un fichier ou un répertoire. Elle remplace avantageusement java.io.File : elle est indépendante du système d'exploitation et peut être relative ou absolue.
Path p1 = Path.of("data", "fichier.txt"); // Java 11+
Path p2 = Paths.get("/home/user/data"); // Java 7+
p1.getFileName(); // → fichier.txt
p1.getParent(); // → data
p1.getRoot(); // → null (chemin relatif)
p2.normalize(); // simplifie les . et ..
Files est une classe utilitaire fournissant des méthodes statiques pour manipuler les fichiers : création, suppression, copie, déplacement, lecture, écriture, accès aux attributs et parcours de répertoires.
// Lecture complète du fichier en une String
String contenu = Files.readString(Path.of("fichier.txt"));
// Lecture ligne par ligne
List<String> lignes = Files.readAllLines(Path.of("fichier.txt"));
// Lecture en streaming (économique en mémoire)
Files.lines(Path.of("fichier.txt")).forEach(System.out::println);
// Écriture simple (écrase le fichier)
Files.writeString(Path.of("test.txt"), "Bonjour\n");
// Écriture en mode ajout (append)
Files.writeString(Path.of("test.txt"), "Suite\n",
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
// Création
Files.createFile(Path.of("nouveau.txt"));
Files.createDirectories(Path.of("data/logs"));
// Copie et déplacement
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
Files.move(src, dest);
// Suppression
Files.delete(path);
Files.deleteIfExists(path);
// Informations
Files.size(path);
Files.getLastModifiedTime(path);
Files.isDirectory(path);
Files.isRegularFile(path);
// Contenu immédiat d'un répertoire
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Path.of("data"))) {
for (Path p : stream)
System.out.println(p);
}
// Parcours récursif (Java 8+, style fonctionnel)
Files.walk(Path.of("data"))
.filter(Files::isRegularFile)
.forEach(System.out::println);
La sérialisation consiste à prendre un objet en mémoire et à en sauvegarder l'état sur un flux de données (vers un fichier, par exemple). Ce concept permet aussi de reconstruire, ultérieurement, l'objet en mémoire à l'identique de ce qu'il était initialement. La sérialisation peut donc être considérée comme une forme de persistance des données.
java.io.Serializable (interface marqueur, sans méthode).transient pour être exclues).| Classe | Rôle | Méthode clé |
|---|---|---|
ObjectOutputStream | Sérialisation (écriture) | writeObject(obj) |
ObjectInputStream | Désérialisation (lecture) | readObject() |
// ── Sauvegarde ──────────────────────────────────────────────
void sauvegarde(String chemin) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(chemin))) {
oos.writeObject(this);
} catch (Exception e) {
System.err.println("Erreur : " + e);
}
}
// ── Relecture ───────────────────────────────────────────────
static Object relecture(String chemin) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(chemin))) {
return ois.readObject();
} catch (Exception e) {
System.err.println("Erreur : " + e);
return null;
}
}
| Besoin | Classe(s) recommandée(s) | Package |
|---|---|---|
| Lire/écrire des octets depuis/vers un fichier | FileInputStream / FileOutputStream | java.io |
| Lire/écrire des types Java (int, double…) | DataInputStream / DataOutputStream | java.io |
| Lire un fichier texte ligne par ligne | BufferedReader + FileReader | java.io |
| Écrire du texte dans un fichier | BufferedWriter + FileWriter | java.io |
| Lire depuis la console | Scanner ou BufferedReader | java.util / java.io |
| Gestion moderne des fichiers (Java 7+) | Files + Path | java.nio.file |
| Accès direct à n'importe quelle position | RandomAccessFile | java.io |
| Persister un objet Java | ObjectOutputStream / ObjectInputStream | java.io |