skip to content

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

level: seniorimportance: should knowfreq 45%

answer

  1. Classpath = unnamed module, old rules; modulepath = module rules
  2. Explicit (has module-info) vs automatic (plain JAR on modulepath) vs unnamed (classpath)
  3. Automatic module: name from manifest or filename, exports all, reads all
  4. Named modules cannot require the unnamed module
  5. Automatic modules = migration bridge

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.

solid answer

~40 s

The JVM has two ways to locate code. The classpath (`-cp`) is the legacy flat list; everything on it lands in the 'unnamed module' with no encapsulation and no dependency checks. The modulepath (`--module-path`) treats artifacts as modules: their descriptors are read, dependencies resolved, and encapsulation enforced. The two interoperate to support migration. A modular JAR on the modulepath is an 'explicit module' (it has module-info.class). A plain JAR on the modulepath becomes an 'automatic module': JPMS synthesizes a module from it, deriving a name (from the Automatic-Module-Name manifest entry if present, else from the filename), exporting all its packages, and letting it read every other module. This bridges un-migrated libraries into the module world. Code on the classpath sits in the unnamed module, which reads everything but cannot be required by named modules.

go deeper

for a junior

Aware there is a modulepath separate from the classpath, and that old JARs can still be used.

for a middle

Distinguishes classpath (unnamed module) from modulepath (module rules) and knows automatic modules exist for plain JARs.

for a senior

Explains the three module kinds, automatic-module name derivation and its risks, that named modules cannot read the unnamed module, and how this enables top-down/bottom-up migration.

for a principal

Plans a phased modularization strategy across many artifacts, mandates Automatic-Module-Name on libraries, anticipates split-package and unnamed-module-readability pitfalls, and balances migration cost against the encapsulation/config benefits.

## Two paths the JVM uses to find code Java can locate classes via two distinct mechanisms, and a single launch can use both at once: - **Classpath** (`-cp` / `-classpath`): the **pre-module** mechanism. A flat, ordered list of JARs/directories. Everything found here is dumped into a single anonymous module called the **unnamed module**, with the old rules: no `requires`, no `exports`, no encapsulation — all public types reachable, classic shadowing applies. - **Modulepath** (`--module-path` / `-p`): the **module-aware** mechanism. Artifacts here are treated as **modules**: their descriptors are read, `requires` edges resolved into a graph, and `exports`/`opens` rules enforced. The choice of path — not the contents of the JAR alone — decides which rules apply. The same JAR behaves differently on the classpath vs the modulepath. ## Three kinds of module 1. **Explicit (named) module** — a JAR with a `module-info.class`, placed on the modulepath. It has an author-defined name, declared dependencies, and a deliberate export surface. This is 'a real module.' 2. **Automatic module** — a *plain* JAR (no module-info.class) placed **on the modulepath**. JPMS automatically promotes it to a module so that explicit modules can `requires` it during migration. 3. **Unnamed module** — *everything on the classpath*. A single catch-all module with no name that **reads all other modules** and **exports all its packages**, but **cannot be named in a `requires`** (named modules cannot depend on the unnamed module). This is where legacy code lives when you have not migrated it. ## Automatic module rules in detail When a plain JAR sits on the modulepath, JPMS fabricates a module descriptor for it with these properties: - **Name derivation:** if the JAR's `META-INF/MANIFEST.MF` declares `Automatic-Module-Name: x.y.z`, that becomes the module name (the stable, recommended approach). Otherwise the name is **derived from the filename** (stripping the version and turning separators into dots) — fragile, because renaming the file changes the module name. - **Exports:** it **exports all of its packages**, so consumers can use anything in it (no encapsulation). - **Reads:** it **requires transitive every other module** — it can read all named modules *and* the unnamed module, which lets an automatic module reach classpath code during mixed migration. Automatic modules are the **migration bridge**: you can start putting your own code in explicit modules while your not-yet-modularized dependencies ride along as automatic modules. ## Why this design (migration strategy) The ecosystem could not modularize overnight. JPMS therefore makes both paths coexist: - **Bottom-up migration:** library authors add module-info (or at least `Automatic-Module-Name`) so consumers can depend on a stable module name. - **Top-down migration:** an application can be modularized while its dependencies remain plain JARs, by placing those dependencies on the modulepath as automatic modules. ## Gotchas worth knowing - **Filename-derived names are unstable** — always prefer `Automatic-Module-Name` so renaming a JAR does not break consumers' `requires`. - **Named modules cannot read the unnamed module**, so once you modularize, every dependency must itself be (at least automatic) on the modulepath, not on the classpath. This sometimes forces a larger migration than expected. - **Split packages across the two paths** still cause errors; an automatic module that splits a package with an explicit module fails resolution. ## Relationship to the core goals The classpath gives up both JPMS goals (no reliable configuration, no strong encapsulation). The modulepath delivers them — fully for explicit modules, partially relaxed for automatic modules (which keep names and dependency edges but waive encapsulation by exporting everything). Understanding which path an artifact is on tells you exactly which guarantees you get.

  • Can a named module depend on code that is on the classpath?
    No. Classpath code lives in the unnamed module, which has no name and cannot be the target of a requires. To depend on it, the dependency must be moved onto the modulepath (becoming an explicit or automatic module).
  • How do you make an automatic module's name stable?
    Add an Automatic-Module-Name entry to the JAR's META-INF/MANIFEST.MF. Without it, the name is derived from the filename and changes if the file is renamed, breaking consumers' requires.

saying these in an interview costs you the question

  • Saying a plain JAR cannot work with modules — on the modulepath it becomes an automatic module
  • Thinking a named module can require classpath (unnamed-module) code — it cannot
  • Assuming filename-derived automatic-module names are stable — use Automatic-Module-Name
  • Confusing the classpath/modulepath choice as a property of the JAR rather than how you launch

context