Java
Programming
Code Optimization
Path Combination
Software Development

How to combine paths in Java?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Java, paths should be combined with the java.nio.file.Path API, not by concatenating strings with slashes. The Path API understands platform separators, absolute-path rules, normalization, and common follow-up file operations.

Use Path.of or Paths.get

The simplest way to build a path from segments is:

java
1import java.nio.file.Path;
2
3Path path = Path.of("var", "data", "images", "photo.jpg");
4System.out.println(path);

Older code may use Paths.get(...), which is fine too. Both approaches let Java choose the correct separator for the host platform.

Combine an Existing Base Path with resolve

If you already have a base path, use resolve:

java
1import java.nio.file.Path;
2
3Path base = Path.of("/srv/uploads");
4Path file = base.resolve("avatars/user.png");
5
6System.out.println(file);

This is the most common path-composition method in real code.

Absolute Child Paths Behave Differently

One important rule surprises people: if the child path is absolute, resolve ignores the base and returns the absolute child.

java
1Path base = Path.of("/srv/uploads");
2Path child = Path.of("/tmp/file.txt");
3
4System.out.println(base.resolve(child));

That behavior is correct, but it means resolve is not a simple string join. It is path-aware composition.

Normalize When You Need a Clean Lexical Path

Normalization removes redundant "." and ".." path segments lexically:

java
1Path messy = Path.of("/srv/uploads/./avatars/../avatars/user.png");
2Path normalized = messy.normalize();
3
4System.out.println(normalized);

This is useful for cleaning paths before logging or comparison, but remember that normalize does not check whether the path actually exists.

If you need a canonical path that resolves symlinks and requires the path to exist, use toRealPath() instead and handle the checked exception it may throw.

Safe Combination with User Input

If a user supplies part of the path, normalize and then verify the result stays under the intended base directory:

java
1import java.nio.file.Path;
2
3Path base = Path.of("/srv/uploads").toAbsolutePath().normalize();
4Path target = base.resolve("images/user.png").normalize();
5
6if (!target.startsWith(base)) {
7    throw new IllegalArgumentException("path traversal attempt");
8}

This pattern is important for upload, download, and file-serving endpoints.

resolveSibling Is Often Handy

When you need a file next to another file rather than inside it, resolveSibling is cleaner than manually asking for the parent:

java
1Path report = Path.of("/srv/reports/daily.csv");
2Path backup = report.resolveSibling("daily.bak");
3
4System.out.println(backup);

That small API call removes a lot of awkward parent-path handling in practice.

It is particularly useful for generating companion files such as backups, temporary files, or sidecar metadata files next to an existing path. That pattern shows up often in reporting and export code. It also keeps the intent obvious to future readers.

Use the Path API with File Operations

The benefit of Path becomes clearer when combined with Files:

java
1import java.nio.file.Files;
2import java.nio.file.Path;
3
4Path output = Path.of("build", "reports", "daily.txt");
5Files.createDirectories(output.getParent());
6Files.writeString(output, "done");

Path creation, parent lookup, directory creation, and file writing all work naturally together.

That is another reason to stay with Path objects from the beginning instead of switching back and forth between strings and file APIs.

Common Pitfalls

The biggest mistake is manual string concatenation such as base + "/" + child. That bypasses platform-aware behavior and ignores absolute-path semantics.

Another mistake is forgetting that resolve with an absolute child path discards the base path.

A third issue is assuming normalize validates existence or follows symlinks. It only rewrites the lexical path string.

Summary

  • Use Path.of or Paths.get instead of string concatenation.
  • Use resolve to attach a child path to a base path.
  • Remember that absolute child paths override the base in resolve.
  • Use normalize to clean lexical path segments.
  • Validate user-influenced paths before using them in file-system operations.

Course illustration
Course illustration

All Rights Reserved.