What is the difference between `exports` and `opens` (including `opens...to` and an `open module`), and why is `opens` needed for reflection-heavy frameworks?
answer
- exports = public API (compile+run); opens = deep reflection (runtime)
- opens lets setAccessible reach private fields
- opens...to = qualified, only named modules
- open module = opens every package (blunt)
- frameworks: Jackson/Hibernate/Spring need opens not exports
basics
~20 sexports makes a package's public types visible at compile and run time. opens instead grants deep reflective access at runtime — letting frameworks read/set private fields and call private members via reflection. Frameworks like Jackson or Hibernate need opens, not just exports.
solid answer
~50 s`exports` and `opens` solve different problems. `exports P` exposes the **public** API of package P for normal compile-time and runtime access. `opens P` grants **deep reflective access** at runtime: any code that reads your module can use reflection (`setAccessible(true)`) to read and write **private** members of types in P — but it does **not** give compile-time access. This separation enforces strong encapsulation by default: under JPMS, reflection into a module's internals is blocked unless that package is opened. That breaks frameworks that rely on field injection or private-field serialization (Jackson, Hibernate, Spring, JAXB) until you open the relevant packages to them. You can scope it: `opens P to com.fasterxml.jackson.databind` opens P only to that named module (qualified opens). And an **`open module`** opens *every* package for reflection without listing them — a blunt migration convenience. Rule of thumb: `exports` for your API, `opens` (ideally qualified) only for the packages a reflective framework must touch.
code
java · 12 linesmodule com.acme.app {
requires com.fasterxml.jackson.databind;
// Normal public API for callers:
exports com.acme.app.api;
// Deep reflective access for JUST Jackson into the DTO package:
opens com.acme.app.dto to com.fasterxml.jackson.databind;
}
// Without the opens line, Jackson reflecting on a private field throws
// InaccessibleObjectException at runtime.go deeper
Knows opens is what reflection frameworks need and exports is for normal use.
Distinguishes runtime deep reflection (opens) from compile+run public access (exports) and names the InaccessibleObjectException symptom.
Uses qualified opens...to to grant least-privilege reflective access and explains open module as a blunt migration tool.
Sets module-encapsulation policy: minimal qualified opens per framework, avoids open module in shipped libraries, and reasons about the security implications of deep reflection.
## Two kinds of access JPMS distinguishes two ways one module reaches into another: 1. **Normal access** — compiling and running against another module's *public* types. Controlled by `exports`. 2. **Deep reflective access** — using the reflection API (`java.lang.reflect`) to inspect and manipulate members, including **private** ones, often via `field.setAccessible(true)`. Controlled by `opens`. Before Java 9, reflection could reach any member of any class with `setAccessible(true)`. JPMS introduced **strong encapsulation**: by default a module's internals are sealed even against reflection. This is great for safety but breaks tools that depend on reaching into your objects. ## `exports` — public API, no deep reflection `exports P` makes the **public/protected** types of package P usable by readers at **compile time and run time** through normal references. It does **not** by itself permit deep reflection into private members. (Reflecting over *public* members of an exported type is allowed; setAccessible on a *non-public* member of a merely-exported package is denied.) ## `opens` — deep reflective access, runtime only `opens P` grants **deep reflective access** to all types in P at **runtime**: readers can reflectively access public, protected, package-private, and **private** members, and call `setAccessible(true)` successfully. Important properties: - It is **runtime-only** — `opens` gives no compile-time access. (`opens` without `exports` means: you can reflect on it but cannot reference its types in source.) - It is exactly what serialization/DI/ORM frameworks need, because they read and write your private fields reflectively. ```java module com.acme.app { opens com.acme.app.model; // frameworks can reflect into model classes } ``` ## Qualified opens: `opens P to M1, M2` Unqualified `opens P` opens the package to **every** module that reads you. A **qualified** open restricts it: ```java opens com.acme.app.model to com.fasterxml.jackson.databind; ``` Now only the named module(s) get deep reflective access; everyone else is still locked out. This is the recommended form — you grant exactly the framework that needs it and no more. (The same `...to` qualification exists for `exports`: `exports P to M` is a **qualified export**, visible only to listed modules — useful for internal APIs shared between your own modules.) ## The `open module` Prefixing the declaration with `open` opens **every** package for reflection without enumerating them: ```java open module com.acme.app { requires ...; exports ...; // you may still list exports for normal access } ``` An `open module` cannot also contain individual `opens` directives (they'd be redundant). It is a **blunt instrument** — convenient during migration when many packages need reflective access, but it surrenders strong encapsulation for the whole module. Prefer per-package, qualified `opens` in finished code. ## Why frameworks need this - **Jackson / Gson / JAXB** serialize and deserialize by reflecting over fields, often private. - **Hibernate / JPA** populate entity private fields and may create proxies. - **Spring / CDI** perform field and constructor injection reflectively. Without opening the package containing those classes, you get `InaccessibleObjectException` ("Unable to make field ... accessible: module ... does not opens ... to ..."). The fix is to `opens` (preferably `opens ... to <framework module>`) the package holding the reflected types. ## Decision guide - Expose an API for normal use → `exports`. - Let a framework reflect into private members → `opens` (qualified to that framework). - Need both → list both `exports P` and `opens P` (they're independent). - Whole module is reflection-heavy during migration → `open module`, then tighten later.
- Does `opens P` give compile-time access to package P?No. `opens` grants only runtime deep reflective access; you cannot reference P's types in source unless P is also `exports`-ed. They are independent and can be combined.
- When would you choose an `open module` over per-package `opens`?Mainly as a migration shortcut when many packages need reflective access and you don't want to enumerate them. It opens every package, surrendering strong encapsulation, so it's discouraged for finished libraries — prefer qualified opens.
exports is opening your shop's front counter — customers buy what's on display. opens is handing a specific contractor the keys to the back rooms and safe so they can rearrange everything inside. You'd give those keys only to the one contractor (qualified opens), not the whole street.
saying these in an interview costs you the question
- Thinking `exports` is enough for Jackson/Hibernate to reflect into private fields
- Believing `opens` provides compile-time access to the package
- Using an unqualified open module by default instead of qualified opens
- Assuming opens and exports are interchangeable / mutually exclusive