skip to content

JPMS Fundamentals

A module declares its dependencies and its exported packages in module-info.java, delivering reliable configuration and strong encapsulation. Interviewers want the two goals stated plainly, since everything else follows from them.

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

questions

5

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

level: middleimportance: must knowfreq 60%

answer

  1. Resolve the whole module graph before main() runs
  2. Missing module → fast clear startup error, not late NoClassDefFoundError
  3. Split package rejected: one package, one module
  4. Duplicate module rejected
  5. Graph-based, so no first-wins shadowing

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.

solid answer

~40 s

Reliable configuration is one of JPMS's two core goals: the module system reads every module's descriptor, follows the `requires` directives, and resolves the full dependency graph before the application runs. This catches three classic classpath ailments early. A missing dependency now fails fast at startup with a clear 'module not found' error instead of a later `NoClassDefFoundError`. Two modules exporting the same package (a 'split package') is rejected — a package must belong to exactly one module. And the same module appearing twice is detected. Because resolution is graph-based rather than a flat top-to-bottom scan, there is no silent shadowing where the first matching class on the classpath wins. The net effect: configuration errors surface at launch with actionable messages, not deep into execution.

go deeper

for a junior

Knows that modules declare dependencies with requires and that missing ones are caught early.

for a middle

Can name the three failures addressed (missing, duplicate, split package), explain resolution and the module graph, and contrast with the late NoClassDefFoundError of the classpath.

for a senior

Explains the 'shift-left' value of resolution-time checks, why split packages must be single-owner, and the boundary between reliable configuration and version management (JPMS does not version-select).

for a principal

Reasons about how reliable configuration affects build/deploy pipelines and large dependency graphs, and where it stops (no version resolution) so external tooling like Maven/Gradle still owns version selection.

## Recap: the classpath's weaknesses The **classpath** is the pre-module mechanism for telling the JVM where to find classes: a flat, ordered list of JARs and directories. It has three well-known failure modes: 1. **Missing dependency, found late.** Nothing records that `app.jar` needs `lib.jar`. If `lib.jar` is absent, everything compiles and even starts; the failure (`NoClassDefFoundError` / `ClassNotFoundException`) appears only when the code path that touches the missing class finally runs — possibly hours in, possibly only in production. 2. **Shadowing / first-wins.** If two JARs contain a class with the same fully-qualified name, the JVM uses whichever appears first on the classpath and silently ignores the other. You get the wrong version with no warning. 3. **Split packages.** A **package** (e.g. `com.example.util`) is meant to be one cohesive namespace, but on the classpath its classes can be scattered across several JARs. This breaks visibility assumptions and causes subtle bugs. ## What 'reliable configuration' means **Reliable configuration** is JPMS's guarantee that the set of modules an application uses is **complete, consistent, and unambiguous — verified before the application's `main` runs**. The module system performs a step called **resolution**: starting from a root module, it reads each module's descriptor, follows every `requires` directive, and transitively pulls in all needed modules to build the **module graph**. If anything is wrong with that graph, resolution fails immediately. ## The specific failures it eliminates - **Missing modules → fast, clear failure.** If a `requires` names a module that is not present on the modulepath, resolution aborts at startup with an error like `module X not found, required by Y`. You learn about the gap before any business logic executes, and the message tells you exactly which dependency is missing and who needs it. - **Duplicate modules → rejected.** The same module supplied twice in the same configuration is an error; you cannot accidentally load two copies. - **Split packages → rejected.** A package must be owned by **exactly one** module in a given configuration. If two readable modules both contain package `com.example.util`, resolution fails. This enforces that a namespace is cohesive and lives in one place. - **No silent shadowing.** Because each package is owned by exactly one module and resolution is graph-based, there is no 'first JAR on the list wins' ambiguity. A class has a single, well-defined provider. ## Why 'before it runs' is the key phrase The value is the **timing**: the difference between a startup error and a runtime error. Classpath problems were notorious for hiding until an unlucky code path executed. JPMS shifts those checks **left** — to resolution time — turning them into deterministic, immediately-visible failures with names attached. ## How it relates to strong encapsulation Reliable configuration is about *which* modules and *what* dependencies; **strong encapsulation** (the sibling goal) is about *what within a module is reachable*. Reliable configuration ensures the graph is sound; strong encapsulation ensures you can only touch the exported parts of the modules in that graph. Together they make the dependency story explicit and enforced. ## A concrete picture If module `A` has `requires B;` and `B` is absent, the old world started fine and blew up later. The module world prints a resolution error at launch naming both `A` and the missing `B`. That single behavioural change is the heart of reliable configuration.

  • Does reliable configuration resolve conflicting versions of the same library?
    No. JPMS does no version selection — it detects missing, duplicate, and split-package problems, but choosing which version of a library to use remains the build tool's (Maven/Gradle) responsibility.
  • When exactly are these checks performed?
    During module resolution, which happens before the application's main method runs. That 'shift-left' timing is the core value: configuration errors become deterministic startup failures rather than late runtime errors.

saying these in an interview costs you the question

  • Saying reliable configuration improves runtime performance — it is about correctness and early detection, not speed
  • Claiming it eliminates all version conflicts — JPMS does not do version selection; it detects missing/duplicate/split-package issues, not 'pick the right version'
  • Thinking split packages are merely discouraged — they are rejected at resolution
  • Confusing it with strong encapsulation (visibility within modules)

context

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 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 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

How do requires, requires transitive, and the readability graph shape module API design and migration at scale?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

requires says your module depends on another. requires transitive also re-exports that dependency to anyone who depends on you, so they read it automatically. The set of who-can-read-whom is the readability graph. Designing these directives well keeps APIs clean and migration manageable.

open as a page