Path.resolve(), relativize(), normalize()
Fast recall
Quick revision
Core answer
resolve combines a base with another path, relativize computes directions from one peer path to another, and normalize removes redundant . and cancellable name/.. elements syntactically without touching the file system.Interview-ready guide
Interview answer
resolve, relativize, and normalize are Path operations for composing and simplifying path syntax. They usually do not access the file system, so they can operate on locations that do not exist. Each answers a different question: build a location, describe a route between locations, or remove redundant elements.
base.resolve(other) treats base as the starting path. If other is relative, its elements are appended; if other is absolute, the result is normally other itself. This is the portable replacement for concatenating directory strings. base.resolveSibling(name) instead resolves beside the current path through its parent.
from.relativize(to) calculates a relative path that, when resolved against from, locates to for compatible normalized paths. Both Paths must use the same provider, and the default provider requires both to be absolute or both relative. For /work/app to /work/test, the result is typically ../test.
normalize() removes . and removes a name followed by .. when possible. For example, reports/2025/../2026 becomes reports/2026. This is purely lexical. If 2025 were a symbolic link, canceling it before access could produce a different real location from traversing the original path.
Use these methods to construct and display Paths, but do not confuse a neat spelling with verified identity. toRealPath() performs I/O on an existing target and resolves links by default. Even then, later file-system changes can invalidate assumptions. The safe summary is: resolve composes, relativize describes a route, normalize cleans syntax, and real-path resolution observes storage.
Path.resolve(), relativize(), normalize()
Direct Answer
resolve combines a base with another path, relativize computes directions from one peer path to another, and normalize removes redundant . and cancellable name/.. elements syntactically without touching the file system.
Deep Dive & Architecture
A lexical operation sees only ordered name elements. It can prove that a/./b has a redundant dot, but it cannot know whether a/link/../b traverses a symbolic link whose target has a different parent. That knowledge requires the file-system provider and an existing path. This is why normalize() is useful but not a security canonicalization step by itself.
Relativization also depends on the base being interpreted as a location, not automatically as the parent directory of a file. If from is /work/report.txt, relativizing to /work/archive.txt produces a path relative to report.txt as though it were a base path. Call from.getParent().relativize(to) when the intended base is the containing directory.
The identity relation is provider-specific. Path.equals compares path values according to the provider and does not generally follow links. Files.isSameFile asks whether two Paths locate the same existing file and may perform I/O. toRealPath produces one real spelling at a moment in time, but it does not lock the target against replacement.
Keeping these layers explicit prevents common bugs: use resolve for construction, normalize for harmless display or known syntactic cleanup, and real operations for facts such as existence, identity, access, or link targets. Then handle the possibility that those facts change.
Code Example
import java.nio.file.Path;
class PathOperations {
public static void main(String[] args) {
Path base = Path.of("work", "app");
Path target = Path.of("work", "test");
Path resolved = base.resolve("logs").resolve("today.txt");
Path relative = base.relativize(target);
Path normalized = Path.of("a", ".", "b", "..", "c").normalize();
if (!resolved.equals(Path.of("work", "app", "logs", "today.txt"))) throw new AssertionError();
if (!relative.equals(Path.of("..", "test"))) throw new AssertionError(relative);
if (!normalized.equals(Path.of("a", "c"))) throw new AssertionError(normalized);
System.out.println(resolved + " | " + relative + " | " + normalized);
}
}
Check your understanding
Answer these without looking back at the article.
- What happens when resolve receives an absolute Path?
- Why can normalize change meaning around a symbolic link?
- How is Path.equals different from Files.isSameFile?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.