eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

In this tutorial, we’ll learn how to construct a relative path from two absolute paths in Java. We’ll focus on two built-in Java APIs – the new I/O (NIO2) Path API and the URI class.

2. Absolute vs. Relative Paths

Before we start, let’s do a quick recap. For all the examples in the text, we’ll use the same file structure in the user’s home directory:

/ (root)
|-- baeldung
    \-- bar
    |   |-- one.txt
    |   |-- two.txt
    \-- foo
        |-- three.txt

An absolute path describes a location regardless of the current working directory, starting at the root node. Here are the absolute paths for our files:

one.txt -> /baeldung/bar/one.txt
two.txt -> /baeldung/bar/two.txt
three.txt -> /baeldung/foo/three.txt

The absolute paths always remain the same even if we change the working directory.

On the other hand, a relative path describes the location of the target node relative to its source. If we’re in the baeldung directory, let’s look at the relative paths for the files:

one.txt -> ./bar/one.txt
two.txt -> ./bar/two.txt
three.txt -> ./foo/three.txt

Now, let’s move to the bar subdirectory and check the relative paths again:

one.txt -> ./one.txt
two.txt -> ./two.txt
three.txt -> ../foo/three.txt

As we can see, the results are slightly different. We must remember that relative values can change if we modify the source context, while absolute paths are constant. An absolute path is a special case of a relative path, where the source node is the root of the system.

3. NIO2 API

Now that we know how relative and absolute paths work, it’s time to check out the NIO2 API. As we know, the NIO2 API was introduced with the release of Java 7, and it improved the old I/O API, which had many pitfalls. Using this API, we’ll try to determine the relative path between two files described by their absolute paths.

Let’s start by constructing Path objects for our files:

Path pathOne = Paths.get("/baeldung/bar/one.txt");
Path pathTwo = Paths.get("/baeldung/bar/two.txt");
Path pathThree = Paths.get("/baeldung/foo/three.txt");

To construct a relative path between the source and a given node, we can use the relativize(Path) method provided by the Path class:

Path result = pathOne.relativize(pathTwo);

assertThat(result)
  .isRelative()
  .isEqualTo(Paths.get("../two.txt"));

As we see, the result is definitely a relative path. Is that correct? Especially with the parent operator (../) at the beginning?

We must remember that a relative path can be specified starting from any type of node, which can be a directory or a file. Especially when we use CLI or explorers, we work with directories. Then all relative paths are calculated based on the current working directory.

In our example, we created the Path pointing to a specific file. So we first need to get to the parent of the file, its directory and then go to the second file. Overall, the result is correct.

If we want to make the result relative to the source directory, we can use the getParent() method:

Path result = pathOne.getParent().relativize(pathTwo);

assertThat(result)
  .isRelative()
  .isEqualTo(Paths.get("two.txt"));

We should note that the Path object may point to any file or directory. We need to provide additional checks if we’re building a more complex logic.

Finally, let’s check the relative path between the one.txt and three.txt files:

Path resultOneToThree = pathOne.relativize(pathThree);
Path resultThreeToOne = pathThree.relativize(pathOne);

assertThat(resultOneToThree)
  .isRelative()
  .isEqualTo(Paths.get("..\..\foo\three.txt"));
assertThat(result)
  .isRelative()
  .isEqualTo(Paths.get("..\..\bar\one.txt"));

This quick test confirms that the relative path is context-dependent. While the absolute paths are still the same, the relative path will be different when we swap the source and destination nodes together.

4. java.net.URI API

After checking the NIO2 API, let’s go to the java.net.URI class. We know that URI (Uniform Resource Identifier) is a string of characters that allows us to identify any resource that can also be used when working with files.

Let’s construct URI objects for our files:

URI uriOne = pathOne.toURI();
// URI uriOne = URI.create("file:///baeldung/bar/one.txt")
URI uriTwo = pathTwo.toURI();
URI uriThree = pathThree.toURI();

We can either construct a URI object using String or convert a previously created Path.

As before, the URI class also provides a relativize(URI) method. Let’s use it to construct the relative path:

URI result = uriOne.relativize(uriTwo);

assertThat(result)
  .asString()
  .contains("file:///baeldung/bar/two.txt");

The result isn’t what we expected, the relative path hasn’t been constructed correctly. To answer the question of why this is so, we need to check the official documentation of the class.

This method only returns a relative value if the source URI is a prefix of a target URI. Otherwise, it returns the target value. As a result, we are unable to build a relative path between the file nodes. In this scenario, one URI will never prefix the other.

To return a relative path, we can set our source URI as the directory of the first file:

URI uriOneParent = pathOne.getParent().toUri(); // file:///baeldung/bar/
URI result = uriOneParent.relativize(uriTwo);

assertThat(result)
  .asString()
  .contains("two.txt");

Now the source node is the target prefix, so the result is calculated correctly. Due to the limitations of the method, we’re unable to determine the relative path between one.txt/two.txt and three.txt files using the URI method. Their directories will not have a common prefix.

5. Summary

In this article, we started out by looking at the key differences between absolute and relative paths.

Next, we constructed a relative path between two files described by their absolute paths. We started by checking the NIO2 API and detailed the relative path-building process.

Finally, we tried to achieve the same result with the java.net.URI class. We found that we can’t do all transformations using this API due to its restrictions.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)