Java module directives define a module’s dependencies, which of its packages other modules can access, and how it participates in service loading. The key distinction is that exports grants ordinary compile-time and run-time access to a package, while opens grants run-time reflective access. requires declares dependencies; uses and provides connect service consumers and providers.
What a module directive declares
A module descriptor, usually named module-info.java, contains a module declaration and directives. A module declaration can have an empty body, or include directives that describe three kinds of relationships:
- Dependencies: which other modules this module needs, using
requires. - Package access: which packages it makes accessible, using
exportsoropens. - Services: which services it consumes or implements, using
usesandprovides.
The Java SE 9 Language Specification groups the directives by these purposes. The examples below use illustrative names, not a tested or real application. See the Java SE 9 Language Specification, Chapter 7 for the normative Java 9 rules.
How do requires directives work?
A requires directive declares that one module depends on another. The named module must be readable to the declaring module under the module system’s rules.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Ordinary dependency
requires com.example.foo.http; declares a dependence on com.example.foo.http. Every module other than java.base has an implicit dependence on java.base, unless it declares that dependence explicitly. The java.base module itself cannot have a requires directive.
Transitive dependency
requires transitive com.example.foo.network; makes the dependency transitive for readability: modules that read the declaring module also acquire an implied dependence on com.example.foo.network. This is useful when the declaring module’s API exposes types from that dependency and its consumers need to read the dependency too. It does not export the dependency’s packages on the declaring module’s behalf.
Rank #2
Static dependency
requires static com.example.foo.optional; means the dependency is required at compile time but optional at run time. It does not allow compilation to succeed without the dependency when the code being compiled requires it.
When should a package be exports or opens?
Use exports when other modules should use a package’s public and protected types and members as ordinary API. Use opens when code needs run-time reflective access, such as when a framework inspects types or members, without making the package available for compile-time use by other modules.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
| Directive | Compile-time access | Run-time access | Reflective access |
|---|---|---|---|
exports package.name; |
Yes, to public and protected types and members | Yes, to public and protected types and members | Yes, to those public and protected elements |
opens package.name; |
No | Yes, to public and protected types and members | Yes, to all types and members in the package |
Unqualified and qualified access
An unqualified directive applies broadly:
exports com.example.foo.bar;makes the package’s exported API accessible to other modules.opens com.example.foo.quux;opens the package for run-time reflection generally.
A qualified directive names the modules that receive access:
exports com.example.foo.internal to com.example.foo.probe;exports that package only to the listed module.opens com.example.foo.internal to com.example.foo.network, com.example.foo.probe;opens it for reflection only to the listed modules.
An unqualified export is a broad API commitment. The Java SE 9 specification identifies unqualified exports, along with transitive dependencies, as elements of a module’s primary API. Qualified exports and opens let a module grant narrower access where that fits its design.
Rank #4
What an open module changes
open module com.example.foo { ... } opens all packages in the module for run-time reflection, as though each package had an opens directive. It does not export all packages for compile-time use. Other modules still get compile-time access only to packages the descriptor explicitly exports; individual opens directives can therefore be omitted from an open module.
How do uses and provides support services?
These directives describe the consumer and provider sides of the service-loading model:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchBest Value
uses com.example.foo.spi.Intf;declares that the module consumes the service represented by that type.provides com.example.foo.spi.Intf with com.example.foo.Impl;declares that the module supplies an implementation of the service. A provider can list one or more implementations.
The consumer declares uses; the module supplying an implementation declares provides ... with. This lets service users and providers be decoupled through ServiceLoader, rather than requiring the consumer to name a particular implementation. See Dev.java’s Modules guide for an introduction to module relationships and services.
Putting the directives together
This illustrative descriptor groups dependencies, package access, and service declarations in one module:
module com.example.foo {
requires com.example.foo.http;
requires java.logging;
requires transitive com.example.foo.network;
exports com.example.foo.bar;
exports com.example.foo.internal to com.example.foo.probe;
opens com.example.foo.quux;
opens com.example.foo.internal to com.example.foo.network,
com.example.foo.probe;
uses com.example.foo.spi.Intf;
provides com.example.foo.spi.Intf with com.example.foo.Impl;
}
Read it by purpose: the first three directives declare dependencies; the exports and opens directives define package access; the final two directives declare service consumption and provision. Each directive expresses a different relationship, so choosing one depends on whether the need is readability, ordinary API access, reflective access, or service loading.
For Java 9 syntax and exact language rules, consult the Java SE 9 JLS, Chapter 7. For an introductory explanation, see Oracle Java Magazine, September/October 2017.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

