skip to content

Readability, Accessibility & exports vs opens

Access needs both readability (your module requires the other) and accessibility (the type is public in an exported package), and reflection additionally needs opens. The exports-versus-opens distinction is the single most-asked JPMS question.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

In the Java module system, what is the difference between `exports` and `opens` for a package?

level: middleimportance: must knowfreq 70%

answer

  1. exports = compile + run, public only, no reflection of internals
  2. opens = run-time deep reflection, includes private members
  3. Frameworks (Jackson/Hibernate/Spring) need opens
  4. Both can be qualified 'to <module>'
  5. open module = all packages opened for reflection

basics

~10 s

exports lets other modules use a package's public types at compile time and run time. opens additionally lets other modules use reflection to reach into the package, including its private members.

solid answer

~40 s

Both declarations appear in `module-info.java`. `exports a.b.c;` makes the public API of package `a.b.c` available to other modules for normal compile-time and run-time access — you can call public methods of public classes. It does NOT grant reflective access to non-public members. `opens a.b.c;` grants deep reflective access at run time: another module can use reflection (including `setAccessible(true)`) to read and invoke even private fields and methods. `opens` does not, by itself, give normal compile-time access. Frameworks like Jackson, Hibernate, or Spring need `opens` (or `open module`) to inject and read private fields. The rule of thumb: `exports` is for your public API surface; `opens` is for letting a reflection-based library look inside. You can also qualify either to specific modules: `exports a.b.c to com.example.client;`.

code

java · 11 lines
java
module com.example.app {
    // Public API: other modules can import and call these types normally.
    exports com.example.app.api;

    // Internal data objects: only Jackson may reflect into private fields.
    opens com.example.app.model to com.fasterxml.jackson.databind;

    // Both: usable as API AND reflectable by everyone.
    exports com.example.app.dto;
    opens   com.example.app.dto;
}

go deeper

for a junior

Knows exports shares a package's public API and opens is needed for reflection-based libraries.

for a middle

Explains that exports is compile+run public access while opens is run-time deep reflection including private members, and names which frameworks need opens.

for a senior

Articulates the orthogonality, uses qualified exports/opens to keep holes minimal, and knows open module semantics and the InaccessibleObjectException failure mode.

for a principal

Sets module-design policy: minimal qualified opens for framework integration, separating API packages from reflected model packages, and weighs strong encapsulation against framework convenience across a multi-module build.

## Background: what a module is The **Java Platform Module System (JPMS)**, introduced in Java 9, groups packages into a named unit called a **module**. A module is described by a file `module-info.java` at the root of its source, which compiles to `module-info.class`. Inside it you declare what the module needs and what it offers. A tiny example: ```java module com.example.app { requires com.example.lib; // I depend on another module exports com.example.app.api; // I let others use this package's public API opens com.example.app.model; // I let others reflect into this package } ``` ## The problem these keywords solve Before modules, every public class in any JAR on the classpath was visible to everyone, and reflection could reach *anything*. Modules introduce **strong encapsulation**: by default, packages inside a module are completely hidden from other modules. You must explicitly open holes. `exports` and `opens` are two *different kinds* of holes. ## `exports` — normal access to a public API `exports a.b.c;` says: *other modules that read mine may use the **public** types in package `a.b.c` the normal way.* "Normal way" means: - **Compile time:** their code can `import a.b.c.Foo;` and call `Foo`'s public/protected members. The compiler allows it. - **Run time:** the JVM allows the same access at execution. Crucially, `exports` only exposes what is *already* `public` (and `protected` members of public classes, to subclasses). Package-private and private members stay hidden. And `exports` gives **no** special reflective powers: you cannot use reflection to call a private method of an exported type from another module. ## `opens` — deep reflective access `opens a.b.c;` says: *at **run time**, other modules may use **reflection** to access **all** members of all types in `a.b.c`, including non-public classes and private fields/methods, including calling `setAccessible(true)`.* What `opens` does **not** do: it does not grant normal compile-time access. If a package is `opens` but not `exports`, you cannot `import` its types and call them directly in source code — you can only reach them reflectively at run time. ## Why both exist They serve two different consumers: 1. **API consumers** call your code directly. They need `exports`. 2. **Reflection-based frameworks** (JSON mappers like Jackson, ORMs like Hibernate, dependency injection like Spring, serialization, testing tools) inspect and mutate your objects' *private* internals at run time. They need `opens`. For example, Jackson sets a private field directly when deserializing JSON into your object; without `opens`, that throws `InaccessibleObjectException`. A package can be both exported and opened, just exported, just opened, or neither. ## Qualified forms Both can be restricted to named modules: ```java exports a.b.c to com.example.client; // only this module gets compile/run access opens a.b.c to com.fasterxml.jackson.databind; // only Jackson can reflect ``` Qualified exports/opens keep the hole as small as possible — good for internal-but-shared packages. ## `open module` If you write `open module com.example.app { ... }`, **every** package in the module is implicitly opened for reflection (but you still must `exports` packages for normal access). This is a blunt convenience for legacy code or heavily reflected modules. ## The mental model - `exports` = "you may *use my public API* the normal way (compile + run)." - `opens` = "you may *reflect into my internals* at run time, even private parts." They are orthogonal: one is about ordinary access to public things, the other is about reflective access to everything.

  • If I only `exports` a package but a JSON library reflects into a private field of one of its types, what happens?
    At run time the library hits `InaccessibleObjectException` when it calls `setAccessible(true)` (or tries to access the private member), because `exports` does not grant deep reflective access. You need `opens` (ideally qualified to that library) for that package.
  • What does `open module` do versus listing individual `opens`?
    `open module` implicitly opens every package in the module for reflection. Individual `opens` directives open only the named packages, keeping encapsulation tighter. `open module` does not replace `exports` — you still need `exports` for normal compile-time access.

exports is putting goods in a shop window others can buy through the front door. opens is giving someone a key to walk into the back room and rummage through everything, even the locked drawers.

saying these in an interview costs you the question

  • Saying `exports` grants reflective access to private members — it does not.
  • Saying `opens` lets you import and call the package's types in source code — it only enables run-time reflection.
  • Claiming a package must be exported before it can be opened — they are independent.
  • Thinking `open module` also handles normal (non-reflective) access — it does not; `exports` is still required.

context

open as a page

What is module readability in JPMS, and how does a module obtain it?

level: juniorimportance: should knowfreq 55%

basics

~10 s

Readability means one module is allowed to depend on another. Module A reads module B when A's module-info.java says requires B. Without that, A cannot use B's types at all.

open as a page

A framework throws `InaccessibleObjectException` (or 'Unable to make field accessible') when reflecting into your module. What is the root cause and how do you fix it correctly?

level: seniorimportance: should knowfreq 58%

basics

~20 s

The framework is using reflection to reach into your code's private members, but your package is not opened. Modules block reflective access by default. Fix it by adding opens yourpackage; (or opens yourpackage to the.framework;) in module-info.java.

open as a page

Can a type be `public` in Java yet still be inaccessible from another module? Explain why.

level: seniorimportance: should knowfreq 60%

basics

~20 s

Yes. Before modules, public meant accessible everywhere. With modules, a public type is only accessible from another module if its package is also exported and the other module reads yours. So public no longer guarantees access.

open as a page

Strong encapsulation in JPMS is a deliberate trade-off. As a module designer, how do you decide what to `exports`, what to `opens`, and how do you keep the surface minimal?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

Export only the packages that form your public API; keep everything else internal. Open packages only for the specific reflection-based frameworks that truly need them, and qualify those opens to just those frameworks. The default should be closed.

open as a page