W3docs

Creare file in Java

Crea file e directory in Java con File.createNewFile, Files.createFile e Files.createDirectories.

"Crea un nuovo file vuoto" e "crea questa struttura di directory" sono due delle operazioni più elementari che il file system offre, eppure Java espone quattro modi sovrapposti per eseguire ciascuna. Le differenze contano — la differenza tra "fallisce se il file esiste" e "sovrascrive silenziosamente," tra "mkdir" e "mkdir -p," tra un metodo legacy che restituisce un boolean e uno moderno che lancia un'eccezione.

Questo capitolo tratta i quattro metodi di creazione:

  • File.createNewFile() — creazione di file legacy.
  • File.mkdir() / File.mkdirs() — creazione di directory legacy.
  • Files.createFile(path) — "crea o fallisce" atomico moderno.
  • Files.createDirectory(path) / Files.createDirectories(path) — creazione di directory moderna.

Oltre agli helper per file temporanei (Files.createTempFile, Files.createTempDirectory) e ai flag OpenOption che permettono agli scrittori di creare file implicitamente.

File.createNewFile() — legacy, restituisce 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

Il contratto è check-and-create atomico: il sistema operativo garantisce che nessun altro processo possa creare lo stesso percorso tra il controllo dell'esistenza e la creazione. Questo rende createNewFile un lock primitivo in alcuni idiomi "PID file" legacy — if (!f.createNewFile()) throw new IllegalStateException("already running");

Cosa non fa:

  • Non crea le directory padre mancanti. new File("does/not/exist/file.txt").createNewFile() lancia IOException.
  • Restituisce false (non lancia eccezione) quando il file esiste già.

Se hai solo bisogno che il file esista alla fine della chiamata, il valore restituito false va bene. Se hai bisogno che il file sia nuovo di zecca (semantica di lock), false è il segnale per intraprendere un percorso diverso.

