skip to content

Module System (JPMS)

The Java Platform Module System: module declarations, directives, readability and accessibility, the module types, module path versus classpath, migration pain, services and the tooling. Interviewers ask about it mostly through what broke and why.

part ofJavaoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

What do the `requires` and `exports` directives do in a `module-info.java`, and why are they fundamental to the Java module system?

level: juniorimportance: must knowfreq 72%

answer

  1. requires = readability (I depend on you)
  2. exports = accessibility (this package is visible)
  3. public is not enough — package must be exported
  4. exports is per-package, no sub-package wildcard
  5. both sides must agree to cross the boundary

basics

~20 s

requires says your module depends on another module so you can use its code. exports makes one of your packages visible to other modules. Without an export, a package stays internal and other modules cannot use it, even if it is public.

solid answer

~40 s

In JPMS (the Java Platform Module System, added in Java 9), every module declares itself in `module-info.java`. `requires <module>` declares a dependency: it grants your module *readability* of that module, so you can compile and run against the types it exports. `exports <package>` does the reverse: it makes exactly that one package accessible to other modules. The two are complementary — a type is usable across a module boundary only if the owning module `exports` its package AND the consuming module `requires` the owning module. Crucially, `public` is no longer enough: a public class in a non-exported package is invisible outside its module ("strong encapsulation"). Exports are per-package and not transitive — listing a package does not export sub-packages. This pair replaces the flat, everything-visible classpath with explicit, compiler-enforced dependencies.

code

java · 14 lines
java
// module com.acme.core's module-info.java
module com.acme.core {
    exports com.acme.core.api;        // visible to readers
    // com.acme.core.internal is NOT exported -> stays private
}

// module com.acme.app's module-info.java
module com.acme.app {
    requires com.acme.core;            // can now read core's exports
    exports com.acme.app.web;
}

// In com.acme.app: OK -> com.acme.core.api is exported and required
// Using com.acme.core.internal.* -> compile error (not exported)

go deeper

for a junior

Knows requires = a dependency and exports = makes a package visible; can read a simple module-info.

for a middle

Explains strong encapsulation (public is not enough), per-package exports with no wildcard, and that both sides must agree.

for a senior

Reasons about API surface design via exports, the implicit java.base, and how the module graph is validated at compile and launch.

for a principal

Designs module boundaries for a library/platform: minimal exported surface, internal packages, and migration trade-offs from the classpath.

## Background: what a module is Before Java 9, code was organized only into **packages** (namespaces like `com.acme.util`) loaded from a flat **classpath**. Any public class anywhere on the classpath was visible to all other code. There was no way to say "this package is my internal implementation, keep out." The **Java Platform Module System (JPMS)**, introduced in Java 9 (Project Jigsaw), adds a layer *above* packages: a **module** is a named, self-describing group of packages. You declare a module in a special file at the source root called **`module-info.java`**: ```java module com.acme.app { requires com.acme.core; exports com.acme.app.api; } ``` ## `requires` — declaring a dependency (readability) `requires <moduleName>` states that your module **depends on** another module. The technical term is that it grants **readability**: module A *reads* module B. Only if A reads B can A use the public types in the packages B exports. The compiler and the runtime both enforce this — if you use a type from a module you did not `requires`, compilation fails. Every module implicitly `requires java.base` (the core JDK module with `java.lang`, `java.util`, etc.); you never write that line. ## `exports` — exposing a package (accessibility) `exports <packageName>` makes the **public** types in exactly that one package accessible to any module that reads yours. This is the heart of **strong encapsulation**: in the module world, `public` means "public *within the module*"; it crosses the module boundary only if the package is also exported. Key rules: - Exports are **per package**, listed individually. `exports com.acme.api` does NOT export `com.acme.api.internal` — there is no wildcard, sub-packages are separate. - Exporting affects only **public/protected** members (normal access rules still apply within an exported package). - A package that is not exported is an **internal** package: invisible to other modules at compile and run time, even via its public classes. ## How the two combine A type crosses a module boundary only when **both** sides agree: 1. The owning module `exports` the package, and 2. The consuming module `requires` the owning module. Miss either and you get an error. This turns dependencies from an implicit, fragile classpath soup into an explicit, compiler-checked graph — no accidental dependence on someone's internals, and missing dependencies are caught at build time rather than as a runtime `NoClassDefFoundError`. ## Why it matters - **Strong encapsulation:** library authors can keep `*.internal` packages truly private. - **Reliable configuration:** the module graph is validated up front; you cannot launch with a missing module. - **Clear API surface:** the exported packages are precisely the public contract.

  • If a class is `public` but its package is not exported, can another module use it?
    No. In JPMS, crossing a module boundary requires both an export of the package and a requires of the module. A public class in a non-exported package is invisible outside its own module (strong encapsulation).
  • Does `exports com.acme.api` also export `com.acme.api.model`?
    No. Exports are per-package with no wildcard; sub-packages are distinct packages and must be exported individually.

