skip to content

What do the `requires` and `exports` directives do in a `module-info.java`, and why are they fundamental to the Java module system?

level: juniorimportance: must knowfreq 72%

answer

  1. requires = readability (I depend on you)
  2. exports = accessibility (this package is visible)
  3. public is not enough — package must be exported
  4. exports is per-package, no sub-package wildcard
  5. both sides must agree to cross the boundary

basics

~20 s

requires says your module depends on another module so you can use its code. exports makes one of your packages visible to other modules. Without an export, a package stays internal and other modules cannot use it, even if it is public.

solid answer

~40 s

In JPMS (the Java Platform Module System, added in Java 9), every module declares itself in `module-info.java`. `requires <module>` declares a dependency: it grants your module *readability* of that module, so you can compile and run against the types it exports. `exports <package>` does the reverse: it makes exactly that one package accessible to other modules. The two are complementary — a type is usable across a module boundary only if the owning module `exports` its package AND the consuming module `requires` the owning module. Crucially, `public` is no longer enough: a public class in a non-exported package is invisible outside its module ("strong encapsulation"). Exports are per-package and not transitive — listing a package does not export sub-packages. This pair replaces the flat, everything-visible classpath with explicit, compiler-enforced dependencies.

code

java · 14 lines
java
// module com.acme.core's module-info.java
module com.acme.core {
    exports com.acme.core.api;        // visible to readers
    // com.acme.core.internal is NOT exported -> stays private
}

// module com.acme.app's module-info.java
module com.acme.app {
    requires com.acme.core;            // can now read core's exports
    exports com.acme.app.web;
}

// In com.acme.app: OK -> com.acme.core.api is exported and required
// Using com.acme.core.internal.* -> compile error (not exported)

go deeper

for a junior

Knows requires = a dependency and exports = makes a package visible; can read a simple module-info.

for a middle

Explains strong encapsulation (public is not enough), per-package exports with no wildcard, and that both sides must agree.

for a senior

Reasons about API surface design via exports, the implicit java.base, and how the module graph is validated at compile and launch.

for a principal

Designs module boundaries for a library/platform: minimal exported surface, internal packages, and migration trade-offs from the classpath.

## Background: what a module is Before Java 9, code was organized only into **packages** (namespaces like `com.acme.util`) loaded from a flat **classpath**. Any public class anywhere on the classpath was visible to all other code. There was no way to say "this package is my internal implementation, keep out." The **Java Platform Module System (JPMS)**, introduced in Java 9 (Project Jigsaw), adds a layer *above* packages: a **module** is a named, self-describing group of packages. You declare a module in a special file at the source root called **`module-info.java`**: ```java module com.acme.app { requires com.acme.core; exports com.acme.app.api; } ``` ## `requires` — declaring a dependency (readability) `requires <moduleName>` states that your module **depends on** another module. The technical term is that it grants **readability**: module A *reads* module B. Only if A reads B can A use the public types in the packages B exports. The compiler and the runtime both enforce this — if you use a type from a module you did not `requires`, compilation fails. Every module implicitly `requires java.base` (the core JDK module with `java.lang`, `java.util`, etc.); you never write that line. ## `exports` — exposing a package (accessibility) `exports <packageName>` makes the **public** types in exactly that one package accessible to any module that reads yours. This is the heart of **strong encapsulation**: in the module world, `public` means "public *within the module*"; it crosses the module boundary only if the package is also exported. Key rules: - Exports are **per package**, listed individually. `exports com.acme.api` does NOT export `com.acme.api.internal` — there is no wildcard, sub-packages are separate. - Exporting affects only **public/protected** members (normal access rules still apply within an exported package). - A package that is not exported is an **internal** package: invisible to other modules at compile and run time, even via its public classes. ## How the two combine A type crosses a module boundary only when **both** sides agree: 1. The owning module `exports` the package, and 2. The consuming module `requires` the owning module. Miss either and you get an error. This turns dependencies from an implicit, fragile classpath soup into an explicit, compiler-checked graph — no accidental dependence on someone's internals, and missing dependencies are caught at build time rather than as a runtime `NoClassDefFoundError`. ## Why it matters - **Strong encapsulation:** library authors can keep `*.internal` packages truly private. - **Reliable configuration:** the module graph is validated up front; you cannot launch with a missing module. - **Clear API surface:** the exported packages are precisely the public contract.

  • If a class is `public` but its package is not exported, can another module use it?
    No. In JPMS, crossing a module boundary requires both an export of the package and a requires of the module. A public class in a non-exported package is invisible outside its own module (strong encapsulation).
  • Does `exports com.acme.api` also export `com.acme.api.model`?
    No. Exports are per-package with no wildcard; sub-packages are distinct packages and must be exported individually.

A module is like an office building. requires is getting a keycard to another building. exports is which of your own rooms have public doors. Even a room with a 'public' sign stays locked to outsiders unless the building lists it as open.

saying these in an interview costs you the question

  • Thinking `public` alone makes a class usable across modules
  • Assuming `exports a.b` cascades to `a.b.c` sub-packages
  • Confusing requires (consume) with exports (provide) directions
  • Believing you must declare requires java.base

context