W3docs

Java NIO Path Class

Rappresenta i percorsi del filesystem in Java moderno con java.nio.file.Path e la factory Paths.

Path è il sostituto moderno di java.io.File. Rappresenta un percorso del filesystem — una sequenza ordinata di componenti di nome, opzionalmente radicata in / o C:\ — e nient'altro. Non legge il file. Non verifica che il file esista. Non blocca nulla. Le operazioni sui byte su disco si trovano in Files (il capitolo successivo). Path è il sostantivo; Files è il verbo.

Se hai già usato java.io.File, la differenza è duplice: Path è immutabile (ogni operazione restituisce un nuovo Path), e separa nettamente "la stringa del percorso" da "ciò che si trova su disco a quel percorso." La maggior parte delle API moderne — Files, FileChannel, overload di BufferedReader.lines() — accettano Path, non File. Il nuovo codice usa Path.

Costruire un Path

Path p = Path.of("logs", "2025", "app.log");         // joins components with the platform separator
Path q = Path.of("/etc/hosts");                       // absolute Unix path
Path r = Paths.get("C:", "Users", "vaz");             // older factory — same behaviour
Path s = Path.of(URI.create("file:///etc/hosts"));    // from a URI

Path.of(...) è la factory moderna; Paths.get(...) è quella precedente e funziona ancora. Entrambe costruiscono un oggetto path senza toccare il filesystem. Path.of("nope/nope/nope") ha successo anche se non esiste alcun file del genere.

Path.of unisce i varargs con il File.separator della piattaforma — / su Unix, \ su Windows. Questo rende portabile il percorso letterale che scrivi: Path.of("src", "main", "java") costruisce la cosa giusta su entrambi i sistemi. Nel momento in cui scrivi Path.of("src/main/java") con slash hardcoded, l'hai resa Unix-only per sbaglio.

Ispezionare un percorso

I metodi di accesso ai componenti, su Path.of("/var/log/app/today.log"):

MetodoRestituisceEsempio
getFileName()ultimo componente come Pathtoday.log
getParent()tutto tranne l'ultimo/var/log/app
getRoot()la radice, o null se relativo/
getNameCount()numero di componenti di nome4
getName(int i)i-esimo componentegetName(0)var
subpath(b, e)componenti di nome [b, e)subpath(1, 3)log/app
isAbsolute()se il percorso ha una radicetrue
toString()la stringa formattata dalla piattaforma/var/log/app/today.log

Questi sono puri: esaminano la lista interna dei nomi dell'oggetto path e ne restituiscono porzioni. Nessuno di essi tocca il disco.

resolve, resolveSibling e la trappola dell'argomento assoluto

resolve(other) significa "unisci this e other":

Path base = Path.of("/var/log");
base.resolve("app.log");                              // /var/log/app.log
base.resolve("app/today.log");                        // /var/log/app/today.log

La trappola: se other è assoluto, resolve restituisce other invariato:

base.resolve("/etc/hosts");                           // /etc/hosts  -- base is discarded
base.resolve(Path.of("/etc/hosts"));                  // same: /etc/hosts

Questo è il comportamento documentato, e colpisce ogni programmatore Java almeno una volta. Se accetti un nome di file dall'input dell'utente e lo risolvi rispetto a una directory base configurata, un attaccante che fornisce "/etc/passwd" ottiene il suo percorso assoluto — sfuggendo alla base. Valida o normalizza sempre l'input esterno prima di usare resolve.

resolveSibling(other) sostituisce l'ultimo componente:

Path p = Path.of("/var/log/today.log");
p.resolveSibling("yesterday.log");                    // /var/log/yesterday.log

È getParent().resolve(other) con un controllo null incorporato. Utile per "scrivi l'output accanto all'input."

relativize: l'inverso

Dato un base e un target, base.relativize(target) restituisce il percorso relativo da base a target:

Path base = Path.of("/var/log");
Path target = Path.of("/var/log/app/today.log");
base.relativize(target);                              // app/today.log
target.relativize(base);                              // ../..

Il contratto: base.resolve(base.relativize(target)) è equivalente a target (modulo normalize). È il modo per trasformare un target assoluto in un riferimento breve e relativo all'interno di una directory base — utile per righe di log, voci di archivio e URL.

Entrambi i percorsi devono essere dello stesso tipo (entrambi assoluti o entrambi relativi) e devono provenire dallo stesso FileSystem. Mescolarli lancia IllegalArgumentException.

normalize: collassa . e ..

Gli oggetti Path permettono componenti . e ..Path.of("/var/log/../tmp") è un Path valido. normalize() li rimuove sintatticamente:

Path.of("/var/log/../tmp").normalize();               // /var/tmp
Path.of("./a/./b/../c").normalize();                  // a/c

"Sintatticamente" è importante: normalize lavora a livello di stringa. Non chiede al filesystem se .. punta davvero dove le stringhe suggeriscono. Se /var/log è un link simbolico a /tmp/logs, allora su disco /var/log/.. è /tmp, non /var. normalize() non lo sa — si limita a eliminare il ...

Quando hai bisogno del percorso reale su disco (symlink risolti, .. interpretato correttamente), usa toRealPath(), che è una chiamata che tocca il filesystem:

Path real = Path.of("/var/log/../tmp").toRealPath(); // resolves symlinks, throws if the file doesn't exist

