W3docs

La classe File en Java

Représentez des chemins de fichiers en Java avec java.io.File — exists, isFile, isDirectory, listFiles.

java.io.File est le type original "cette chaîne est un chemin" introduit dans Java 1.0. La classe elle-même n'effectue aucune E/S — elle n'ouvre, ne lit et n'écrit aucune donnée — elle nomme simplement un emplacement dans le système de fichiers et offre quelques méthodes pour interroger le système d'exploitation sur cet emplacement et pour y effectuer des opérations ponctuelles (exists, isDirectory, delete, renameTo, listFiles).

java.nio.file.Path (Java 7) est le remplaçant moderne et c'est ce que le nouveau code devrait utiliser, mais vous rencontrerez File dans chaque base de code antérieure à ~2012, et de nombreuses API plus anciennes continuent de l'accepter et de le retourner. Ce chapitre couvre ce qu'il fait, où se situent les limites, et comment il s'interface avec Path.

Construction

Un File encapsule une chaîne de chemin. Quatre constructeurs couvrent les cas courants :

File a = new File("data/users.txt");                 // relative to the JVM's working directory
File b = new File("/var/log/app.log");                // absolute
File c = new File("/tmp", "session.txt");             // parent + child
File d = new File(new File("/tmp"), "session.txt");  // parent File + child

Le constructeur n'effectue aucune validation — passer un chemin absurde construit un File sans problème ; ce n'est que lorsque vous appelez exists(), delete(), etc. que le système d'exploitation intervient.

Utilisez le constructeur à deux arguments pour "parent + nom" plutôt que la concaténation de chaînes. Il choisit le bon séparateur (/ sous Unix, \ sous Windows) et évite le bug où le chemin parent peut ou non se terminer par un séparateur :

File good = new File(parentDir, "data.txt");         // separator handled for you
File bad  = new File(parentDir + "/data.txt");        // brittle: depends on parentDir's exact string

Interrogation du système de fichiers

File expose un large ensemble de requêtes retournant des valeurs boolean et long. Les plus courantes :

File f = new File("data/users.txt");
f.exists();              // does the path point to anything?
f.isFile();              // is it a regular file?
f.isDirectory();         // is it a directory?
f.isHidden();            // hidden by OS convention (leading dot on Unix, hidden attr on Windows)
f.length();              // size in bytes (0 for a directory)
f.lastModified();        // epoch millis; 0 if it doesn't exist or can't be queried
f.canRead();             // permission check from the JVM's point of view
f.canWrite();
f.canExecute();

Chacun de ces appels sollicite le système d'exploitation. Ils sont peu coûteux individuellement mais pas gratuits — appeler exists() puis isDirectory() puis length() représente trois appels système. Si vous avez besoin de plusieurs attributs d'un même fichier, Files.readAttributes(path, BasicFileAttributes.class) (partie suivante) n'effectue qu'un seul appel système.

Vues du chemin

File vous offre plusieurs façons d'examiner la même chaîne sous-jacente :

File f = new File("data/../data/users.txt");
f.getName();              // "users.txt"        — last component
f.getParent();            // "data/../data"     — String, or null at the root
f.getParentFile();        // File for the parent, or null
f.getPath();              // "data/../data/users.txt" — what you constructed
f.getAbsolutePath();      // resolved against working dir, NOT canonicalised
f.getCanonicalPath();     // resolved, normalised, symlinks followed — can throw IOException

getAbsolutePath et getCanonicalPath sont la paire la plus confusante de la classe :

  • getAbsolutePath — préfixe le répertoire de travail courant de la JVM si le chemin est relatif. Retourne la chaîne avec les segments .. encore présents.
  • getCanonicalPath — identique au chemin absolu, puis résout .. et ., puis suit les liens symboliques. Peut accéder au disque et lever une IOException.