A module is like an office building. requires is getting a keycard to another building. exports is which of your own rooms have public doors. Even a room with a 'public' sign stays locked to outsiders unless the building lists it as open.

saying these in an interview costs you the question

  • Thinking `public` alone makes a class usable across modules
  • Assuming `exports a.b` cascades to `a.b.c` sub-packages
  • Confusing requires (consume) with exports (provide) directions
  • Believing you must declare requires java.base

context

open as a page

What is the difference between the classpath and the module path, and how does the same plain JAR behave on each?

level: juniorimportance: must knowfreq 50%

basics

~20 s

The classpath (-cp) is the old flat list of JARs where everything is visible to everything; code there forms the unnamed module. The module path (-p) is the JPMS list where each JAR is a module. A plain JAR is part of the unnamed module on the classpath, but an automatic module on the module path.

open as a page

What is the difference between the classpath and the module path in Java, and how does each find and load classes?

level: juniorimportance: must knowfreq 60%

basics

~20 s

The classpath is a flat list of JARs/folders the JVM scans to find classes, with no boundaries between them. The module path holds modules that declare their names and dependencies in module-info, so the JVM resolves and enforces them.

open as a page

What is 'reliable configuration' in JPMS, and which classpath failures does it eliminate?

level: middleimportance: must knowfreq 60%

basics

~20 s

Reliable configuration means the module system checks all dependencies up front. Each module declares what it needs with 'requires', so missing or duplicate modules are caught when the program starts instead of failing later with a runtime error.

open as a page

What is 'strong encapsulation' in JPMS, and how does it change the meaning of 'public'?

level: middleimportance: must knowfreq 62%

basics

~20 s

Strong encapsulation means only the packages a module explicitly exports are usable by other modules. A public class in a non-exported package is hidden, so 'public' no longer means 'visible to everyone' — it means visible only within the module unless the package is exported.

open as a page

What is a split package in JPMS, why is it forbidden, and how do you fix one during migration?

level: middleimportance: must knowfreq 62%

basics

~20 s

A split package is when the same package name lives in two different modules. The module system forbids it because each package must belong to exactly one module. You fix it by merging the package into one module or moving the classes so the package exists in only one place.

open as a page

What are the three categories of modules in the Java Platform Module System, and what defines each one?

level: middleimportance: must knowfreq 62%

basics

~20 s

JPMS has three kinds of modules: named modules (a JAR with a module-info.java declaring its name, exports, and requires), automatic modules (a plain JAR placed on the module path, getting an auto-derived name), and the unnamed module (everything on the old classpath).

open as a page

What is the 'unnamed module', and what role does the classpath play in the module system?

level: middleimportance: must knowfreq 45%

basics

~20 s

