1. Comment Java a appris à manipuler des fichiers
Java et les fichiers : par où tout a commencé
Il était une fois, en 1996, les concepteurs du langage ont proposé une première manière de travailler avec les fichiers — le paquet java.io. On y a vu apparaître des classes telles que File, FileInputStream, FileOutputStream, Reader, Writer et d’autres. Ces classes permettaient d’effectuer diverses opérations sur les fichiers : savoir si un fichier existe, obtenir sa taille, sa date de modification, et travailler avec des répertoires.
Mais, comme souvent avec une première version, tout était loin d’être parfait — beaucoup de choses auraient pu être plus pratiques et plus sûres.
Limites de l’ancienne API (java.io)
- File — ce n’est pas un fichier, mais un « raccourci ». La classe File ne sait pas lire ni écrire le contenu. Elle décrit le chemin et les métadonnées. Pour lire/écrire, il faut des flux séparés : FileInputStream/FileOutputStream ou Reader/Writer.
- La gestion des chemins — une douleur. Assembler les chemins à la main comme "C:\\Users\\" + user + "\\Documents" se cassait souvent lors d’un portage entre Windows et Linux.
- Pas de prise en charge des liens symboliques. L’ancienne API ne comprenait pas les symlinks et pouvait mal fonctionner avec eux.
- Prise en charge limitée des attributs. Difficile d’obtenir les permissions, le propriétaire, les indicateurs « caché », etc.
- Peu efficace pour les gros fichiers. Les flux classiques n’offraient pas de scénarios pratiques et rapides pour de grands volumes de données, et l’E/S asynchrone était absente.
Apparition de java.nio.file (NIO.2, Java 7)
Avec Java 7 est arrivé un nouveau paquet : java.nio.file (souvent — NIO.2). Il a apporté :
- Path — une abstraction moderne du chemin (y compris les chemins à l’intérieur des archives ZIP, des systèmes de fichiers cloud et réseau).
- Files — des utilitaires statiques pour lire, écrire, copier, supprimer.
- FileSystem — prise en charge non seulement du disque local, mais aussi d’autres systèmes de fichiers.
- Prise en charge des liens symboliques, des attributs étendus, des permissions.
- E/S asynchrones et meilleures performances pour de gros volumes de données.
- Manipulation pratique des répertoires et des API de flux.
NIO signifie « New Input/Output », et bien qu’il ait plus de dix ans, cela reste le standard moderne en Java.
2. Comparaison des approches : java.io vs java.nio.file
Classe File (java.io)
- Représente le chemin d’un fichier ou d’un répertoire et leurs métadonnées.
- Ne sait pas lire/écrire le contenu du fichier.
- Permet d’obtenir l’existence, la taille, le type (fichier/répertoire), le chemin absolu, etc.
import java.io.File;
public class ExampleIO {
public static void main(String[] args) {
File file = new File("example.txt");
if (file.exists()) {
System.out.println("Le fichier existe !");
System.out.println("Taille : " + file.length() + " octets");
}
}
}
Classe Path (java.nio.file)
- Abstraction moderne du chemin, se combine et se normalise naturellement.
- Peut représenter des chemins locaux, d’archives et réseau.
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ExampleNIOPath {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
if (Files.exists(path)) {
System.out.println("Fichier trouvé !");
System.out.println("Taille : " + Files.size(path) + " octets");
}
}
}
Classe Files (java.nio.file)
- Ensemble de méthodes statiques pour lire/écrire en entier et par parties.
- Copie, suppression, déplacement de fichiers.
- Création/suppression de répertoires.
- Accès aux attributs étendus.
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class ExampleNIOFiles {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
}
}
Tableau comparatif
| Fonction | |
|
|---|---|---|
| Représentation du chemin | Oui | Oui |
| Lecture/écriture du contenu | Non | Oui (via Files) |
| Manipulation des chemins | Peu pratique | Pratique (resolve, normalize) |
| Liens symboliques | Non | Oui |
| Attributs de fichiers | Limité | Étendu |
| E/S asynchrones | Non | Oui (NIO.2) |
| Multiplateforme | Partielle | Excellente |
3. Quand utiliser quoi ?
Ancienne API (java.io) : quand est-elle utile ?
- Pour la prise en charge du code legacy. Si le projet a démarré avant Java 7 et utilise partout File, FileInputStream, FileOutputStream, il peut être nécessaire de maintenir la compatibilité.
- Dans les nouveaux projets — déconseillé. Utiliser le « vétéran » File aujourd’hui — c’est comme regarder des vidéos sur des cassettes VHS.
Nouvelle API (java.nio.file) : le standard actuel
- Préférez toujours Path et Files dans les nouveaux projets.
- Plus simple, plus sûr, plus puissant et mieux intégré aux streams, aux lambdas et à try-with-resources.
Aide-mémoire rapide
- Lecture, écriture, copie, suppression ? — via Files.
- Travail sur les chemins (assemblage, normalisation) ? — via Path.
- Taille, date, permissions ? — Files.getAttribute() et méthodes associées.
- Parcours de répertoires, récursif ? — Files.walk, Files.list.
4. Exemples : à quoi ressemblerait le code avec l’ancienne et la nouvelle API
Exemple 1: Vérification de l’existence d’un fichier
Ancienne méthode :
import java.io.File;
File file = new File("data.txt");
if (file.exists()) {
System.out.println("Fichier trouvé !");
}
Nouvelle méthode :
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("data.txt");
if (Files.exists(path)) {
System.out.println("Fichier trouvé !");
}
Exemple 2: Lecture de toutes les lignes d’un fichier
Ancienne méthode :
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
File file = new File("data.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
Nouvelle méthode :
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
Path path = Paths.get("data.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
Commentaire : La nouvelle méthode est plus courte et plus sûre ; avec try-with-resources, la gestion des ressources est encore plus simple.
Exemple 3: Écriture d’une chaîne dans un fichier
Ancienne méthode :
import java.io.FileWriter;
FileWriter writer = new FileWriter("output.txt");
writer.write("Bonjour, fichier !");
writer.close();
Nouvelle méthode :
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("output.txt");
Files.write(path, "Bonjour, fichier !".getBytes());
5. Subtilités utiles
Pourquoi Java a-t-elle décidé de tout refondre ?
- Sécurité et fiabilité. Moins d’erreurs liées à la fermeture des ressources et aux chemins.
- Multiplateforme. Les mêmes classes pour Windows, Linux et même les archives ZIP.
- Facilité d’extension. Plus simple d’ajouter la prise en charge des systèmes de fichiers cloud et réseau.
- Performances. Meilleur comportement sur les gros fichiers et opérations asynchrones.
- Compatibilité avec la pile Java moderne. Lambdas, streams, try-with-resources.
Comment passer de l’ancienne API à la nouvelle ?
Il est presque toujours possible de convertir File en Path et inversement :
File file = new File("data.txt");
Path path = file.toPath();
File file2 = path.toFile();
Que faire si vous tombez sur du vieux code?
- Pas de panique : on peut tout faire plus simplement et de manière plus moderne.
- Si possible — réécrivez avec java.nio.file.
- Si ce n’est pas possible — utilisez les ponts (toPath(), toFile()) et migrez progressivement.
6. Exemple final: mini-application
Exemple : Vérifions si un fichier existe et affichons sa taille.
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FileInfo {
public static void main(String[] args) {
Path path = Paths.get("test.txt");
if (Files.exists(path)) {
try {
long size = Files.size(path);
System.out.println("Fichier trouvé. Taille : " + size + " octets");
} catch (Exception e) {
System.out.println("Erreur lors de l’obtention de la taille du fichier : " + e.getMessage());
}
} else {
System.out.println("Fichier introuvable !");
}
}
}
Ce que nous avons utilisé ici :
- Path — représentation du chemin.
- Files.exists() — vérification de l’existence.
- Files.size() — obtention de la taille.
7. Erreurs courantes lors de l’utilisation des API de fichiers
Erreur n° 1: Penser que File peut lire ou écrire le contenu. En réalité File — ce n’est qu’un « raccourci ». Pour lire/écrire utilisez FileInputStream/FileOutputStream (ancienne API) ou les utilitaires Files (nouvelle API).
Erreur n° 2: Concaténer manuellement les chemins avec + ou un slash. Cela conduit à des erreurs selon les OS. Utilisez Path.resolve() ou Paths.get(...) avec plusieurs arguments.
Erreur n° 3: Oublier de fermer un flux. Dans l’ancienne API, c’est une cause fréquente de fuites de ressources. Dans la nouvelle API, de nombreuses opérations via Files ne créent pas de flux, et là où ils sont nécessaires — appliquez try-with-resources.
Erreur n° 4: Utiliser l’ancienne API dans de nouveaux projets. Si vous démarrez un nouveau projet — choisissez java.nio.file. N’avoir recours à java.io a du sens seulement pour la compatibilité avec du code legacy.
GO TO FULL VERSION