Classpath vs Module Path in Java

Naveen AnamaneniUpdated · 7 min read

Java can find compiled code in two ways. The class path is the original mechanism: a flat list of folders and JAR files that are searched, in order, for each class. The module path, added in Java 9, is a list of locations that contain modules, which Java resolves as a graph of declared dependencies before the program starts. The same JAR can behave differently depending on which path you put it on. In this lesson, you will learn how the two paths differ, what the unnamed module and automatic modules are, and how an existing application can move toward modules step by step.

"Classpath in Java" covers the class path itself; this lesson compares it with the module path.

The Comparison

PointClass pathModule path
Option-cp or --class-path-p or --module-path
ContainsFolders and JARs of classesFolders and JARs of modules
StructureOne flat list; every public class can see every otherA graph: each module sees only what it requires and what is exported to it
Missing dependencyFound when the class is first used: NoClassDefFoundErrorFound at startup: the JVM refuses to run
Same package in two JARsAllowed; the first one found wins, silentlyAn error
EncapsulationNone beyond public/privateNon-exported packages are hidden
Reflection on private membersAllowed with setAccessible(true)Only for opened packages
How to runjava -cp app.jar com.school.Mainjava -p mods -m expenses.app/com.poolstring.expenses.app.Main

Three Kinds of Module at Run Time

Where a JAR is placed decides what it becomes:

JAROn the class pathOn the module path
Has module-info.class (modular JAR)Part of the unnamed module; its module-info is ignoredA named module, with the encapsulation it declares
No module-info.class (plain JAR)Part of the unnamed moduleAn automatic module
KindNameExportsCan read
Named moduleFrom module-infoOnly what it declaresOnly what it requires
Automatic moduleFrom the manifest entry Automatic-Module-Name, or derived from the JAR file nameEvery packageEvery other module, and the unnamed module
Unnamed moduleNoneEvery package, but named modules can't require itEvery module
A flat pool of classes, or a graph of modules

The key rule: a named module can't depend on the unnamed module. Code in a real module can't use classes that are only on the class path. That is why automatic modules exist: they let a named module requires a library that hasn't been modularized yet.

Example: The Same Code, Both Ways

The expense tracker from "Java Platform Module System (JPMS)" can be run from either path.

On the module path, as modules:

Terminal
java --module-path mods -m expenses.app
Terminal output
By category: {Food=649.75, Travel=1200}
Total: 1849.75
Running in module: expenses.app

On the class path, the same two JARs are treated as plain JARs:

Terminal
java -cp mods/expenses.app.jar:mods/expenses.core.jar \
    com.poolstring.expenses.app.Main

The first two lines of output are the same, but the last line reports Running in module: null: the class is now in the unnamed module, the module descriptors are ignored, and the internal package is reachable by anyone again.

(On Windows, the separator between class path entries is ; instead of :.)

Finding Out What a JAR Needs

The jdeps tool analyzes dependencies:

Terminal
jdeps --module-path mods -s mods/expenses.app.jar
Terminal output
expenses.app -> expenses.core
expenses.app -> java.base

For a non-modular JAR, jdeps --generate-module-info can even write a first draft of a module-info.java.

Moving an Application to Modules

You don't have to convert everything at once:

StepWhat you do
1. Stay on the class pathEverything is in the unnamed module. Nothing to change.
2. Find the obstaclesRun jdeps to see dependencies, split packages and use of JDK internals.
3. Modularize your own codeAdd module-info.java; put third-party JARs on the module path, where they become automatic modules.
4. Replace automatic modules over timeAs libraries publish real modules, your requires lines keep working.

When a module must reach into JDK internals or another module's private parts during a migration, the command-line options --add-exports, --add-opens and --add-reads grant access from outside. They are temporary measures, not a design.

Common Mistakes

Expecting module rules to apply on the class path. There, module-info is ignored.

A named module depending on class path code. It can't; move that JAR to the module path as an automatic module.

Relying on automatic module names derived from file names. my-lib-2.1.jar becomes my.lib; a renamed file changes the name.

The same package in two JARs on the module path. Split packages are rejected.

Interview Corner

What is the difference between the class path and the module path?

The class path is a flat list of directories and JARs searched in order for classes; all public types are accessible to all code, missing classes are discovered when first used, and duplicate packages silently shadow each other. The module path holds modules, which the JVM resolves into a dependency graph at startup; only exported packages are accessible, missing or conflicting modules prevent startup, and split packages are errors. Code on the class path belongs to the unnamed module; a plain JAR on the module path becomes an automatic module that exports everything; a JAR with module-info.class on the module path is a named module.

What is an automatic module?

A non-modular JAR placed on the module path. It becomes a named module whose name comes from the Automatic-Module-Name manifest attribute or is derived from the JAR file name. It exports and opens all its packages and can read all other modules, including the unnamed module. Automatic modules let modular code depend on libraries that haven't been modularized yet.

Practice

  1. Easy: Run the expense tracker both ways and note the difference in the "Running in module" line.
  2. Harder: Run jdeps -s on a JAR from a project you use, and list the JDK modules it depends on.

Quick check: Class path and module path

4 questions
1When is a missing dependency detected on the module path?
2What does a plain JAR become on the module path?
3Where does class path code live?
4Can a named module require code that is only on the class path?

Key Takeaways

Was this tutorial helpful?

Lesson 516 of 578