Even on Java 9+, code loaded from the classpath isn't outside the module system — it all goes into one special bucket called the unnamed module. The unnamed module can read every other module and export all its packages.

open as a page

In the Java module system, what is the difference between `exports` and `opens` for a package?

level: middleimportance: must knowfreq 70%

basics

~10 s

exports lets other modules use a package's public types at compile time and run time. opens additionally lets other modules use reflection to reach into the package, including its private members.

open as a page

In JPMS, how do the `provides ... with` and `uses` directives work, and how do they replace the META-INF/services file?

level: middleimportance: must knowfreq 60%

basics

~20 s

In a module's module-info.java, the provider writes provides Service with Impl; to declare its implementation, and the consumer writes uses Service; to declare it loads that service. These two lines replace the old META-INF/services text file when using modules.

open as a page

What is the difference between `exports` and `opens` (including `opens...to` and an `open module`), and why is `opens` needed for reflection-heavy frameworks?

level: seniorimportance: must knowfreq 62%

basics

~20 s

exports makes a package's public types visible at compile and run time. opens instead grants deep reflective access at runtime — letting frameworks read/set private fields and call private members via reflection. Frameworks like Jackson or Hibernate need opens, not just exports.

open as a page

What happened to internal JDK APIs like sun.* and com.sun.* in the module system, and how do you cope when your code depends on them?

level: seniorimportance: must knowfreq 58%

basics

~20 s

In Java 9+, internal JDK packages (like sun.misc.Unsafe) are encapsulated by their modules and no longer exported, so code that used them fails to compile or warns/breaks at runtime. The right fix is to switch to a supported public API; as a temporary workaround you can use --add-exports or --add-opens.

open as a page

What is a module in the Java Platform Module System (JPMS), and how do you declare one?

level: juniorimportance: should knowfreq 55%

basics

~20 s

A module is a named group of related Java packages and resources. You declare it in a special file, module-info.java, at the root of your source code; it names the module and lists what it needs and what it shares.

open as a page

What is module readability in JPMS, and how does a module obtain it?

level: juniorimportance: should knowfreq 55%

basics

~10 s

Readability means one module is allowed to depend on another. Module A reads module B when A's module-info.java says requires B. Without that, A cannot use B's types at all.

open as a page

What is a Service Provider Interface (SPI) in Java, and how does ServiceLoader let code discover implementations at runtime?

level: juniorimportance: should knowfreq 55%

basics

~10 s

An SPI is an interface (or abstract class) that other code provides implementations for. ServiceLoader.load(MyService.class) finds and loads those implementations at runtime, so your program can use plug-ins it wasn't compiled against.

open as a page

What is jdeps, and what kinds of problems does it help you diagnose in a Java codebase?

level: juniorimportance: should knowfreq 45%

basics

~10 s

jdeps is a JDK command-line tool that analyzes the dependencies of compiled Java classes and JARs. It shows which packages and modules your code depends on, and can flag use of internal JDK APIs.

open as a page

What is `requires transitive`, and how does implied readability change what consumers of your module can see?

level: middleimportance: should knowfreq 58%

basics

~20 s

requires transitive X means anyone who depends on your module automatically also gets to read module X, without listing it themselves. It is used when your public API exposes types from X, so callers can use those types.

open as a page

Why does JPMS forbid cyclic module dependencies, and what do you do when two modules seem to need each other?

level: middleimportance: should knowfreq 41%

basics

~20 s

JPMS does not allow module A to require module B while B also requires A (directly or through a chain). Cycles are banned so the module graph can be resolved in a clear order. You break a cycle by extracting the shared code into a third module, or by inverting a dependency with an interface.

open as a page

How does an automatic module get its name, and why is relying on the filename-derived name risky?

level: middleimportance: should knowfreq 40%

basics

~20 s

An automatic module's name comes from the Automatic-Module-Name entry in the JAR's MANIFEST.MF if present. If not, Java derives it from the JAR filename (dropping the version and turning non-letters/digits into dots). Filename-derived names are risky because renaming the file changes the module name.

