W3docs

Créer des fichiers en Java

Créez des fichiers et répertoires en Java avec File.createNewFile, Files.createFile et Files.createDirectories.

« Créer un nouveau fichier vide » et « construire cette arborescence de répertoires » sont deux des opérations les plus simples que le système de fichiers propose, et pourtant Java expose quatre façons qui se chevauchent pour chacune. Les différences sont importantes — elles distinguent « échoue si le fichier existe » de « écrase silencieusement », mkdir de mkdir -p, une méthode historique retournant un boolean d'une méthode moderne levant des exceptions.

Ce chapitre couvre les quatre créateurs :

  • File.createNewFile() — création de fichier héritée.
  • File.mkdir() / File.mkdirs() — création de répertoire héritée.
  • Files.createFile(path) — « créer ou échouer » atomique moderne.
  • Files.createDirectory(path) / Files.createDirectories(path) — création de répertoire moderne.

Ainsi que les utilitaires de fichiers temporaires (Files.createTempFile, Files.createTempDirectory) et les indicateurs OpenOption qui permettent aux writers de créer des fichiers implicitement.

File.createNewFile() — hérité, retourne un boolean

File f = new File("data/users.txt");
boolean created = f.createNewFile();      // true if it actually created the file
                                          // false if it already existed
                                          // throws IOException if the parent dir is missing

Le contrat est atomique vérification-et-création : le système d'exploitation garantit qu'aucun autre processus ne peut créer le même chemin entre la vérification d'existence et la création. Cela fait de createNewFile un verrou primitif dans certains idiomes historiques de « fichier PID » — if (!f.createNewFile()) throw new IllegalStateException("already running");

