Start Learning
Javaneer
Back to stage
Stage 4·Advanced Java

Modules (JPMS)

module-info.java, strong encapsulation, and when modules are worth it.

12 min readAdvanced
On this page

As programs grow to hundreds of classes, keeping them organized and controlling what's exposed becomes vital. The Java Platform Module System (JPMS), added in Java 9, lets you group packages into modules with explicit boundaries.

The problem modules solve

Before modules, any public class was visible to everyone on the classpath. There was no way to say "these classes are internal - hands off." Large codebases became tangled, and it was easy to accidentally depend on another library's private internals.

Modules are like rooms with doors

Imagine a house where every wall is glass and every door is always open - anyone can walk into any room and rummage through your things. Modules add real walls and lockable doors: each module decides which rooms (packages) are open to visitors and which stay private. You publish a clear front door and keep the rest internal.

Declaring a module

A module is defined by a module-info.java file at the root of its source:

module com.myapp.orders {
    // packages this module makes visible to others
    exports com.myapp.orders.api;

    // other modules this one depends on
    requires com.myapp.core;
    requires java.net.http;
}
  • exports - makes a package accessible to other modules. Packages not exported are strongly encapsulated: truly private, even if their classes are public.
  • requires - declares a dependency on another module.

Strong encapsulation

This is the headline benefit. If a package isn't exported, code outside the module simply cannot access it - the compiler and the JVM both enforce it. Your internal implementation stays internal.

module com.myapp.orders {
    exports com.myapp.orders.api;      // public API - others may use it
    // com.myapp.orders.internal is NOT exported → hidden from everyone
}

A clear public API

Modules force you to think deliberately: "What is my public contract, and what is an implementation detail?" Exporting only your intended API - and hiding the rest

  • makes libraries safer to evolve, because you can change unexported internals without breaking anyone.

The JDK itself is modular

Since Java 9, the JDK is split into modules (java.base, java.sql, java.net.http, …). java.base is always available; others you requires as needed. This also enables jlink, which builds a custom, minimal runtime containing only the modules your app uses - great for small container images.

Do you need modules?

When to use JPMS

Modules shine for large applications and libraries that need strict boundaries and a well-defined public API. Many everyday applications - especially those built with Spring Boot - run perfectly well on the classpath without declaring modules. Understand JPMS so you recognize it in the wild, but don't feel obligated to modularize every small project.

Modules vs. packages

Don't confuse the two levels of organization:

PackageModule
GroupsRelated classesRelated packages
BoundaryNaming/namespaceEnforced access + dependencies
Declared inpackage statementmodule-info.java

Quick check

In JPMS, what happens to a public class in a package that is NOT exported by its module?

Key takeaways

  • JPMS (Java 9+) groups packages into modules with explicit boundaries.
  • module-info.java uses 'exports' to expose packages and 'requires' to declare dependencies.
  • Non-exported packages are strongly encapsulated - hidden from other modules even if public.
  • The JDK itself is modular; jlink can build a minimal custom runtime with only needed modules.
  • Modules benefit large apps and libraries; many everyday apps run fine on the classpath without them.

That's Stage 4 complete! You've mastered concurrency, functional programming, streams, reflection, the JVM, and modules. Stage 5 shifts from language features to the craft of writing genuinely great software.