- Java Modules Cheat Sheet
- β‘ Source Files
- Archiver β jar
- β More Options
- Virtual Machine β java
- β Path to JARs
- β‘ More Options
- β’ Main Module or Class
- General Options β javac , jar , java
- Recording Versions β javac , jar
- Hacking the Module System β javac , java
- Building Modules on the Command Line
- A Basic Build
- Defining a Main Class
- Circumventing Strong Encapsulation
- Extending the Module Graph
Java Modules Cheat Sheet
Tell the compiler where to find your dependencies’ JARs:
—class-path, -cp $ Path to JARs, separated by : (π§) or ; (β), e.g. —class-path libs/foo.jar:libs/bar.jar . Alternatively, add all JARs in a directory with —class-path ‘libs/*’ . —module-path, -p $ Like class path but with first-level support for directories: —module-path libs .
Usually, regular JARs go on the class path and modular JARs on the module path, but that’s not a hard rule.
—release $ Compile for a specific VM version. Supported targets: 6, 7, 8, 9, 10, 11 -source $ Provide source compatibility with specified release -target $ Generate class files for specific VM version —>
β‘ Source Files
Tell the compiler which source files to compile. There are various options:
- Manually list all source files (possibly including module-info.java ), separated by space
- Automatically determine all source files with `find $ -name *.java` (include backticks, replace $ with actual path; π§)
- Use —source-path or —module-source-path (not covered here) Source Path —>
Archiver β jar
—create, -c Operation mode that creates an archive —file, -f $
If $ contains a module-info.class , the JAR will be modular.
β More Options
Option of particular interest when packaging modules:
—main-class, -e $ Record the application entry point, i.e. the class with the main method
Virtual Machine β java
β Path to JARs
Tell the compiler where to find your application’s JARs. Works exactly as during compilation, but remember to include your JARs.
β‘ More Options
Option of particular interest when running modules:
—show-module-resolution During JVM startup, log messages that show module resolution —illegal-access $ Define whether code in unnamed modules can use inaccessible types/members in named modules; $ can be permit , warn (default on Java 11), debug , or deny
β’ Main Module or Class
Tell the JVM how to launch the application. If the main class is on the class path:
If the main class is in a JAR on the module path:
General Options β javac , jar , java
@$
Recording Versions β javac , jar
The module system does not interpret module versions, but it can record them:
javac —module-version $ Embed the $ in the module declaration jar —module-version $ Embed the $ in the module declaration, possibly overriding the one embedded earlier by the compiler
At run time, the version of a module and its dependencies are available via ModuleDescriptor::requires .
Hacking the Module System β javac , java
—add-modules $
In some places you can specify more than one module. To do that, separate them with commas.
tags with directory trees * replace * «βΈ» with «βΈ» * «βΌ» with «βΎ» —>
Building Modules on the Command Line
When using the module system to create modules for your code, you will likely do that in a project that uses a build tool and so it is its task to get things right. But it helps tremendously to understand what «right» looks like and how to correctly configure javac , jar , and java to compile, package, and run your application. This will give you a better understanding of the module system and help debug problems in case the build tool doesn’t get it right.
Note: You need to know the module system basics to get the most out of this article. You may also want to check out the description of the core JDK tools.
A Basic Build
Given a project with a few source files, a module declaration, and a few dependencies, this is how you can compile, package, and run it in the simplest way:
# compile sources files, including module-info.java $ javac --module-path $DEPS -d $CLASS_FOLDER $SOURCES # package class files, including module-info.class $ jar --create --file $JAR $CLASSES # run by specifying a module by name $ java --module-path $JAR:$DEPS --module $MODULE_NAME/$MAIN_CLASS
There’s a bunch of placeholders in there:
- $DEPS is the list of dependencies. These are typically paths to JAR files separated by : (Unix) or ; (Windows), but on the module path, this can also just be folder names (without the /* -trickery that’s required on the class path).
- $CLASS_FOLDER is the path to the folder where the *.class files will be written to.
- $SOURCES is the list of *.java files and must include module-info.java .
- $JAR is the path to the JAR file that will be created.
- $CLASSES is the list of *.class files that was created during compilation (thus found in $CLASS_FOLDER ) and must include module-info.class .
- $MODULE_NAME/$MAIN_CLASS is the name of the initial module (i.e. the one where module resolution starts) followed by the name of the class containing the app’s main method.
For a simple «Hello World» style project with the common src/main/java structure, just a single source file, dependencies in a deps folder, and using Maven’s target folder that would look as follows:
$ javac --module-path deps -d target/classes src/main/java/module-info.java src/main/java/com/example/Main.java $ jar --create --file target/hello-modules.jar target/classes/module-info.class target/classes/com/example/Main.class $ java --module-path target/hello-modules.jar:deps --module com.example/com.example.Main
Defining a Main Class
The jar option —main-class $MAIN_CLASS embeds $MAIN_CLASS as the class containing the main method in the module descriptor, which allows you to launch a module without having to name the main class:
$ jar --create --file target/hello-modules.jar --main-class com.example.Main target/classes/module-info.class target/classes/com/example/Main.class $ java --module-path target/hello-modules.jar:deps --module com.example
Note that it is possible to override that class and launch another, simply by naming it as before:
# create a JAR with `Main` and `Side`, # making `Main` the main class $ jar --create --file target/hello-modules.jar --main-class com.example.Main target/classes/module-info.class target/classes/com/example/Main.class target/classes/com/example/Side.class # override the main class and launch `Side` $ java --module-path target/hello-modules.jar:deps --module com.example/com.example.Side
Circumventing Strong Encapsulation
The module system is very strict about access to internal APIs: If the package isn’t exported or opened, access will be denied. But a package can’t just be exported or opened by a module’s author — there are also the command line flags —add-exports and —add-opens , which allow the module’s user to do that as well.
As an example, see this code that tries to create an instance of the internal class sun.util.BuddhistCalendar :
BuddhistCalendar calendar = new BuddhistCalendar();
To compile and run it, we need to use —add-exports :
javac --add-exports java.base/sun.util=com.example.internal module-info.java Internal.java # package with `jar` java --add-exports java.base/sun.util=com.example.internal --module-path com.example.internal.jar --module com.example.internal
If the access is reflective.
Class.forName("sun.util.BuddhistCalendar").getConstructor().newInstance();
. compilation will work without further configuration, but we need to add —add-opens when running the code:
java --add-opens java.base/sun.util=com.example.internal --module-path com.example.internal.jar --module com.example.internal
Extending the Module Graph
Starting with an initial set of root modules, the module system computes all of their dependencies and builds a graph, where the modules are nodes and their readability relations are directed edges. This module graph can be extended with the command line flags —add-modules and —add-reads , which add modules (and their dependencies) and readability edges, respectively.
As an example, let’s imagine a project that has an optional dependency on java.sql, but the module is not otherwise required. That means it’s not added to the module graph without a little help:
# launch without java.sql $ java --module-path example.jar:deps --module com.example/com.example.Main # launch with java.sql $ java --module-path example.jar:deps --add-modules java.sql --module com.example/com.example.Main
An alternative approach to optional dependencies would be to not list the dependency at all and only add it with —add-modules and —add-reads (this is rarely helpful and not generally recommended — just an example):
$ java --module-path example.jar:deps --add-modules java.sql --add-reads com.example=java.sql --module com.example/com.example.Main
Specify version of modules that are being compiled —patch-module = (: )* Override or augment a module with classes and resources in JAR files or directories —upgrade-module-path
Override location of upgradeable modules —>