W3docs

Clase Path de Java NIO

Representa rutas del sistema de archivos en Java moderno con java.nio.file.Path y la fábrica Paths.

Path es el reemplazo moderno de java.io.File. Representa una ruta del sistema de archivos — una secuencia ordenada de componentes de nombre, opcionalmente anclada en / o C:\ — y nada más. No lee el archivo. No comprueba si el archivo existe. No bloquea nada. Las operaciones de bytes en disco están en Files (el siguiente capítulo). Path es el sustantivo; Files es el verbo.

Si has usado java.io.File, la diferencia es doble: Path es inmutable (cada operación devuelve un nuevo Path), y separa claramente "la cadena de la ruta" de "lo que hay en disco en esa ruta". La mayoría de las API modernas — Files, FileChannel, sobrecargas de BufferedReader.lines() — toman Path, no File. El código nuevo usa Path.

Construir 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(...) es la fábrica moderna; Paths.get(...) es la más antigua y sigue funcionando. Ambas construyen un objeto de ruta sin tocar el sistema de archivos. Path.of("nope/nope/nope") tiene éxito aunque no exista tal archivo.

Path.of une los argumentos varargs con el File.separator de la plataforma — / en Unix, \ en Windows. Esto hace que la ruta literal que escribes sea portable: Path.of("src", "main", "java") construye la ruta correcta en ambos sistemas. En el momento en que escribes Path.of("src/main/java") con barras hardcodeadas, la has hecho exclusiva de Unix sin querer.

Inspeccionar una ruta

Los métodos de acceso a componentes, sobre Path.of("/var/log/app/today.log"):

MétodoDevuelveEjemplo
getFileName()último componente como Pathtoday.log
getParent()todo excepto el último/var/log/app
getRoot()la raíz, o null si es relativa/
getNameCount()número de componentes de nombre4
getName(int i)componente i-ésimogetName(0)var
subpath(b, e)componentes de nombre [b, e)subpath(1, 3)log/app
isAbsolute()si la ruta tiene raíztrue
toString()la cadena formateada según la plataforma/var/log/app/today.log

Estos métodos son puros: examinan la lista interna de nombres del objeto de ruta y devuelven fragmentos de ella. Ninguno toca el disco.

resolve, resolveSibling y la trampa del argumento absoluto

resolve(other) es "unir this y 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 trampa: si other es absoluto, resolve devuelve other sin cambios:

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

Este es el comportamiento documentado y atrapa a todo programador Java una vez. Si aceptas un nombre de archivo de la entrada del usuario y lo resuelves contra un directorio base configurado, un atacante que proporcione "/etc/passwd" aterrizará su ruta absoluta — escapando de la base. Valida o normaliza siempre la entrada externa antes de usar resolve.

resolveSibling(other) reemplaza el último componente:

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

Es getParent().resolve(other) con una comprobación de nulo incorporada. Útil para "escribir la salida junto a la entrada".

relativize: la operación inversa

Dada una base y un objetivo, base.relativize(target) devuelve la ruta relativa desde base hasta 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);                              // ../..

El contrato: base.resolve(base.relativize(target)) es equivalente a target (módulo normalize). Así es como conviertes un objetivo absoluto en una referencia corta y relativa dentro de un directorio base — útil para líneas de registro, entradas de archivo y URLs.

Ambas rutas deben ser del mismo tipo (ambas absolutas o ambas relativas) y deben provenir del mismo FileSystem. Mezclarlas lanza IllegalArgumentException.

normalize: colapsar . y ..

Los objetos Path permiten componentes . y ..Path.of("/var/log/../tmp") es un Path válido. normalize() los elimina sintácticamente:

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

"Sintácticamente" importa: normalize trabaja a nivel de cadena. No pregunta al sistema de archivos si .. apunta realmente a donde sugieren las cadenas. Si /var/log es un enlace simbólico a /tmp/logs, entonces en disco /var/log/.. es /tmp, no /var. normalize() no lo sabe — simplemente elimina el ...

Cuando necesitas la ruta real en disco (enlaces simbólicos resueltos, .. interpretado correctamente), usa toRealPath(), que es una llamada que toca el sistema de archivos:

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

Para comprobaciones de igualdad de rutas y comparaciones de cadenas, normalize() es lo que quieres. Para "el nombre canónico del archivo en disco en este momento", es toRealPath().

La igualdad es basada en cadenas

