How Java Modules Are Transforming Application Development!
By Rohan Vats
Updated on Jul 02, 2025 | 21 min read | 18.43K+ views
Share:
For working professionals
For fresh graduates
More
By Rohan Vats
Updated on Jul 02, 2025 | 21 min read | 18.43K+ views
Share:
Table of Contents
Did you know? Before Java 9, the JDK was a monolith. It was one massive package, and you couldn’t pick and choose the parts you needed. Java 9 introduced a module system to break this monolith into smaller, more manageable pieces. |
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 construction and runtime of Java Modules. It will guide 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 learn the most in-demand technologies and languages that leading companies use.
The Java Module System, introduced in Java 9, enhances the Java ecosystem by enabling modularity within the platform. It complements existing features like the classpath and JVM by offering improved encapsulation, dependency management, and runtime optimization.
Java modules allow applications to be more organized, maintainable, and scalable, while reducing memory usage and startup times. This system integrates seamlessly with Java’s principles of robustness and platform independence. It modernizes how complex systems are built and managed.
In 2025, professionals who can use advanced Java programming techniques to streamline business operations will be in high demand. If you're looking to develop skills in programming, here are some top-rated courses to help you get there:
At its core, a Java module is a named, self-describing collection of code (packages and classes) and data (resources). It 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.
Also Read: Java Tutorial: Learn Java Programming From Scratch For Beginners
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. It significantly improves 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." This allows 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.
Also Read: Top 32 Exception Handling Interview Questions and Answers in 2025 [For Freshers & Experienced]
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 required 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
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:
1. Automatic Modules: When you place a traditional JAR file (without a module-info.java) on the module path, the Java Module System treats it as an "automatic module."
They may also introduce split-package errors if the same package exists in multiple automatic modules.
2. Unnamed Module: Any classes loaded from the traditional classpath (not the module path) belong to the "unnamed module."
This mechanism primarily exists for backward compatibility, allowing legacy code to run on a modular JVM. This enforces modular integrity. Legacy code should be gradually migrated to named modules to participate fully in JPMS.
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.
Building and Launching Modular Java Programs involves dividing your application into self-contained modules, each with specific functionality. By defining modules with a module-info.java descriptor, you ensure clear dependencies, exports, and access controls.
During launch, the Java Module System optimizes performance by loading only necessary modules, improving security and reducing application size. This modular approach enhances development, deployment, and overall efficiency, making the program more maintainable and scalable.
To create a module in Java, start by defining a module-info.java file. Here, you declare the module’s name, list its dependencies with requires, and specify accessible packages using exports. Organize your classes and packages within the module directory for proper encapsulation.
Compile the module using the javac command with the --module-path flag to include dependencies, and run it with the java command. Specify the --module-path and --module options. This modular structure enhances code organization, maintainability, and scalability.
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
You can get a better hang of Java with upGrad’s free Core Java Basics course. It covers variables, data types, loops, and OOP principles to build strong coding skills. Perfect for aspiring developers, students, and professionals transitioning to Java.
Also Read: Java Language History: Key Milestones and Development
To truly understand the power of the Java Module System, we'll now explore the prominent Java Module Construction and Runtime concepts.
Advanced concepts in Java module construction and runtime focus on optimizing modularity, scalability, and performance. Key concepts include managing module dependencies for flexibility, using access control mechanisms like exports and opens for secure encapsulation. It includes configuring a custom module path for efficient module organization.
The jlink tool enables the creation of streamlined modular runtime images, reducing application size. Additionally, dynamic module resolution supports flexible module interactions, crucial for building scalable, secure, and efficient Java applications.
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.
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.
Although modularity improves maintainability and scalability, it can create integration challenges. For instance, modularizing an e-commerce platform allows independent updates to components, boosting performance by loading only essential modules.
However, integrating legacy systems can be tricky, as their non-modular design complicates dependency management and module descriptor maintenance. It requires careful planning for smooth integration.
Here are the common benefits of Java modules:
Benefits |
Details |
Encapsulation | Modules control which packages are exposed, reducing the risk of other parts of the codebase accessing internal logic by mistake. |
Dependency Clarity | Each module declares exactly what it needs and what it offers, making dependencies easier to track and maintain. |
Improved Security | By exposing only required APIs, modules reduce the chances of unauthorized access to sensitive parts of your code. |
Performance Boost | The module system can eliminate unused code at runtime, leading to faster execution and reduced memory usage. |
Organized Codebase | Applications can be split into logical modules, improving structure, readability, and maintainability. |
Next let’s skim though the limitations of Java modules.
Limitations |
Details |
Learning Overhead | Developers new to Java modules may find it tricky to understand syntax, rules, and best practices. |
Legacy Integration | Older libraries not designed with modules in mind might need adjustments or rewrites to fit in. |
Scaling Complexity | Large projects can become harder to manage due to additional planning required for module boundaries. |
Tool Compatibility | Some build tools, plugins, or IDEs may have incomplete or limited support for the module system. |
Build Performance | Modular builds can take longer, especially in large applications, due to extra resolution and validation steps. |
Also Read: Python vs Java: Mastering the Best Language For Your Career
Next, let’s look at how upGrad can help you improve your understanding of Java programming.
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.
However, understanding modern Java development can be overwhelming. Without structured guidance, grasping advanced concepts like Java Modules 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.
Reference:
https://medium.com/@pvprasanth474/whats-new-in-java-9-the-top-4-features-you-should-know-cd2578ca1ed1
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