Introduction to Java Modules: A Paradigm Shift in Application Development
By Rohan Vats
Updated on May 28, 2025 | 21 min read | 18.28K+ views
Share:
For working professionals
For fresh graduates
More
By Rohan Vats
Updated on May 28, 2025 | 21 min read | 18.28K+ views
Share:
Table of Contents
Ever wonder how leading tech companies streamline their massive Java-based systems to ensure crystal-clear dependencies and unparalleled reliability? Most of the answers lie in Java Modules, introduced in Java 9. Their impact extends beyond code organization as Java modules fundamentally restructure application construction for a more robust and predictable outcome. |
Java Modules, introduced in Java 9, offer a powerful solution to the challenges of managing complex Java applications. They resolve "JAR Hell" by enforcing strict module boundaries and explicit dependencies, unlike the loose dependency model of traditional JARs.
This fundamental shift enhances reliability by preventing accidental access to internal APIs, improves scalability through more efficient memory usage and faster startup times, and strengthens security by allowing for fine-grained control over what code is exposed. The result is more maintainable and predictable systems that are easier to build and deploy.
This blog explores the core concepts behind Java 9 Module construction and runtime, guiding you through the practical steps of building and launching modular Java projects.
Don't just learn Java Modules, truly excel at building robust, predictable software. Explore Online Software Development Courses from top universities to unlock your full potential. Master the most in-demand technologies and languages that leading companies use, ensuring your skills are current and highly marketable. Enroll today and transform your career as a full-stack engineer.
This section introduces the fundamental building blocks of the Java Platform Module System (JPMS). We'll explain what a module truly is and elaborate on the key declarations and mechanisms that govern its behavior.
At its core, a Java module is a named, self-describing collection of code (packages and classes) and data (resources) that explicitly declares its dependencies on other modules and defines which packages are accessible to other modules. This structure represents a significant evolution beyond traditional JARs, which often suffered from weak encapsulation and ambiguous dependencies.
This strong encapsulation and explicit dependency management are central to overcoming the classpath issues and promoting more reliable and maintainable applications. During runtime, the Java Virtual Machine (JVM) uses these declarations to construct a module graph, rigorously verifying all dependencies and failing fast if any are missing or unsatisfiable.
Are your Java applications growing increasingly complex? upGrad's Software and Tech Courses go beyond basic modularity, empowering you to architect robust, future-proof solutions. Master practical, industry-ready skills designed to tackle real-world challenges with these courses:
Let us now learn about the fundamental declarations and mechanisms that define a module's behavior and interactions within the Java ecosystem.
1. Module Declaration (module-info.java): The heart of any Java module is its module descriptor file, module-info.java. This file, located in the module's root directory, acts as its manifest, defining its name, readability, and accessibility rules.
// Example module-info.java
module com.example.mymodule {
// Module declarations go here
}
2. Module Name: Every module must have a unique name. This name is crucial for the module system to identify and resolve dependencies. Best practice suggests using a reverse-domain name convention (e.g., com.example.mymodule) to ensure uniqueness and avoid conflicts.
3 . Requiring Dependencies (requires): Java 9 modules explicitly declare their dependencies on other modules using the requires keyword within module-info.java. This makes the module system aware of the module graph, ensuring all necessary modules are present at runtime.
module com.example.mymodule {
requires java.base; // Declares a dependency on the base Java module
requires com.example.anothermodule; // Depends on a custom module
}
Also Read: 15 Essential Java Full Stack Developer Skills in 2025
Example: Logging libraries or test frameworks may be required during development but not shipped in production.
4. Exporting and Opening Packages (exports, opens): All packages within a module are encapsulated by default. To make packages accessible to other modules, they must be explicitly exports or opens.
module com.example.mymodule {
exports com.example.mymodule.api; // Public API for other modules
opens com.example.mymodule.internal; // Allows reflection on internal package
}
5. Qualified Exports/Opens (exports ... to, opens ... to): Sometimes, you don't want to export a package to all modules but only to specific ones. Qualified exports/opens allow you to specify a comma-separated list of target module names. This provides fine-grained control over visibility, preventing unintended dependencies on internal APIs. Use this when exposing internal APIs to trusted plugins without polluting the public module interface.
module com.example.core {
exports com.example.core.internal.spi to com.example.plugin.loader; // Only plugin.loader can access
}
Also Read: Why is Java Platform Independent Language-A Detailed Guide
6. Using and Providing Services (uses, provides): The service mechanism in JPMS facilitates the creation of pluggable architectures where modules can use (discover) implementations of a service interface and provide (implement) a service.
// Module com.example.serviceuser
module com.example.serviceuser {
uses com.example.service.MyService; // Declares intent to use MyService
}
// Module com.example.serviceprovider
module com.example.serviceprovider {
provides com.example.service.MyService with com.example.serviceprovider.MyServiceImpl; // Provides an implementation
}
Other Important Considerations:
To build truly intelligent and data-backed applications, you also need to enhance your analytical toolkit. Explore our Hypothesis Testing Crash Course – master A/B testing and make data-driven decisions. Elevate your entire skill set today!
Also Read: 50 Java Projects With Source Code in 2025: From Beginner to Advanced
With a solid understanding of Java 9 Modules, the next logical step is to see them in action. We'll now walk through Building and Launching Modular Java Programs.
You've learned the theory behind Java Modules; now let's get practical. This section cuts through the complexity, showing you exactly how to compile, package, and run your modular Java applications. We'll understand command-line tools and the different steps involved, turning concepts into actionable knowledge so you can confidently bring your modular projects to life.
The Java 9 Module System, or Project Jigsaw, was a monumental release that fundamentally re-architected the Java platform into interconnected modules. This overhaul brought strong encapsulation and explicit dependencies, significantly improving security and performance by enabling leaner, more focused application runtimes.
It specifically aimed to dismantle long-standing issues like "JAR Hell" and circumvent the "classpath dilemma," allowing developers to build more reliable, scalable, and maintainable applications. Java Platform Module System offers a blueprint for robust software architecture by self-contained components and clearly defining their interactions.
While Java Modules revolutionize application architecture, the future of development is increasingly intelligent. Equip yourself with the Generative AI Mastery Certificate for Data Analysis for tomorrow's challenges. Learn to integrate AI into your robust datasets. Start building truly smart solutions today!
Also Read: Top 32 Exception Handling Interview Questions and Answers in 2025 [For Freshers & Experienced]
You've explored the "why" behind Java Modules; now let's dive into the "how." Before we compile and run, we'll set up a simple modular project, understanding the essential file structure and module-info.java declarations that make your application modular.
Here, you'll find step-by-step instructions on creating your module-info.java file and compiling your modular Java source code. This will demonstrate the practical application of the exports and requires directives, culminating in a ready-to-run modular JAR.
To create a Java module, you define its module-info.java descriptor and compile your source code with the Java compiler, javac, specifically instructing it about your module.
Step 1: Project Structure: Organize your source code into a directory structure that reflects your module. Each module should have its root directory, and inside that, typically a src folder containing your module-info.java and package directories.
my-modular-project/
├── my.module.name/
│ ├── src/
│ │ ├── module-info.java
│ │ └── com/
│ │ └── example/
│ │ └── MyClass.java
└── out/
Step 2: Create module-info.java : Inside your module's src directory, create the module-info.java file. This is where you declare your module's name, its dependencies, and which packages it exports.
// my.module.name/src/module-info.java
module my.module.name {
// We export com.example so other modules can use its public classes
exports com.example;
// If MyClass needed types from java.base (like String, System), we'd require it
// requires java.base; //
}
Explanation:
Step 3: Write Your Java Code: Create your regular Java source files within their respective packages.
// my.module.name/src/com/example/MyClass.java
package com.example;
public class MyClass {
public String getGreeting() {
return "Hello from MyClass in a module!";
}
}
Step 4: Compile the Module: Use the javac command. The key difference from traditional compilation is the use of the --module-source-path option to tell javac where to find your module source files, and --module to specify the module you are compiling. This creates compiled class files in a designated output directory.
# Navigate to the project root: my-modular-project/
mkdir -p out/my.module.name # Create output directory for the module
javac --module-source-path my.module.name/src \
-d out \
my.module.name/src/module-info.java \
my.module.name/src/com/example/MyClass.java
Explanation:
Note: javac places the output in a directory matching the module name under ‘out’ automatically.
After compilation, your out directory will contain the compiled module:
out/
├── my.module.name/
│ ├── module-info.class
│ └── com/
│ └── example/
│ └── MyClass.class
While Java Modules streamline advanced application development, every powerful solution starts with strong foundational coding. Build your core programming skills with our Learn Basic Python Programming course. Master Python, Matplotlib, and coding fundamentals in just 5 hours! Join 43k+ learners and kickstart your coding journey today!
You've successfully compiled your first modular Java code. To truly understand the power of the Java Module System, we'll now explore the prominent Java Module Construction and Runtime concepts.
This section will shift from the detailed syntax of module declarations to modular Java's broader architectural and runtime aspects. We'll explore the fundamental mechanisms that enable modular applications to be packaged, discovered, and executed, focusing on how modules interact with the Java Virtual Machine and each other during the build and launch phases.
Specifically, we'll delve into the module path, module resolution, and the creation of custom runtime images using jlink, giving you the tools to optimize deployment and ensure robust application execution.
Modular JARs are essentially regular .jar files with a twist: they contain a compiled module-info.class file at their root. This file, derived from your module-info.java source, acts as a module descriptor, providing the Java runtime with all the necessary information about the module – its name, dependencies, and exported packages. Crucially, the module name defined in module-info.class must match the actual module name used for resolution on the module path.
When the JVM encounters a JAR with this descriptor on the module path, it treats it as a well-defined module, adhering to strong encapsulation and explicit dependency rules. This packaging ensures that modular applications are compact and efficient, as only the necessary components are included.
The distinction between the module path and the classpath is fundamental in modular Java.
The module path ensures that the JPMS can build a reliable and predictable module graph at startup, significantly reducing runtime errors caused by classpath issues.
Feature | Classpath (Legacy) | Module Path (JPMS) |
Encapsulation | Weak (all public types are visible) | Strong (only exports packages are visible) |
Dependencies | Implicit (relies on order, "JAR Hell") | Explicit (requires declarations) |
Package Rules | Split packages allowed (same package in multiple JARs) | No split packages allowed (single module owns the package) |
Resolution | Flat search, first-come, first-served | Graph-based, strict resolution |
Runtime Issues | Common (e.g., NoClassDefFoundError) | Minimized by early detection |
Default Module | Unnamed Module | Named Modules, Automatic Modules |
When a modular Java application starts, the Java Virtual Machine doesn't just blindly load JARs. Instead, it engages in a sophisticated process called module resolution. This involves:
If any of these checks fail, the application will not start, providing early detection of configuration issues that would have previously led to mysterious runtime errors. This robust resolution process is a cornerstone of JPMS's reliability.
The java.base module is the bedrock of the Java Platform Module System. It's the fundamental module that every other module implicitly requires, even if you don't explicitly add requires java.base; in your module-info.java file (though it's good practice for clarity). This module contains the most basic and essential functionalities of the Java SE Platform, including:
Its implicit requirement ensures that all modules access these core building blocks without redundant declarations, making it the unavoidable root of every module graph.
Prior to Java 9, deploying a Java application meant bundling your code with a full JDK or JRE installation, which could be quite large. The jlink tool, introduced with JPMS, changes this. It analyzes your application's module graph (including your application modules and the JDK modules it depends on).
Then, it creates a custom runtime image that includes only the required JVM parts (like Java.base, Java.desktop, etc.). jlink can consume modules packaged as modular JARs or the JDK's own .jmod files.
Benefits of jlink:
You typically use the jlink command-line tool, specifying your application's modules and the desired output directory. It then analyzes your module graph and constructs the runtime.
# Example jlink command
jlink --module-path <path-to-your-modules>:<path-to-jdk-modules> \
--add-modules <your-main-module>,<other-needed-modules> \
--output my-modular-app-runtime
This effectively "links" your application with a minimal, optimized JVM, making deployment much more efficient and secure. This is a game-changer for many modern Java deployments.
Beyond the use of Java Modules for robust application architecture, the future of software development is deeply integrated with AI. Elevate your skills to build intelligent applications with the Microsoft Gen AI Mastery Certificate for Software Development. Learn to leverage new-age Generative AI tools and build solutions for tomorrow.
Also Read: Types of Variables in Java: Java Variables Explained
Having explored the advanced technicalities of Java Modules, including packaging, runtime behavior, and image creation, it's natural to consider their real-world impact.
This section provides a balanced perspective on Java Modules, first highlighting the compelling advantages driving their adoption in modern application development, then candidly addressing the common hurdles and considerations developers may face during implementation.
Java Modules offer a significant leap forward in application design, moving beyond the limitations of the traditional classpath. They enforce robust boundaries, streamline dependencies, and enable optimized runtimes, fundamentally changing how we build scalable and maintainable Java software. Understanding these core advantages clarifies why embracing modularity is a strategic decision for modern development.
Benefit | Description | Impact |
Strong Encapsulation | Hides internals, only exporting public APIs. | Allows teams to build independent components with clear API boundaries, reducing accidental misuse and fostering robust, secure applications. |
Reliable Configuration | Validates dependencies at build and runtime. | Developers gain confidence their applications will start predictably, eliminating "JAR Hell" and leading to fewer debugging hours. |
Scalable Platform | Create custom, minimal JVM runtimes (jlink). | Operations teams deploy significantly smaller application bundles, reducing disk space, speeding up transfers, and enabling quicker startup in containerized environments. |
Improved Performance | JPMS restricts class loading to declared dependencies only, speeding up application startup and reducing memory overhead | Developers experience faster feedback loops; in production, this translates to more efficient resource utilization and a snappier user experience. |
Easier Parallel Dev. | Clear APIs enable independent teamwork. | Development teams can work more independently, accelerating development cycles as changes in one module are less likely to inadvertently break another. |
While powerful, adopting Java Modules comes with its own set of challenges. This sub-section will address common hurdles and provide insights into navigating them effectively. Here's a concise table summarizing the challenges for Developers and Architects when adopting the Java Platform Module System (JPMS):
Category | Challenge for Developers | Challenge for Architects |
Learning Curve | Understanding new JPMS concepts (module-info.java, exports, requires, etc.) | Designing truly modular architectures with clear module boundaries |
Legacy Code | Refactoring monolithic applications; resolving strong encapsulation and cyclic dependency issues | Guiding the migration of existing codebases; ensuring architectural consistency during refactoring |
Dependencies | Integrating non-modular third-party libraries; managing --add-exports/--add-opens flags | Evaluating ecosystem maturity for JPMS; establishing dependency governance within the module graph |
Build & Tooling | Adjusting build scripts (Maven/Gradle); ensuring IDE compatibility | Defining deployment strategies (e.g., jlink custom runtimes); ensuring tooling supports architectural vision |
Runtime Issues | Dealing with reflection limitations, managing mixed classpath/module path scenarios | Adapting architectural patterns to JPMS (e.g., cross-cutting concerns); balancing encapsulation with practical needs |
Design & Ops | Granularity decisions for modules, ensuring code conforms to explicit module contracts | Ensuring prescriptive architecture matches descriptive, managing module evolution and versioning |
Also Read: Python vs Java: Mastering the Best Language For Your Career
To expand your Java expertise and leverage modularity effectively, focus on hands-on practice with JPMS tooling like jlink and dive into real-world modular project structures. Continuously experimenting with module declarations and understanding error messages will solidify your grasp on this powerful paradigm.
Understanding modern Java development can be overwhelming. Without structured guidance, grasping advanced concepts like Java Modules and becoming a versatile full-stack engineer often feels like an uphill battle. upGrad bridges this gap, providing industry-aligned tech courses to equip you with in-demand skills.
Here are some additional courses that can further enhance your development skills:
Still struggling to find the right Java software development course to match your goals in 2025? Reach out to upGrad for personalized counseling and valuable insights, or visit your nearest upGrad offline center for more details.
Boost your career with our popular Software Engineering courses, offering hands-on training and expert guidance to turn you into a skilled software developer.
Master in-demand Software Development skills like coding, system design, DevOps, and agile methodologies to excel in today’s competitive tech industry
Stay informed with our widely-read Software Development articles, covering everything from coding techniques to the latest advancements in software engineering.
408 articles published
Software Engineering Manager @ upGrad. Passionate about building large scale web apps with delightful experiences. In pursuit of transforming engineers into leaders.
Get Free Consultation
By submitting, I accept the T&C and
Privacy Policy
India’s #1 Tech University
Executive PG Certification in AI-Powered Full Stack Development
77%
seats filled
Top Resources