Pour les vérifications sensibles à la sécurité (ce chemin fourni par l'utilisateur est-il dans le répertoire autorisé ?), getCanonicalPath est la seule sûre — sinon un chemin relatif comme safe-dir/../../../etc/passwd passe au travers d'une vérification startsWith("safe-dir").

Listage d'un répertoire

Quatre variantes, deux paires :

File dir = new File("/tmp");

String[]  names    = dir.list();                              // child names, no metadata
File[]    children = dir.listFiles();                          // child File objects

String[]  txt      = dir.list((d, name) -> name.endsWith(".txt"));         // FilenameFilter
File[]    files    = dir.listFiles(File::isFile);                            // FileFilter

FilenameFilter et FileFilter sont des interfaces fonctionnelles à une seule méthode (vocabulaire de la partie 12), donc une lambda ou une référence de méthode fonctionne directement. La différence : FilenameFilter reçoit le répertoire parent et le nom nu ; FileFilter reçoit le File enfant construit. Utilisez FileFilter si vous avez besoin d'appeler isDirectory() ou length() pour décider ; utilisez FilenameFilter si le filtrage par nom suffit.

Ces quatre méthodes retournent null si le chemin n'est pas un répertoire — elles ne lèvent pas d'exception. C'est une source classique de NullPointerException :

for (File child : dir.listFiles()) { ... }                   // NPE if dir is not a directory!
File[] children = dir.listFiles();
if (children != null) for (File c : children) { ... }         // correct

La méthode moderne Files.list(path) retourne un Stream<Path> vide pour un répertoire manquant ou lève une NotDirectoryException claire. L'API File retourne simplement null et vous laisse planter.

Création, suppression, renommage

File expose quelques méthodes de mutation :

f.createNewFile();        // creates an empty file; returns boolean; throws IOException on real failure
f.mkdir();                // creates this directory; parent must exist
f.mkdirs();               // creates this directory and any missing parents
f.delete();               // deletes this file or empty directory; returns boolean
f.renameTo(other);        // OS-specific behaviour; returns boolean

Le thème récurrent — les valeurs de retour boolean qui n'expliquent pas pourquoi — est la principale raison d'être de Files. f.delete() retourne false si le fichier n'existait pas, si vous n'aviez pas la permission, s'il s'agissait d'un répertoire non vide, ou si un autre processus le maintenait ouvert sous Windows. Il est impossible de déterminer lequel à partir de la valeur de retour. La méthode correspondante Files.delete(path) lève une exception spécifique (NoSuchFileException, AccessDeniedException, DirectoryNotEmptyException) et constitue l'API appropriée pour une vraie gestion des erreurs.

renameTo est le pire offenseur : il peut échouer sans lever la moindre exception, et les modes d'échec (renommage entre volumes, cible existante, permission, verrou) dépendent du système d'exploitation. Files.move(src, dst, REPLACE_EXISTING) est le remplaçant moderne et vous informe de ce qui a mal tourné.

Interface avec Path

Chaque File connaît son Path et vice versa :

File f = new File("data/users.txt");
Path p = f.toPath();              // bridge to java.nio.file
File g = p.toFile();              // bridge back

Les deux interopèrent à faible coût. Quand vous êtes bloqué par une API héritée qui retourne un File, la bonne approche est généralement f.toPath() puis d'appeler Files.* dessus. Le nouveau code devrait partir de Path.of(...) et ne convertir en File qu'au point d'appel d'une méthode héritée.

Exemple concret : construction d'une arborescence et parcours avec File

Le programme ci-dessous crée une petite arborescence de répertoires dans le répertoire temporaire du système, la peuple de quelques fichiers, puis interroge chaque entrée avec File. Il démontre les lambdas FilenameFilter et FileFilter, le piège du retour null, la résolution du chemin canonique, et le problème du manque d'informations sur les erreurs avec delete(). Chaque artefact est nettoyé avec deleteOnExit.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • a.getCanonicalPath() affiche un chemin absolu normalisé sans segment ... getAbsolutePath() ne normalise pas — pour une vérification de sécurité, c'est la version canonique que vous comparez à un préfixe autorisé.
  • La forme FilenameFilter (d, name) -> name.endsWith(".txt") est une lambda à deux arguments ; File::isFile est une référence de méthode pour le FileFilter à un argument. Les deux sont des interfaces fonctionnelles, le même vocabulaire que la partie 12 — File était "prêt pour les lambdas" bien avant que les lambdas n'existent.
  • notDir.listFiles() a retourné null parce que data.csv n'est pas un répertoire. La boucle for aurait levé une NPE si nous avions omis la vérification du null. Files.list(path) lève une exception claire dans le même cas.
  • ghost.delete(), a.delete() et sub.delete() ont tous retourné un boolean. Les deux premiers sont faciles à interpréter ; le troisième a retourné false parce que le répertoire n'était pas vide, et l'API ne nous a donné aucun moyen de distinguer "n'était pas vide" de "pas de permission". C'est le manque que Files.delete(path) comble.
  • root.toPath() est le pont vers java.nio.file. Une fois que vous avez un Path, le reste de la partie 13 s'applique — Files.readString, Files.lines, Files.walk, tous les assistants static.

La suite

Le prochain chapitre, Création de fichiers en Java, couvre les trois façons de créer un nouveau fichier ou répertoire — l'héritage File.createNewFile et mkdir(s), ainsi que les modernes Files.createFile, Files.createDirectory et Files.createDirectories — et lequel choisir pour quelle tâche.

Pratique

Pratique
`dir.listFiles()` sur un `File` pointant vers un fichier ordinaire (et non un répertoire) retourne…
`dir.listFiles()` sur un `File` pointant vers un fichier ordinaire (et non un répertoire) retourne…
Was this page helpful?