Ce qu'il ne fait pas :

  • Il ne crée pas les répertoires parents manquants. new File("does/not/exist/file.txt").createNewFile() lève IOException.
  • Il retourne false (et ne lève pas d'exception) si le fichier existe déjà.

Si vous avez seulement besoin que le fichier existe à la fin de l'appel, la valeur de retour false convient. Si vous avez besoin que le fichier soit tout nouveau (sémantique de verrou), false est votre signal pour prendre un chemin différent.

File.mkdir() et File.mkdirs()

new File("logs").mkdir();           // creates "logs" — fails if "." has no perms or parent missing
new File("a/b/c").mkdirs();          // creates "a", "a/b", and "a/b/c" — like `mkdir -p`

Les deux retournent un boolean, les deux perdent l'information sur pourquoi un échec s'est produit. mkdir échoue si un parent est manquant ; mkdirs non. Les deux réussissent (retournent true) uniquement si le répertoire a été nouvellement créé — s'il existe déjà, ils retournent false. Combiné au problème du « manque d'informations sur les erreurs », c'est le genre d'API héritée que l'on enveloppe dans un utilitaire :

File dir = new File("data");
if (!dir.exists() && !dir.mkdirs()) throw new IOException("cannot create " + dir);

Le Files.createDirectories(path) moderne est le remplacement en une seule ligne.

Files.createFile(path) — moderne, lève des exceptions

Files.createFile est l'équivalent java.nio.file de File.createNewFile() avec une différence importante : il lève une exception au lieu de retourner un boolean.

Path p = Path.of("data/users.txt");
Files.createFile(p);                          // creates an empty regular file
                                              // throws FileAlreadyExistsException if it exists
                                              // throws NoSuchFileException if the parent is missing

FileAlreadyExistsException est ce que vous catch si le résultat d'existence est important ; NoSuchFileException est ce que vous catch (ou évitez avec createDirectories) si le parent pourrait ne pas être là. Les types d'exception sont des sous-classes spécifiques d'IOException, donc un catch (IOException e) générique fonctionne toujours.

Vous pouvez passer des arguments FileAttribute pour définir les permissions POSIX au moment de la création sur Unix — l'utilisation la plus courante est de s'assurer que les fichiers secrets (clés privées, jetons) sont créés avec 0600 :

import static java.nio.file.attribute.PosixFilePermissions.*;
var attr = asFileAttribute(fromString("rw-------"));
Files.createFile(Path.of("/tmp/secret"), attr);   // born with 0600 permissions, atomically

(Cet appel lève UnsupportedOperationException sur Windows, qui n'a pas de modèle de permissions POSIX — protégez avec une vérification de plateforme si vous ciblez les deux.)

Files.createDirectory versus Files.createDirectories

La même différence que mkdir versus mkdir -p, mais basée sur les exceptions :

Files.createDirectory(Path.of("logs"));          // one level deep; parent must exist
Files.createDirectories(Path.of("a/b/c"));        // creates every missing ancestor

createDirectory lève FileAlreadyExistsException si la cible existe déjà et n'est pas un répertoire ; si c'est déjà un répertoire, il lève également une exception (ce qui n'est généralement pas ce que vous souhaitez).

createDirectories est le choix le plus souple : il ne fait rien si tous les répertoires sont déjà présents, et crée ce qui manque sinon. Il ne lève pas d'exception si le chemin existe déjà en tant que répertoire. Cela le rend idempotent — sûr à appeler au démarrage sans vérification exists().

Fichiers et répertoires temporaires

Pour les tests, les espaces de travail et les situations « j'ai besoin d'un endroit sûr pour ça pendant quelques minutes », le JDK fournit Files.createTempFile et Files.createTempDirectory :

Path scratch = Files.createTempFile("session-", ".log");          // /tmp/session-3829387.log
Path workdir = Files.createTempDirectory("export-");               // /tmp/export-1827392

Les deux choisissent un nom unique dans le répertoire temporaire du système, les deux retournent un Path vers la nouvelle entrée, et les deux créent l'entrée avec des permissions restrictives sur Unix. Le préfixe et le suffixe sont des indices auxquels le JDK ajoute une valeur unique — vous ne choisissez pas le nom exact (c'est justement le but : un autre appelant ne peut pas le prédire et écraser le vôtre).

Les fichiers temporaires ne sont pas supprimés automatiquement. Vous pouvez soit :

  • Appeler Files.deleteIfExists(path) quand vous avez terminé ; ou
  • Appeler path.toFile().deleteOnExit() pour planifier une suppression à l'arrêt de la JVM (annulé par les arrêts forcés) ; ou
  • Ouvrir le fichier avec StandardOpenOption.DELETE_ON_CLOSE si vous en avez seulement besoin pendant qu'un flux est ouvert.

Les writers créent les fichiers implicitement

La plupart du temps, vous n'avez pas besoin d'un appel « créer le fichier » du tout — un writer en crée un pour vous. Files.newBufferedWriter, Files.write et Files.writeString acceptent tous des varargs OpenOption... qui déterminent ce qui se passe selon que le fichier existe ou non :

import static java.nio.file.StandardOpenOption.*;
Files.writeString(path, "hello\n", CREATE, WRITE, TRUNCATE_EXISTING);
Files.writeString(path, "more\n",  CREATE, WRITE, APPEND);
Files.writeString(path, "new\n",   CREATE_NEW);     // fails if file exists
  • CREATE — crée si absent, sinon ouvre l'existant.
  • CREATE_NEW — crée, lève FileAlreadyExistsException s'il existe. Même sémantique que Files.createFile.
  • TRUNCATE_EXISTING — vide le contenu du fichier à l'ouverture (comportement par défaut de writeString sans append).
  • APPEND — écrit à la fin sans tronquer.

Le comportement par défaut de Files.writeString (sans options) est CREATE, WRITE, TRUNCATE_EXISTING — c'est-à-dire « créer ou écraser ». Files.newBufferedWriter fonctionne de la même façon par défaut. Si vous souhaitez une sémantique d'ajout, vous devez l'indiquer explicitement.

Un exemple pratique : chaque créateur côte à côte

Le programme ci-dessous construit une petite arborescence depuis zéro sous le répertoire temporaire du système, en utilisant les deux API et plusieurs options d'ouverture. Chaque étape affiche ce qui a changé ; le dernier bloc montre ce qui se passe quand on relance une opération sur un chemin qui existe déjà.

java— editable, runs on the server

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

  • Le premier legacy.createNewFile() a retourné true (créé) ; le second a retourné false (existait déjà). Le boolean ne dit pas ce qui s'est passé — vous devez vous souvenir de la convention.
  • deep.mkdirs() a réussi une fois et a retourné false la deuxième fois. Ce false est identique à « permission refusée » ou « parent manquant » — c'est exactement le problème du manque d'informations sur les erreurs que Files résout.
  • Files.createFile sur un chemin existant a levé FileAlreadyExistsException. Le type d'exception est spécifique, donc un vrai gestionnaire peut distinguer « déjà présent » de « permission refusée » sans analyser des chaînes de caractères.
  • Files.createDirectories appelé deux fois de suite n'a rien fait de nuisible la deuxième fois. C'est la propriété qui en fait le bon choix dans le code de démarrage : pas de garde, il suffit de l'appeler.
  • Files.writeString(log, "line 1\n") a créé le fichier parce que CREATE fait partie des options par défaut. Les deuxième et troisième appels ont utilisé APPEND explicitement, et le fichier a accumulé trois lignes. Le quatrième appel a utilisé CREATE_NEW et a refusé d'écraser. Les valeurs par défaut sont conçues pour le cas « écraser avec un nouveau contenu » ; vous optez explicitement pour l'ajout.
  • Files.createTempFile(root, "scratch-", ".tmp") a produit un nom comme scratch-1827392.tmp — votre préfixe et suffixe, plus un fragment unique que la JVM choisit pour que deux appels simultanés ne se télescopent jamais.
  • Le nettoyage parcourt root en ordre inverse pour que les fichiers et répertoires enfants disparaissent avant leurs parents. Files.delete refuse de supprimer un répertoire non vide ; cet ordonnancement est la façon dont on construit un rm -rf manuel.

Et ensuite

Vous savez créer des fichiers ; le chapitre suivant, Lire des fichiers en Java, les lit — d'abord avec les raccourcis modernes (Files.readString, Files.readAllLines, Files.lines), puis avec la pile classique FileReader / BufferedReader / Scanner pour que le code plus ancien des chapitres suivants ait une base solide.

Exercices pratiques

Pratique
Vous voulez une ligne unique qui crée un répertoire ainsi que tous les répertoires parents manquants, et ne fait **rien** si le répertoire existe déjà. Quel appel utiliser ?
Vous voulez une ligne unique qui crée un répertoire ainsi que tous les répertoires parents manquants, et ne fait **rien** si le répertoire existe déjà. Quel appel utiliser ?
Was this page helpful?