File.mkdir() e 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`

Entrambi restituiscono boolean, entrambi perdono informazioni sul perché si è verificato un fallimento. mkdir fallisce se manca un padre; mkdirs no. Entrambi hanno successo (restituiscono true) solo se la directory è stata appena creata — se esiste già, restituiscono false. Combinato con il problema "nessuna informazione sull'errore", questo è il tipo di API legacy che viene avvolto in un helper:

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

Il moderno Files.createDirectories(path) è la sostituzione in una riga sola.

Files.createFile(path) — moderno, lancia eccezioni

Files.createFile è l'omologo java.nio.file di File.createNewFile() con una differenza importante: lancia un'eccezione invece di restituire 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 è quella che si cattura con catch se il risultato sull'esistenza è importante; NoSuchFileException è quella che si cattura (o si previene con createDirectories) se il padre potrebbe non essere presente. I tipi di eccezione sono sottoclassi specifiche di IOException, quindi un generico catch (IOException e) funziona comunque.

Puoi passare argomenti FileAttribute per impostare i permessi POSIX al momento della creazione su Unix — l'uso più comune è assicurarsi che i file segreti (chiavi private, token) vengano creati con 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

(Quella chiamata lancia UnsupportedOperationException su Windows, che non ha un modello di permessi POSIX — proteggi con un controllo della piattaforma se hai come target entrambe le piattaforme.)

Files.createDirectory versus Files.createDirectories

La stessa differenza tra mkdir e mkdir -p, ma basata sulle eccezioni:

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

createDirectory lancia FileAlreadyExistsException se il target esiste già e non è una directory; se è già una directory, lancia comunque (non quello che di solito si vuole).

createDirectories è la scelta più comoda: non fa nulla se tutte le directory sono già presenti, e crea quelle mancanti altrimenti. Non lancia eccezioni se il percorso esiste già come directory. Questo lo rende idempotente — sicuro da chiamare all'avvio senza un controllo exists().

File e directory temporanei

Per i test, lo spazio di lavoro temporaneo e "ho bisogno di un posto sicuro dove mettere questo per qualche minuto," il JDK fornisce Files.createTempFile e Files.createTempDirectory:

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

Entrambi scelgono un nome univoco nella directory temporanea del sistema, entrambi restituiscono un Path alla nuova voce, ed entrambi creano la voce con permessi restrittivi su Unix. Il prefisso e il suffisso sono suggerimenti ai quali il JDK aggiunge un valore univoco — non puoi scegliere il nome esatto (questo è il punto: un'altra chiamata non può prevederlo e sovrascrivere il tuo).

I file temporanei non vengono eliminati automaticamente. Devi:

  • Chiamare Files.deleteIfExists(path) quando hai finito; oppure
  • Chiamare path.toFile().deleteOnExit() per pianificare un'eliminazione alla chiusura della JVM (annullata da kill forzati); oppure
  • Aprire il file con StandardOpenOption.DELETE_ON_CLOSE se ne hai bisogno solo mentre uno stream è aperto.

Gli scrittori creano file implicitamente

La maggior parte delle volte non hai bisogno di una chiamata "crea file" — uno scrittore ne crea uno per te. Files.newBufferedWriter, Files.write e Files.writeString accettano tutti varargs OpenOption... che decidono cosa succede quando il file esiste o non esiste:

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 — crea se mancante, altrimenti apri quello esistente.
  • CREATE_NEW — crea, lancia FileAlreadyExistsException se esiste. La stessa semantica di Files.createFile.
  • TRUNCATE_EXISTING — svuota il contenuto del file all'apertura (il default per writeString quando non si aggiunge in coda).
  • APPEND — scrivi alla fine senza troncare.

Il default per Files.writeString (senza opzioni) è CREATE, WRITE, TRUNCATE_EXISTING — ovvero "crea o sovrascrivi." Files.newBufferedWriter usa gli stessi default. Se vuoi la semantica di append, devi specificarla esplicitamente.

Un esempio pratico: tutti i metodi di creazione a confronto

Il programma seguente costruisce un piccolo albero da zero nella directory temporanea del sistema usando entrambe le API e diverse opzioni di apertura. Ogni passo stampa cosa è cambiato; l'ultimo blocco mostra cosa succede quando si riesegue un'operazione su un percorso che esiste già.

java— editable, runs on the server

Cosa ricavare dall'esecuzione:

  • Il primo legacy.createNewFile() ha restituito true (creato); il secondo ha restituito false (già esistente). Il boolean non ti dice cosa è successo — devi ricordare la convenzione.
  • deep.mkdirs() ha avuto successo una volta e ha restituito false la seconda. Quel false sembra identico a "permesso negato" o "padre mancante" — esattamente il problema della mancanza di informazioni sull'errore che Files risolve.
  • Files.createFile su un percorso esistente ha lanciato FileAlreadyExistsException. Il tipo di eccezione è specifico, quindi un vero handler può distinguere "già presente" da "permesso negato" senza analizzare stringhe.
  • Files.createDirectories chiamato due volte di fila non ha fatto nulla di dannoso la seconda volta. È questa proprietà che lo rende la scelta giusta nel codice di avvio: nessuna guardia, basta chiamarlo.
  • Files.writeString(log, "line 1\n") ha creato il file perché CREATE è nelle opzioni predefinite. Le seconda e terza chiamata hanno usato APPEND esplicitamente, e il file ha accumulato tre righe. La quarta chiamata ha usato CREATE_NEW e si è rifiutata di sovrascrivere. I default sono progettati per il caso "sovrascrivi con nuovo contenuto"; si opta esplicitamente per l'append.
  • Files.createTempFile(root, "scratch-", ".tmp") ha prodotto un nome come scratch-1827392.tmp — il tuo prefisso e suffisso, più un segmento univoco che la JVM sceglie in modo che due chiamate concorrenti non si scontrino mai.
  • La pulizia percorre root in ordine inverso in modo che i file e le directory figlio scompaiano prima dei loro genitori. Files.delete si rifiuta di eliminare una directory non vuota; quell'ordinamento è il modo in cui viene costruito un rm -rf manuale.

Prossimi passi

Puoi creare file; il prossimo capitolo, Leggere file in Java, li legge — prima con i metodi moderni in una riga (Files.readString, Files.readAllLines, Files.lines), poi con il classico stack decorator FileReader / BufferedReader / Scanner in modo che il codice più vecchio nei capitoli successivi abbia una base su cui poggiare.

Esercitazione

Pratica
Vuoi un comando unico che crei una directory insieme a tutte le directory padre mancanti, e non faccia **nulla** se la directory esiste già. Qual è la chiamata?
Vuoi un comando unico che crei una directory insieme a tutte le directory padre mancanti, e non faccia **nulla** se la directory esiste già. Qual è la chiamata?
Was this page helpful?