Chapter 11 Modules Flashcards
Understanding Modules * Describe the Modular JDK * Declare modules and enable access between modules * Describe how a modular project is compiled and run
Introducing Modules
- The Java Platform Module System (JPMS) was introduced in Java 9
- The main purpose of a module is to provide groups of related packages to offer a particular set of functionality to developers.
The Java Platform Module System includes the following:
- A format for module JAR files
- Partitioning of the JDK into modules
- Additional command-line options for Java tools
A module
is a group of one or more packages
plus a special file called module-info.java
.
dependencies where one module relies on code in another.
Java Platform Module System (JPMS)
- The Java Platform Module System (JPMS) was introduced in Java 9
- The Java Platform Module System includes the following:
- A format for module JAR files
- Partitioning of the JDK into modules
- Additional command-line options for Java tools
BENEFITS OF MODULES
-
Better Access Control
- fifth level of access control.
- They can expose packages within the modular JAR to specific other packages.
- This stronger form of encapsulation really does create internal packages.
-
Clearer Dependency Management
specify their dependencies in the module-info.java file. -
Custom Java Builds
- allows developers to specify what modules they actually need.
- A tool called
jlink
is used to create this runtime image.
-
Improved Performance
improves startup time and requires less memory to run. -
Unique Package Enforcement
A package is only allowed to be supplied by one module.
module-info.java
This is the simplest possible module-info.java
file
module zoo.animal.feeding { }
- The module-info file must be in the root directory of your module.
- The module-info file must use the keyword module.
1: module zoo.animal.care { 2: exports zoo.animal.care.medical; 3: requires zoo.animal.feeding; 4: }
- Line 1 specifies the name of the module.
- Line 2 lists the package we are exporting so it can be used by other modules.
- On line 3, we see a new keyword. The requires statement specifies that a module is needed. The zoo.animal.care module depends on the zoo.animal.feeding module.
There are a few key differences between a module-info
file and a regular Java class
:
- The
module-info
file must be in the root directory of your module. Regular Java classes should be in packages. - The
module-info
file must use the keywordmodule
instead of class, interface, or enum. - The module name follows the naming rules for package names. It often includes periods (.) in its name. Regular class and package names are not allowed to have dashes (-). Module names follow the same rule.
That’s a lot of rules for the simplest possible file. There will be many more rules when we flesh out this file later in the chapter.
CAN A MODULE-INFO.JAVA
FILE BE EMPTY?
Yes. As a bit of trivia,
it was legal to compile any empty file with a .java extension even before modules.
The compiler sees there isn’t a class in there and exits without creating a .class file.
COMPILING OUR FIRST MODULE
> [!NOTE]
When you’re entering commands at the command line, they should be typed all on one line.
javac --module-path mods -d feeding feeding/zoo/animal/feeding/*.java feeding/module-info.java
- the
-d
option specifies the directory to place the class files in. - The end of the command is a list of the .java files to compile. You can list the files individually or use a wildcard for all .java files in a subdirectory.
-
--module-path
option indicates the location of any custom module files. - The syntax
--module-path
and-p
are equivalent.
The following four commands show the -p option:
javac -p mods -d feeding feeding/zoo/animal/feeding/*.java feeding/*.java javac -p mods -d feeding feeding/zoo/animal/feeding/*.java feeding/module-info.java javac -p mods -d feeding feeding/zoo/animal/feeding/Task.java feeding/module-info.java javac -p mods -d feeding feeding/zoo/animal/feeding/Task.java feeding/*.java
WHAT HAPPENED TO THE CLASSPATH
?
You can still use these options in Java 11. In fact, it is common to do so when writing nonmodular programs.
-cp, --class-path, and -classpath
RUNNING OUR FIRST MODULE
Pay special attention to the book.module/com.sybex.OCP part. It is important to remember that you specify the module name followed by a slash (/) followed by the fully qualified class name.
java --module-path mods --module book.module/com.sybex.OCP
-
Location of modules,
--module-path, -p
-
Module name,
--module, -m
- Module/package separator
- Package name
- Class name
examples:
java --module-path feeding --module zoo.animal.feeding/zoo.animal.feeding.Task java -p feeding -m zoo.animal.feeding/zoo.animal.feeding.Task
PACKAGING OUR FIRST MODULE
Be sure to create a mods directory before running this command:
jar -cvf mods/zoo.animal.feeding.jar -C feeding/ .
- We are packaging everything under the feeding directory
- and storing it in a JAR file named zoo.animal.feeding.jar under the mods folder.
run the program again, but this time using the mods directory instead of the loose classes:
java -p mods -m zoo.animal.feeding/zoo.animal.feeding.Task
Since the module path is used, a module JAR is being run.
> [!NOTE]
It is possible to version your module using the –module-version option.
This isn’t on the exam but is good to do when you are ready to share your module with others.
version your module using the –module-version option.
ORDER MATTERS!
Order matters when compiling a module.
javac -p mods -d care care/module-info.java care/zoo/animal/care/details/*.java care/zoo/animal/care/medical/*.java
The compiler complains that it doesn’t know anything about the package zoo.animal.care.medical.
care/module-info.java:3: error: package is empty or does not exist: zoo.animal.care.medical exports zoo.animal.care.medical;
- A package must have at least one class in it in order to be exported.
- Since we haven’t yet compiled zoo.animal.care.medical.Diet, the compiler acts as if it doesn’t exist.
- If you get this error message, you can reorder the javac statement.
- Alternatively, you can compile the packages in a separate javac command, before compiling the module-info file.
jar -cvf mods/zoo.animal.care.jar -C care/ .
Diving into the module-info File
These are not Java keywords they are directives, can appear in any order in the module-info file.
- exports,
- requires,
- provides,
- uses,
- and opens.
EXPORTS
- exports packageName exports a package to other modules.
- It’s also possible to export a package to a specific module.
module zoo.animal.talks { exports zoo.animal.talks.content to zoo.staff; exports zoo.animal.talks.media; exports zoo.animal.talks.schedule; requires zoo.animal.feeding; requires zoo.animal.care; }
EXPORTED TYPES
Exporting a package
- All public classes, interfaces, and enums are exported.
- Further, any public and protected fields and methods in those files are visible.
- Fields and methods that are private are not visible because they are not accessible outside the class.
- Similarly, package-private fields and methods are not visible because they are not accessible outside the package.
REQUIRES TRANSITIVE
requires moduleName specifies that the current module depends on moduleName.
There’s also a requires transitive moduleName, which means that any module that requires this module will also depend on moduleName.
module zoo.animal.feeding { exports zoo.animal.feeding; } module zoo.animal.care { exports zoo.animal.care.medical; requires transitive zoo.animal.feeding; } module zoo.animal.talks { exports zoo.animal.talks.content to zoo.staff; exports zoo.animal.talks.media; exports zoo.animal.talks.schedule; // no longer needed requires zoo.animal.feeding; // no longer needed requires zoo.animal.care; requires transitive zoo.animal.care; } module zoo.staff { // no longer needed requires zoo.animal.feeding; // no longer needed requires zoo.animal.care; requires zoo.animal.talks; }
Duplicate requires Statements
module bad.module { requires zoo.animal.talks; requires transitive zoo.animal.talks; }
Java doesn’t allow you to repeat the same module in a requires clause. It is redundant and most like an error in coding. Keep in mind that requires transitive is like requires plus some extra behavior.
PROVIDES, USES, AND OPENS
The provides keyword specifies that a class provides an implementation of a service.
To use it, you supply the API and class name that implements the API:
provides zoo.staff.ZooApi with zoo.staff.ZooImpl
The uses keyword specifies that a module is relying on a service. To code it, you supply the API you want to call:
uses zoo.staff.ZooApi
Discovering Modules
- “unjar” module JAR file and open the module-info file.
- java command, -d or –describe-module
java -p mods -d zoo.animal.feeding java -p mods --describe-module zoo.animal.feeding
- jar command, -d or –describe-module
jar -f mods/zoo.animal.feeding.jar -d jar --file mods/zoo.animal.feeding.jar --describe-module
Discovering Modules via java command
module-info in zoo.animal.feeding:
module zoo.animal.feeding { exports zoo.animal.feeding; }
The java command now has an option to describe a module. The following two commands are equivalent:
java -p mods -d zoo.animal.feeding java -p mods --describe-module zoo.animal.feeding
Each prints information about the module. For example, it might print this:
zoo.animal.feeding file:///absolutePath/mods/zoo.animal.feeding.jar exports zoo.animal.feeding requires java.base mandated
- The first line is the module we asked about:** zoo.animal.feeding**.
- The second line starts information about the module. In our case, it is the same package exports statement we had in the module-info file.
- On the third line, we see requires java.base mandated. Now wait a minute. The module-info file very clearly does not specify any modules that zoo.animal.feeding has as dependencies.
- The java.base module is special. It is automatically added as a dependency to all modules. This module has frequently used packages like java.util. That’s what the mandated is about. You get java.base whether you asked for it or not.
- In classes, the java.lang package is automatically imported whether you type it or not. The java.base module works the same way. It is automatically available to all other modules.
MORE ABOUT DESCRIBING MODULES
module-info in zoo.animal.care:
module zoo.animal.care { exports zoo.animal.care.medical to zoo.staff; requires transitive zoo.animal.feeding; }
Now we have the command to describe the module and the output.
java -p mods -d zoo.animal.care zoo.animal.care file:///absolutePath/mods/zoo.animal.care.jar requires zoo.animal.feeding transitive requires java.base mandated qualified exports zoo.animal.care.medical to zoo.staff contains zoo.animal.care.details
- The first line of the output is the absolute path of the module file.
- The two requires lines should look familiar as well.
- The first is in the module-info, and the other is added to all modules.
- Next comes something new. The qualified exports is the full name of exporting to a specific module.
- Finally, the contains means that there is a package in the module that is not exported at all. This is true. Our module has two packages, and one is available only to code inside the module.
Listing Available Modules
you can use the java command to list the modules that are available. The simplest form lists the modules that are part of the JDK:
java --list-modules
When we ran it, the output went on for 70 lines and looked like this:
java.base@11.0.2 java.compiler@11.0.2 java.datatransfer@11.0.2
This is a listing of all the modules that come with Java and their version numbers. You can tell that we were using Java 11.0.2 when testing this example.
java -p mods --list-modules
zoo.animal.care file:///absolutePath/mods/zoo.animal.care.jar zoo.animal.feeding file:///absolutePath/mods/zoo.animal.feeding.jar zoo.animal.talks file:///absolutePath/mods/zoo.animal.talks.jar zoo.staff file:///absolutePath/mods/zoo.staff.jar
Since these are custom modules, we get a location on the file system. If the project had a module version number, it would have both the version number and the file system path.
> [!Note]
that –list-modules exits as soon as it prints the observable modules.
It does not run the program.
Showing Module Resolution
- In case listing the modules didn’t give you enough output, you can also use the –show-module-resolution option.
- You can think of it as a way of debugging modules.
- It spits out a lot of output when the program starts up.
- Then it runs the program.
java --show-module-resolution -p feeding -m zoo.animal.feeding/zoo.animal.feeding.Task
Luckily you don’t need to understand this output. That said, having seen it will make it easier to remember. Here’s a snippet of the output:
root zoo.animal.feeding file:///absolutePath/feeding/ java.base binds java.desktop jrt:/java.desktop java.base binds jdk.jartool jrt:/jdk.jartool ... jdk.security.auth requires java.naming jrt:/java.naming jdk.security.auth requires java.security.jgss jrt:/java.security.jgss ... All fed!
- It starts out by listing the root module. That’s the one we are running:
zoo.animal.feeding. - Then it lists many lines of packages included by the mandatory java.base module.
- After a while, it lists modules that have dependencies.
- Finally, it outputs the result of the program All fed!.
- The total output of this command is 66 lines.
THE JAR COMMAND
the jar command can describe a module. Both of these commands are equivalent:
jar -f mods/zoo.animal.feeding.jar -d jar --file mods/zoo.animal.feeding.jar --describe-module
The output is slightly different from when we used the java command to describe the module. With jar, it outputs the following:
zoo.animal.feeding jar:file:///absolutePath/mods/zoo.animal.feeding.jar /!module-info.class exports zoo.animal.feeding requires java.base mandated
The JAR version includes the module-info in the filename, which is not a particularly significant difference in the scheme of things. You don’t need to know this difference. You do need to know that both commands can describe a module.
THE JDEPS COMMAND
The jdeps command gives you information about dependencies within a module.
it looks at the code in addition to the module-info file.
This tells you what dependencies are actually used rather than simply declared.
Let’s start with a simple example and ask for a summary of the dependencies in zoo.animal.feeding. Both of these commands give the same output:
jdeps -s mods/zoo.animal.feeding.jar jdeps -summary mods/zoo.animal.feeding.jar
Notice that there is one dash (-) before -summary
rather than two.
Regardless, the output tells you that there is only one package and it depends on the built-in java.base module.
zoo.animal.feeding -> java.base
Alternatively, you can call jdeps without the summary option and get the long form:
jdeps mods/zoo.animal.feeding.jar [file:///absolutePath/mods/zoo.animal.feeding.jar] requires mandated java.base (@11.0.2) zoo.animal.feeding -> java.base zoo.animal.feeding -> java.io java.base zoo.animal.feeding -> java.lang java.base
- The first part of the output shows the module filename and path.
- The second part lists the required java.base dependency and version number.
- This has the high-level summary that matches the previous example.
- Finally, the last four lines of the output list the specific packages within the java.base modules that are used by zoo.animal.feeding.
There is not a short form of --module-path
in the jdeps
command. The output is only two lines:
jdeps -s --module-path mods mods/zoo.animal.care.jar jdeps -summary --module-path mods mods/zoo.animal.care.jar
There is not a short form of –module-path in the jdeps command. The output is only two lines:
zoo.animal.care -> java.base zoo.animal.care -> zoo.animal.feeding
We can see that the zoo.animal.care module depends on our custom zoo.animal.feeding module along with the built-in java.base.
In case you were worried the output was too short, we can run it in full mode:
jdeps --module-path mods mods/zoo.animal.care.jar
This time we get lots of output:
zoo.animal.care [file:///absolutePath/mods/zoo.animal.care.jar] requires mandated java.base (@11.0.2) requires transitive zoo.animal.feeding zoo.animal.care -> java.base zoo.animal.care -> zoo.animal.feeding zoo.animal.care.details -> java.lang java.base zoo.animal.care.details -> zoo.animal.feeding zoo.animal.feeding zoo.animal.care.medical -> java.lang java.base
As before, there are three sections.
* The first section is the filename and required dependencies.
* The second section is the summary showing the two module dependencies with an arrow.
* The last six lines show the package-level dependencies.
THE JMOD COMMAND
jmod
command
JMOD files are recommended only when you have native libraries or something that can’t go inside a JAR file. This is unlikely to affect you in the real world.
The most important thing to remember is that jmod is only for working with the JMOD files.