path1.equals(path2) compara las rutas como cadenas (componente a componente). No normaliza, no resuelve enlaces simbólicos, no comprueba el sistema de archivos:

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

Para comparar dos rutas como "¿apuntan al mismo archivo?", normaliza ambas y compara, o llama a Files.isSameFile(p1, p2) (toca el sistema de archivos, la única comprobación totalmente correcta). Para ordenar y usar como claves en HashSet, la igualdad por cadena es lo que Path te ofrece; es adecuada para la mayoría de usos, pero no significa "mismo archivo en disco".

Interoperabilidad con File

Path y File se convierten en ambas direcciones:

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

Necesitarás esto cuando una API antigua tome File y una nueva tome Path (o viceversa). No almacenes rutas como File; convierte en el límite de la API y mantén Path en tu código.

Ejemplo práctico: join, resolve, relativize, normalize

El programa a continuación recorre cada operación de Path introducida en este capítulo con rutas concretas. La salida hace visible la diferencia entre resolve y resolveSibling, entre normalize y toRealPath, y entre resolve con argumento absoluto y con argumento relativo.

java— editable, runs on the server

Lo que se puede extraer de la ejecución:

  • Path.of(\"/var\", \"log\", \"app\", \"today.log\") produjo /var/log/app/today.log en Unix y \\var\\log\\app\\today.log en Windows. Dejar que la fábrica varargs una los componentes es la forma portable; las barras hardcodeadas / o \ en la cadena de entrada son la forma no portable. Usa la fábrica.
  • La línea resolve(\"/etc/hosts\") descartó base y devolvió /etc/hosts. Ese es el comportamiento con argumento absoluto y la fuente más frecuente de "pero le di un directorio base, ¿por qué está escribiendo en /etc/hosts?". Valida siempre los nombres de archivo proporcionados por el usuario antes de usar resolve. La forma defensiva es base.resolve(other).normalize().startsWith(base) — e incluso eso tiene sutiles problemas cuando hay enlaces simbólicos de por medio.
  • base.relativize(target) devolvió app/today.log. Concatenar eso de vuelta con base.resolve(...) produjo el objetivo original — la identidad del viaje de ida y vuelta. Úsalo cuando escribas un mensaje de registro o una entrada de archivo que necesita una forma corta y relativa de una ruta absoluta larga.
  • Path.of(\"/var/log/../tmp/./a/b/../c\").normalize() produjo /var/tmp/a/c. La transformación fue puramente a nivel de cadena: cada . eliminado, cada par nombre/.. eliminado. No se consultó el sistema de archivos. toRealPath en la siguiente línea sí consultó el sistema de archivos — por eso el resultado fue el nombre canónico, con enlaces simbólicos resueltos, del archivo real en disco. (En macOS verás que la ruta temporal regresa con raíz en /private/var/folders/... en lugar de /var/folders/...: toRealPath siguió el enlace simbólico real /var/private/var, algo que normalize nunca puede hacer.) Dado que toRealPath verifica cada componente, el directorio intermedio sub del ejemplo debe existir realmente — por eso el programa lo crea primero.
  • Las dos comprobaciones de equals: Path.of(\"/var/log\") y Path.of(\"/var\", \"log\") eran iguales (misma secuencia interna de nombres, misma cadena); Path.of(\"/var/log\") y Path.of(\"/var/log/.\") no lo eran (uno tiene un componente . al final, el otro no). La conclusión: equals es una comparación de cadenas. Para "¿apuntan estas dos rutas al mismo archivo?" usa Files.isSameFile(a, b) del siguiente capítulo — es la única comprobación que pregunta al sistema de archivos.

Qué sigue

Path es el sustantivo. El siguiente capítulo, Clase Files de Java, cubre el verbo — Files, la gigantesca clase de utilidades con operaciones de una línea sobre el sistema de archivos: readString, writeString, createDirectories, copy, move, delete, walk, y más.

Práctica

Práctica
`base` es `Path.of('/srv/uploads')` y `userInput` es la cadena `'/etc/passwd'` (un valor controlado por un atacante). Tu código hace `base.resolve(userInput)` para calcular la ruta destino. ¿Cuál es la ruta resultante y cuál es la lección de seguridad?
`base` es `Path.of('/srv/uploads')` y `userInput` es la cadena `'/etc/passwd'` (un valor controlado por un atacante). Tu código hace `base.resolve(userInput)` para calcular la ruta destino. ¿Cuál es la ruta resultante y cuál es la lección de seguridad?
Was this page helpful?