Java 9 Modules与Package、Jar的区别及引入原因解析
Great question—this is a common point of confusion for developers who’ve been using Java pre-9. Let’s unpack the problem first, then compare Modules to packages/JARs, and finally address the Python/Node.js comparison.
The Pain Points of Packages and JARs
Before Modules, Java’s code organization had several critical flaws that held back scalability and maintainability:
- Classpath Hell: JARs on the classpath were treated as a single flat namespace. If two JARs contained classes with the same fully qualified name, the first one loaded won out—leading to endless dependency conflicts (looking at you,
commons-loggingvsslf4j). - No Explicit Dependencies: There was no way to declare "this code depends on that JAR". You had to manually manage classpaths, and runtime errors from missing dependencies were far too common.
- Weak Encapsulation: Any
publicclass in any package was accessible to every other class on the classpath. You couldn’t truly hide internal implementation details—even package-private access could be bypassed easily with reflection. - Bloated JDK: The JRE was a monolith; even simple command-line apps had to carry the entire runtime, which was a nightmare for small, resource-constrained environments like IoT devices.
Modules vs. Packages vs. JARs
Let’s break down the core differences clearly:
Packages
- Purpose: The smallest unit of code organization within a single codebase. They group related classes to avoid naming collisions and organize code logically.
- Limitations: No cross-package access control beyond basic
public/package-privatemodifiers, no dependency tracking, and no way to hide packages from external code. They’re purely a code-structuring tool, not a modularity mechanism.
JARs
- Purpose: A packaging format that bundles multiple packages, resources, and metadata into a single file for easy distribution.
- Limitations: JARs are essentially "zip files with a manifest"—they don’t declare dependencies, don’t enforce encapsulation, and don’t solve classpath conflicts. They’re a distribution tool, not a modularity solution.
Modules (Java Platform Module System, JPMS)
- Purpose: A system-level modularity mechanism that adds a higher layer of abstraction above packages and JARs. Each module is a self-contained unit defined by a
module-info.javafile that explicitly declares:- The module’s unique name (e.g.,
com.example.myapp) - Dependencies on other modules (
requires com.google.gson) - Which packages are exposed to other modules (
exports com.example.myapi) - Which packages are accessible via reflection (
opens com.example.internal to com.fasterxml.jackson.databind)
- The module’s unique name (e.g.,
- Key Advantages:
- Strong Encapsulation: Only exported packages are visible to other modules—internal implementation packages are truly hidden, even from reflection (unless explicitly opened).
- Explicit Dependencies: The JVM validates that all required modules are present at startup, eliminating runtime
ClassNotFoundExceptionfrom missing dependencies. - Classpath Hell Mitigation: Modules have unique names, so two modules with overlapping package names don’t conflict (as long as they’re on the module path instead of the classpath).
- Modular JDK: The JDK itself is split into modules (e.g.,
java.base,java.sql,java.desktop), allowing you to create minimal runtime images withjlinkthat only include the modules your app needs.
Did Java Copy Python/Node.js Module Systems?
Short answer: No, but it shares some core goals.
Python and Node.js have had module systems (Python’s import/pip, Node.js’s CommonJS/ES Modules) for years, focused on explicit dependency declaration, encapsulation of internal code, and avoiding naming conflicts.
JPMS was designed to solve Java’s specific pain points (classpath hell, JRE bloat, weak encapsulation) while maintaining strict backward compatibility with pre-9 code. Unlike Python/Node.js modules (which are file-level or package-level and often dynamic), JPMS is a static, JVM-level system that integrates tightly with Java’s type system and compilation process.
A key difference is backward compatibility: Java allows modular and non-modular code to coexist (non-modular code runs in the "unnamed module"). Python and Node.js didn’t have to support decades of legacy code, so their module systems could be more opinionated from the start.
内容的提问来源于stack exchange,提问作者JavaUser

