In the preceding section, you saw that a module must require another module if it wants to use its packages. However, that does not automatically make all packages in the required module available. A module states which of its packages are accessible, using the exports keyword. For example, here is a part of the module declaration for the java.xml module:
This module makes many packages available, but hides others (such as jdk.xmt.internat) by not exporting them.
When a package is exported, its pubtic and protected classes and interfaces, and their pubtic and protected members, are accessible outside the module. (As always, protected types and members are accessible only in subclasses.)
However, a package that is not exported is not accessible outside its own module. This is quite different from Java before modules. In the past, you were able to use public classes from any package, even if it was not part of the public API. For example, it was commonly recommended to use classes such as sun.misc.BASE64Encoder or com.sun.rowset.CachedRowSetlmpl when the public API did not provide the appropriate functionality.
Nowadays, you can no longer access unexported packages from the Java platform API since all of them are contained inside modules. As a result, some programs will no longer run with Java 9. Of course, nobody ever committed to keeping non-public APIs available, so this should not come as a shock.
Let us put exports to use in a simple situation. We will prepare a module com.horstmann.greet that exports a package, also called com.horstmann.greet, following the convention that a module that provides code for others should be named after the top-level package inside it. There is also a package com.horstmann.greet .internal that we don’t export.
A public Greeter interface is in the first package.
public interface Greeter
static Greeter newInstance()
return new com.horstmann.greet.internal.GreeterImpl();
String greet(String subject);
The second package has a class that implements the interface. The class is public since it is accessed in the first package.
public class GreeterImpl implements Greeter
public String greet(String subject)
return “Hello, ” + subject + “!”;
The com.horstmann.greet module contains both packages but only exports the first:
The second package is inaccessible outside the module.
We put our application into a second module, which will require the first module:
public class HelloWorld
public static void main(String args)
Greeter greeter = Greeter.newInstance();
Here is the source file structure for these two modules:
To build this application, first compile the com.horstmann.greet module:
javac com.horstmann.greet/modute-info.java \
Then compile the application module with the first module on the module path:
javac -p com.horstmann.greet v2ch09.exportedpkg/modute-info.java \
Finally, run the program with both modules on the module path:
java -p v2ch09.exportedpkg:com.horstmann.greet \
You have now seen the requires and exports statements that form the backbone of the Java Platform Module System. As you can see, the module system is conceptually simple. Modules specify what modules they need, and which packages they offer to other modules. Section 9.12, “Qualified Exporting and Opening,” on p. 521 shows a minor variation of the exports statement.
Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.