Classpath vs Module Path in Java
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
| Point | Class path | Module path |
|---|---|---|
| Option | -cp or --class-path | -p or --module-path |
| Contains | Folders and JARs of classes | Folders and JARs of modules |
| Structure | One flat list; every public class can see every other | A graph: each module sees only what it requires and what is exported to it |
| Missing dependency | Found when the class is first used: NoClassDefFoundError | Found at startup: the JVM refuses to run |
| Same package in two JARs | Allowed; the first one found wins, silently | An error |
| Encapsulation | None beyond public/private | Non-exported packages are hidden |
| Reflection on private members | Allowed with setAccessible(true) | Only for opened packages |
| How to run | java -cp app.jar com.school.Main | java -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:
| JAR | On the class path | On the module path |
|---|---|---|
Has module-info.class (modular JAR) | Part of the unnamed module; its module-info is ignored | A named module, with the encapsulation it declares |
No module-info.class (plain JAR) | Part of the unnamed module | An automatic module |
| Kind | Name | Exports | Can read |
|---|---|---|---|
| Named module | From module-info | Only what it declares | Only what it requires |
| Automatic module | From the manifest entry Automatic-Module-Name, or derived from the JAR file name | Every package | Every other module, and the unnamed module |
| Unnamed module | None | Every package, but named modules can't require it | Every module |
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:
java --module-path mods -m expenses.appBy category: {Food=649.75, Travel=1200}
Total: 1849.75
Running in module: expenses.appOn the class path, the same two JARs are treated as plain JARs:
java -cp mods/expenses.app.jar:mods/expenses.core.jar \
com.poolstring.expenses.app.MainThe 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:
jdeps --module-path mods -s mods/expenses.app.jarexpenses.app -> expenses.core
expenses.app -> java.baseFor 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:
| Step | What you do |
|---|---|
| 1. Stay on the class path | Everything is in the unnamed module. Nothing to change. |
| 2. Find the obstacles | Run jdeps to see dependencies, split packages and use of JDK internals. |
| 3. Modularize your own code | Add module-info.java; put third-party JARs on the module path, where they become automatic modules. |
| 4. Replace automatic modules over time | As 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
- Easy: Run the expense tracker both ways and note the difference in the "Running in module" line.
- Harder: Run
jdeps -son a JAR from a project you use, and list the JDK modules it depends on.
Key Takeaways
Was this tutorial helpful?