Few new Features of Java 9

🔹 Strong Encapsulation
Before Java 9:
If a class was
public
, anyone on the classpath could use it — even if it was meant to be internal.Libraries often had “public by accident” APIs because Java had no way to hide internals beyond package-private.
Even JDK internals (like
sun.misc.Unsafe
) were widely used by accident.
With the Java Platform Module System (JPMS):
Each module declares in
module-info.java
which packages itexports
.Only exported packages are accessible outside the module.
Example:module com.example.foo { exports com.example.foo.api; // visible to other modules // no export for com.example.foo.internal → hidden }
Everything else remains strongly encapsulated — you can’t import or reflectively access it without explicit
opens
.The JDK itself is modularized: internal packages are strongly hidden unless you use
--add-opens
at runtime.
Benefit: You can design a clear API surface (what’s public) while keeping implementation details hidden.
🔹 What is module-info.java
?
It’s a special descriptor file that lives in the root of a Java module (like
src/main/java/
module-info.java
).Think of it as the manifest for a module: it declares the module’s name, its dependencies, and what packages it makes available to the outside world.
Example:
module com.yahoo.identity.umapi {
requires java.sql; // depends on JDK module
requires com.fasterxml.jackson; // depends on another module
exports com.yahoo.identity.umapi.services; // make this package visible to others
exports com.yahoo.identity.umapi.providers; // make this package visible to others
}
🔹 Why was it introduced?
Before Java 9:
All classes were thrown onto the classpath.
No strong boundaries — any code could access any public class, even internal ones.
JDK itself was a giant monolith (
rt.jar
).
With modules:
Stronger encapsulation: Only exported packages are visible to other modules.
Reliable configuration: Compiler/runtime knows exact dependencies.
Smaller runtimes: Tools like
jlink
can assemble minimal JREs with just the modules you need.Better readability: The graph of dependencies is explicit.
🔹 Structure of module-info.java
Module declaration
module com.example.myapp { }
Requires (dependencies)
requires java.logging; requires com.fasterxml.jackson.databind;
Exports (make packages visible to other modules)
exports com.example.myapp.api; // open to everyone
Opens (for reflection — e.g., Hibernate/Jackson)
opens com.example.myapp.model; // allow deep reflection on this package
Permits sealed types across packages
Without modules, sealed subclasses must be in the same package.
With modules, sealed subclasses just need to be in the same module.
That’s whymodule-info.java
matters for your case (JaxRsService
sealed interface +RestEasyProvider
).
🔹 Benefits in large projects
Helps prevent “classpath hell” (accidental collisions).
Makes APIs cleaner by hiding internal packages (only export what you want public).
Lets you ship smaller, faster JREs tuned for microservices (
jlink
).
🔹 What jlink
Does
jlink
(introduced in Java 9) is a tool for creating a custom Java runtime image that contains:only the modules your application actually needs,
a slimmed-down JVM,
and an optional launcher script for your app.
Instead of shipping a giant JDK/JRE (hundreds of MBs, with lots of unused modules), you ship a trimmed runtime (sometimes as small as ~30 MB).
🔹 Why It Matters
Smaller Footprint
Ideal for Docker/microservices: reduces image size, faster deployments.
Example: If your app doesn’t need
java.desktop
, you leave it out.
Security
Less surface area → fewer unneeded APIs available for attackers.
Only exports the modules you explicitly added.
Performance
- Startup can be faster because there are fewer modules to scan and initialize.
Deployment Simplicity
You don’t depend on whatever JRE is installed on the server.
The runtime is self-contained and predictable.
🔹 How It Works (Step by Step)
1. Create/compile your modular JAR
Your app should have a module-info.java
. Example:
module com.example.myapp {
requires java.sql;
exports com.example.myapp.api;
}
Compile:
javac -d out --module-source-path src $(find src -name "*.java")
jar --create --file myapp.jar -C out/com.example.myapp .
2. Analyze dependencies
Use jdeps
to find what modules your app really uses:
jdeps --print-module-deps myapp.jar
# Example output: java.base,java.sql
3. Run jlink
jlink \
--module-path $JAVA_HOME/jmods:out \
--add-modules com.example.myapp,java.base,java.sql \
--launcher runapp=com.example.myapp/com.example.myapp.Main \
--output myruntime \
--strip-debug --compress=2 --no-header-files --no-man-pages
--module-path
→ location of JDK modules + your app modules--add-modules
→ modules to include--launcher
→ creates a startup script--output
→ target folderOptions like
--strip-debug
,--no-man-pages
,--compress=2
shrink size further
Let’s breakdown this above code.
Here’s what every piece does, top to bottom:
jlink \
Runs the jlink tool to build a custom Java runtime image (a trimmed JRE containing only the modules you ask for).
--module-path $JAVA_HOME/jmods:out \
Tells jlink where to find modules:
$JAVA_HOME/jmods
→ all standard JDK modules as.jmod
files.out
→ your app’s modules (modular JARs or.jmod
you built).
(On Windows use;
instead of:
.)👉 But: this only makes them available. It doesn’t mean jlink will include everything in those paths.
--add-modules com.example.myapp,java.base,java.sql \
Declares the root modules to include:
com.example.myapp
→ your application module.java.base
→ the core module (implicitly required; listing it is fine).java.sql
→ adds JDBC APIs.
jlink will pull in transitive dependencies of these automatically; anything not needed is left out.
Note:- Understand in below way.
Think of
--module-path
as “where to look”,and
--add-modules
as “which ones to pick up and package”.
--launcher runapp=com.example.myapp/com.example.myapp.Main \
Creates a convenience launcher script inside the image:
Name:
runapp
(will appear undermyruntime/bin/runapp
orrunapp.bat
on Windows).Target: run
com.example.myapp.Main
as the main class in the modulecom.example.myapp
.
So you’ll start your app withmyruntime/bin/runapp
instead of typing a longjava -m ...
command.
--output myruntime \
Writes the generated runtime image to the myruntime/
directory:
myruntime/
bin/ # contains 'java' and your 'runapp' launcher
conf/
lib/
release
--strip-debug --compress=2 --no-header-files --no-man-pages
Size/footprint optimizations:
--strip-debug
→ remove most debug metadata from classes in the image to shrink size (keep off if you need full debugging info in the image).--compress=2
→ compress resources in the image with the highest built-in level (smaller image; a tiny decompression cost at startup).--no-header-files
→ don’t include C header files.--no-man-pages
→ don’t include manual pages.
(All of these reduce disk size; they don’t change your code’s runtime throughput.)
4. Run your custom runtime
./myruntime/bin/java -version
./myruntime/bin/runapp
You’ve now got a standalone runtime just for your app.
🔹 jlink
vs jmod
jmod
→ packaging format (like a JAR but module-aware, can contain native code/resources). The JDK itself ships as.jmod
files under$JAVA_HOME/jmods
.jlink
→ the builder tool that uses.jmod
files to assemble a runtime.
You usually use jlink
. You’d touch jmod
only if you’re distributing libraries or a custom module with native code.
🔹 Real-World Uses
Microservices: each container gets a stripped-down runtime with only
java.base
+ app modules.Desktop apps: ship one folder that has your app + a minimal JVM, no need for users to install Java.
Edge devices/IoT: very small runtimes possible (tens of MBs).
More about jmod
:-
jmod
is a file format introduced with Java 9 as part of the Java Platform Module System (JPMS). It packages a Java module into a .jmod
file. You can think of it as a special archive format (like .jar
files) but designed specifically for modules in the modular Java system.
Key Differences Between .jar
and .jmod
.jar
(Java Archive): Used to package classes and resources into a single file. It can be executed if it has aMain-Class
..jmod
(Java Module): Goes further by supporting native code, configuration files, and legal notices in addition to classes and resources. It is not executable directly.
So, while JARs are more general-purpose, JMODs are tailored for modular applications in Java 9+.
What’s Inside a .jmod
File?
A .jmod
file may contain:
Compiled
.class
files (like a.jar
).Native libraries (
.dll
,.so
,.dylib
).Configuration files.
Legal/license notices.
Resource files (images, text, etc.).
Subscribe to my newsletter
Read articles from Mohit jain directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Mohit jain
Mohit jain
Oracle‬†Certified‬†Java‬†Developer‬†with‬†7+ years‬†of‬†experience‬†specializing‬†in‬†backend‬†development,‬†microservices‬ architecture,‬†and‬†cloud-based‬†solutions.‬†Proven‬†expertise‬†in‬†designing‬†scalable‬†systems,‬†optimizing‬†performance,‬†and‬ mentoring‬†teams‬†to‬†enhance‬†productivity.‬†Passionate‬†about‬†building‬†high-performance‬†applications‬†using‬†Java,‬†Spring‬ Boot, Kafka, and cloud technologies (AWS/GCP)