skip to content

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%

answer

  1. Classpath = flat search list, module path = resolved graph
  2. -cp vs -p/--module-path
  3. Classpath fails late (NoClassDefFoundError); module path fails fast at startup
  4. module-info: name, requires, exports
  5. Split/duplicate packages = silent on classpath, error on module path

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.

solid answer

~40 s

Both tell the JVM where to find compiled code, but they treat it differently. The classpath (-cp) is a flat, ordered search list of JARs and directories; the JVM looks up a class by scanning entries in order, with no notion of who owns or exports a package. The module path (-p / --module-path) holds modules: each carries a module-info that declares its name, what it requires, and what it exports. At startup the JVM runs module resolution from a root set, building a graph and failing fast on missing or duplicate modules and split packages. The module system then enforces strong encapsulation: only exported packages are accessible, and reads must be declared. So the classpath is permissive and late-failing; the module path is structured, validated up front, and access-controlled.

code

java · 11 lines
java
// module-info.java for module com.example.app
module com.example.app {
    requires com.example.util;      // declared dependency
    exports com.example.app.api;    // visible to others; other packages stay hidden
}

// Launching on the classpath (flat, permissive):
//   java -cp app.jar:util.jar com.example.app.Main
//
// Launching on the module path (resolved, enforced):
//   java -p mods -m com.example.app/com.example.app.Main

go deeper

for a junior

Knows the classpath is a list of JARs scanned to find classes and the module path holds modules with declared dependencies; can name the -cp vs -p flags.

for a middle

Explains module resolution, strong encapsulation (exports/requires), and fail-fast vs late failure; understands split-package errors.

for a senior

Reasons about the trade-offs (permissive vs validated), when each is appropriate, and how reflection/opens and automatic modules fit in.

for a principal

Sets organizational policy on adopting JPMS, weighs migration cost vs the architectural integrity it enforces, and understands how the module graph affects layering, runtime images (jlink), and library packaging.

## The problem both solve When the JVM runs, it must find the bytecode for every class your program references. Java offers two mechanisms for telling it where to look: the **classpath** (old, since Java 1.0) and the **module path** (new, since Java 9's Java Platform Module System, JPMS). ## Key terms - **Class**: a compiled `.class` file containing bytecode. - **JAR**: a zip archive of `.class` files and resources. - **Package**: a namespace for classes (e.g. `com.example.util`), mapped to a folder structure. - **Module**: a *named, self-describing* group of packages. A module is just a JAR (or directory) that contains a special compiled file, `module-info.class`, produced from `module-info.java`. - **`module-info.java`**: the module declaration. It states the module's `name`, what other modules it `requires`, and which of its packages it `exports` (makes visible to others). Anything not exported is hidden. ## The classpath You pass it with `-cp` / `-classpath` (or the `CLASSPATH` env var). It is a **flat, ordered list** of JARs and directories separated by `:` (Unix) or `;` (Windows). To resolve a class, the JVM walks the entries in order and uses the first match. Consequences: - **No boundaries**: every public class in every JAR is visible to every other class. There is no concept of a package being "private" to a JAR. - **No declared dependencies**: a JAR does not say what it needs; missing dependencies surface only as `NoClassDefFoundError` / `ClassNotFoundException` *at runtime*, when the missing class is first touched. - **JAR hell**: if two JARs contain the same class, the first on the path silently wins — version conflicts are invisible. ## The module path You pass it with `-p` / `--module-path`. It holds **modules**. At startup the JVM performs **module resolution**: 1. Start from a **root set** (e.g. the module named with `-m`, plus the platform modules it needs). 2. Follow each module's `requires` clauses, pulling in dependencies transitively, building a **module graph**. 3. **Fail fast** on problems: a missing required module, two modules with the same name, or a **split package** (the same package exported by two modules) aborts startup with a clear error — *before* `main` runs. Once resolved, the system enforces **strong encapsulation** and **reliable configuration**: - A package is accessible to another module only if the owner **`exports`** it *and* the consumer **`requires`** the owner. - Reflection into non-exported packages is blocked unless `opens` is declared. ## Side-by-side | | Classpath | Module path | |---|---|---| | Flag | `-cp` | `-p` / `--module-path` | | Structure | flat search list | resolved module graph | | Dependencies | implicit, runtime-discovered | declared in `module-info`, resolved at startup | | Encapsulation | none (all public is open) | only exported packages visible | | Failure timing | late (NoClassDefFoundError) | early (resolution error at launch) | | Duplicate/split packages | silent first-wins | hard error | ## Why it matters The classpath is permissive and forgiving but lets architecture rot (anyone can use anyone's internals) and lets dependency problems hide until runtime. The module path makes the dependency graph explicit and validated, and enforces encapsulation — at the cost of more up-front declaration and a migration effort. Both can be used together (a question of its own).

  • When does a missing dependency surface on each path?
    On the classpath, only at runtime when the absent class is first referenced (NoClassDefFoundError/ClassNotFoundException). On the module path, at startup during resolution, before main runs.
  • If a JAR has no module-info, can it go on the module path?
    Yes — it becomes an automatic module (its name derived from the JAR/Automatic-Module-Name), requiring all other modules and exporting all packages. It's a migration bridge, not a real module.

The classpath is a shared open-plan office: anyone can walk to anyone's desk and grab any file. The module path is a building with named offices, locked doors, and a directory: you can only enter rooms that were declared open to you, and the floor plan is checked before anyone moves in.

saying these in an interview costs you the question

  • Saying the module path is just a renamed classpath — it adds resolution and encapsulation, not just a new flag
  • Claiming missing dependencies on the classpath fail at startup (they fail at runtime)
  • Believing all public classes are accessible across modules (only exported packages are)
  • Thinking duplicate classes on the module path are tolerated like classpath first-wins

context