open as a page

What does `requires static` mean, and when would you use an optional, compile-time-only module dependency?

level: seniorimportance: should knowfreq 40%

basics

~20 s

requires static X makes module X required when compiling but optional at run time. It is for things like annotations or optional integrations that you compile against but that do not need to be present when the app runs.

open as a page

How do the `uses` and `provides...with` directives implement the service-loader pattern in JPMS, and how does this relate to `ServiceLoader`?

level: seniorimportance: should knowfreq 44%

basics

~20 s

uses S declares that your module consumes a service of interface type S via ServiceLoader. provides S with Impl declares that your module supplies Impl as an implementation of S. Together they let modules discover implementations without a hard dependency on the provider.

open as a page

What is the difference between the classpath and the modulepath, and what is an 'automatic module'?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The classpath is the old flat list of JARs with no module rules. The modulepath loads JARs as modules with their declared dependencies and exports. A plain JAR placed on the modulepath becomes an 'automatic module' — it gets a name and exports everything, so old libraries can be used as modules.

open as a page

Contrast bottom-up and top-down JPMS migration strategies and explain how automatic modules and the unnamed module enable incremental migration.

level: seniorimportance: should knowfreq 47%

basics

~20 s

Bottom-up means modularizing your dependencies first, then your own code; top-down means modularizing your own application first while leaving libraries as automatic modules. Automatic modules (plain jars on the module path) and the classpath unnamed module let you mix modular and non-modular code so you can migrate gradually.

open as a page

Why can a named module not read the unnamed module, and how does this affect migrating an app to JPMS?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The unnamed module has no name, and a module-info file declares dependencies by name (requires X). With no name to write, a named module literally cannot require the unnamed module, so it cannot see any code left on the classpath.

open as a page

How do mixed classpath + module path setups work during a migration to JPMS, and what should you watch out for?

level: seniorimportance: should knowfreq 35%

basics

~20 s

You can run with both -cp and -p at once. JARs you move onto the module path become modules (named or automatic); the rest stay classpath code in the unnamed module. You usually migrate from the leaf libraries inward because named modules can't see classpath code.

open as a page

What happens during module resolution on the module path, and how does it honor module-info compared to the classpath's lookup?

level: seniorimportance: should knowfreq 30%

basics

~20 s

At startup the JVM reads each module's module-info, starts from a root module, follows its requires clauses to build a dependency graph, and verifies there are no missing modules, duplicate names, or split packages. The classpath does none of this — it just searches JARs in order when a class is needed.

open as a page

A framework throws `InaccessibleObjectException` (or 'Unable to make field accessible') when reflecting into your module. What is the root cause and how do you fix it correctly?

level: seniorimportance: should knowfreq 58%

basics

~20 s

The framework is using reflection to reach into your code's private members, but your package is not opened. Modules block reflective access by default. Fix it by adding opens yourpackage; (or opens yourpackage to the.framework;) in module-info.java.

open as a page

Can a type be `public` in Java yet still be inaccessible from another module? Explain why.

level: seniorimportance: should knowfreq 60%

basics

~20 s

Yes. Before modules, public meant accessible everywhere. With modules, a public type is only accessible from another module if its package is also exported and the other module reads yours. So public no longer guarantees access.

open as a page

What is ServiceConfigurationError, when is it thrown, and what are the common operational pitfalls when wiring SPI across the classpath and module path?

level: seniorimportance: should knowfreq 35%

basics

~20 s

ServiceConfigurationError is thrown when a declared provider can't be loaded or instantiated — bad class name, no public no-arg constructor, or it doesn't implement the service. It surfaces while you iterate the ServiceLoader, not when you call load(). Typical pitfalls: forgetting uses on the module path, stale META-INF/services entries, and missing constructors.

open as a page

showing 1–30 of 40