Per i confronti di uguaglianza tra percorsi e confronti di stringhe, normalize() è ciò che vuoi. Per "il nome canonico del file su disco in questo momento," è toRealPath().

L'uguaglianza è basata sulle stringhe

path1.equals(path2) confronta i percorsi come stringhe (componente per componente). Non normalizza, non risolve symlink, non controlla il filesystem:

Path.of("/var/log").equals(Path.of("/var/log/."));            // false  -- one has a trailing '.' component
Path.of("/var/log/.").equals(Path.of("/var/log/.").normalize()); // false -- normalize() dropped the '.'
Path.of("/var/log").equals(Path.of("/var/log").normalize());  // true   -- already normalized, no change
Path.of("/var/log").equals(Path.of("/var/log"));              // true

Per confrontare due percorsi come "puntano allo stesso file," normalizza entrambi e confronta, oppure chiama Files.isSameFile(p1, p2) (che tocca il filesystem, l'unico controllo completamente corretto). Per ordinamento e chiavi HashSet, l'uguaglianza basata sulle stringhe è ciò che Path ti fornisce; va bene per la maggior parte degli usi ma non significa "stesso file su disco."

Interoperabilità con File

Path e File si convertono in entrambe le direzioni:

File f = Path.of("/etc/hosts").toFile();
Path p = new File("/etc/hosts").toPath();

Ne avrai bisogno quando una vecchia API accetta File e una nuova API accetta Path (o viceversa). Non memorizzare i percorsi come File; converti al confine dell'API e mantieni Path nel tuo codice.

Un esempio completo: join, resolve, relativize, normalize

Il programma seguente percorre ogni operazione su Path introdotta in questo capitolo usando percorsi concreti. L'output rende visibile la differenza tra resolve e resolveSibling, tra normalize e toRealPath, e tra resolve con argomento assoluto e con argomento relativo.

java— editable, runs on the server

Cosa trarre dall'esecuzione:

  • Path.of(\"/var\", \"log\", \"app\", \"today.log\") ha prodotto /var/log/app/today.log su Unix e \\var\\log\\app\\today.log su Windows. Lasciare che la factory con varargs unisca i componenti è il modo portabile; gli slash o backslash hardcoded nella stringa di input sono il modo non portabile. Usa la factory.
  • La riga resolve(\"/etc/hosts\") ha scartato base e restituito /etc/hosts. Questo è il comportamento con argomento assoluto e la fonte più frequente di "ma ho fornito una directory base, perché sta scrivendo in /etc/hosts?" Valida sempre i nomi di file forniti dall'utente prima di usare resolve. La forma difensiva è base.resolve(other).normalize().startsWith(base) — e anche quella ha sottili lacune quando sono coinvolti i symlink.
  • base.relativize(target) ha restituito app/today.log. Concatenandolo di nuovo con base.resolve(...) si è ottenuto il target originale — l'identità di andata e ritorno. Usalo quando scrivi un messaggio di log o una voce di archivio che necessita di una forma breve e relativa di un lungo percorso assoluto.
  • Path.of(\"/var/log/../tmp/./a/b/../c\").normalize() ha prodotto /var/tmp/a/c. La trasformazione era puramente a livello di stringa: ogni . rimosso, ogni coppia nome/.. rimossa. Il filesystem non è stato consultato. toRealPath nella riga successiva ha invece consultato il filesystem — ecco perché il risultato era il nome canonico, con symlink risolti, del file effettivo su disco. (Su macOS vedrai il percorso temporaneo restituito radicato in /private/var/folders/... anziché /var/folders/...: toRealPath ha seguito il vero symlink /var/private/var, cosa che normalize non può mai fare.) Poiché toRealPath controlla ogni componente, la directory intermedia sub nell'esempio deve effettivamente esistere — ecco perché il programma la crea prima.
  • I due controlli equals: Path.of(\"/var/log\") e Path.of(\"/var\", \"log\") erano uguali (stessa sequenza interna di nomi, stessa stringa); Path.of(\"/var/log\") e Path.of(\"/var/log/.\") non lo erano (uno ha un componente . finale, l'altro no). La lezione: equals è un confronto di stringhe. Per "questi due percorsi puntano allo stesso file?" usa Files.isSameFile(a, b) dal prossimo capitolo — è l'unico controllo che interroga il filesystem.

Cosa viene dopo

Path è il sostantivo. Il prossimo capitolo, Java Files Class, copre il verbo — Files, la gigantesca classe di utilità con operazioni in una riga sul filesystem: readString, writeString, createDirectories, copy, move, delete, walk, e altro ancora.

Esercitati

Pratica
`base` e' `Path.of('/srv/uploads')` e `userInput` e' la stringa `'/etc/passwd'` (un valore controllato da un attaccante). Il tuo codice fa `base.resolve(userInput)` per calcolare il percorso target. Qual e' il percorso risultante e qual e' la lezione di sicurezza?
`base` e' `Path.of('/srv/uploads')` e `userInput` e' la stringa `'/etc/passwd'` (un valore controllato da un attaccante). Il tuo codice fa `base.resolve(userInput)` per calcolare il percorso target. Qual e' il percorso risultante e qual e' la lezione di sicurezza?
Was this page helpful?