Study guide

12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules

Oracle Java SE 17 Developer Certified Professional 1Z0-829

Track your progress as you read

Finished sections get a check mark.

1 Introduction: The 1Z0-829 Exam

About the Java SE 17 Developer (1Z0-829) Exam

2 Chapter 1: Java Fundamentals & Language Basics

1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs

3 Chapter 2: Program Flow & Decision Constructs

2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels

4 Chapter 3: Object-Oriented Design & Class Mechanics

3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)

5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types

4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers

6 Chapter 5: Pattern Matching & Nested Types

5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation

7 Chapter 6: Generics, Collections & Arrays

6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class

8 Chapter 7: Functional Programming & Lambda Expressions

7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References

9 Chapter 8: The Stream API

8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

10 Chapter 9: Advanced Streams & Collectors

9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls

11 Chapter 10: Exception Handling

10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions

12 Chapter 11: Date/Time API & Localization

11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates

13 Chapter 12: Java Platform Module System (JPMS)

12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies

14 Chapter 13: Concurrency & Multithreading

13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

15 Chapter 14: Java I/O and NIO.2

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`

16 Chapter 15: Accessing Databases with JDBC

15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)

17 Chapter 16: Annotations & the Java Logging API

16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration

12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules

Key Takeaways Explain This Topic AI

Java 17 strictly enforces deep encapsulation by default, preventing reflective access to non-public members via AccessibleObject.setAccessible(true) unless a package is explicitly opened.

The 'exports' directive grants compile-time and runtime access only to public types and members; attempting deep reflection on an exported package throws InaccessibleObjectException.

The opens <package>; directive permits runtime reflection without granting compile-time import access, making it the ideal choice for dependency injection and object-relational mapping frameworks.

Qualified opens ( opens <package> to <module>; ) restrict deep reflective inspection to specific designated consumer modules, preserving encapsulation against all others.

Declaring open module <name> opens every package in the module for reflective access at runtime; individual opens directives are forbidden inside an open module declaration.

Last updated: October 2026

Reflection, Deep Encapsulation, opens , and Open Modules

Reflection has long been a cornerstone of enterprise Java frameworks, powering dependency injection containers (Spring, CDI), object-relational mapping engines (Hibernate), and serialization libraries (Jackson, GSON). Prior to Java 9, reflective code could invoke AccessibleObject.setAccessible(true) on any Field , Method , or Constructor to bypass standard access control checks ( private , protected , package-private). In Java 17 LTS, deep encapsulation is strongly enforced by default, requiring explicit module descriptors to authorize reflective access.

Deep Encapsulation Barriers in Java 17

Under Java 17, the JVM strictly differentiates between compile-time accessibility and runtime reflective access :

Shallow Accessibility : Access to public types and public/protected members for direct invocation. Granted via the exports directive.

Deep Reflection : Access to private, package-private, or protected members via setAccessible(true) . Blocked across module boundaries by default.

Why exports Alone Forbids Deep Reflection

A frequent misconception is that exporting a package opens it to reflection. If a module exports a package using exports com.example.model; , consuming modules can compile against and invoke its public members. However, if a framework attempts to inspect or mutate a private field via reflection: // Inside a consuming module:

  • Class<?> clazz = Class.forName("com.example.model.Account");
  • Field balanceField = clazz.getDeclaredField("balance"); // private double balance;

balanceField.setAccessible(true); // THROWS EXCEPTION!

The JVM blocks the operation and throws: java.lang.reflect.InaccessibleObjectException: Unable to make field private double

  • com.example.model.Account.balance accessible: module com.example.billing does not
  • "opens com.example.model" to unnamed module @5b2133b1

Warning

The InaccessibleObjectException Trap An exports directive permits standard compilation against public members, but does not permit setAccessible(true) on non-public members from another module. To allow reflection to bypass access checks on private fields or constructors, the declaring module must use the opens directive.

The opens Directive

The opens directive is designed specifically to grant reflective access to a package at runtime without exposing it at compile time: module com.example.billing {

  • requires java.sql;
  • opens com.example.billing.entities;
  • }

Key Characteristics of opens :

Runtime Reflection Only : An opens directive does not export the package for compile-time access. Other modules cannot write import com.example.billing.entities.User; in their source files. Attempting to do so triggers a compile-time error.

Deep Reflective Access : At runtime, any module that reads com.example.billing can invoke setAccessible(true) on private, package-private, protected, and public members of types in com.example.billing.entities .

Combining exports and opens : A package can be simultaneously exported (for public compile-time APIs) and opened (for framework reflection): module com.example.billing {

  • exports com.example.billing.dto; // Public types accessible at compile time
  • opens com.example.billing.dto; // Deep reflection permitted at runtime
  • }
  • Qualified Opens ( opens ... to )

Granting unrestricted reflection to all modules can create security vulnerabilities. To mitigate this risk, JPMS provides qualified opens , which restrict deep reflection to specific designated consumer modules: module com.example.service {

  • opens com.example.service.model to org.hibernate.orm.core, com.fasterxml.jackson.databind;
  • }

In this declaration:

Only Hibernate and Jackson are permitted to invoke setAccessible(true) on private members within com.example.service.model .

Any other module attempting deep reflection on this package receives an InaccessibleObjectException at runtime.

The package remains completely hidden from compile-time imports across all modules.

Open Modules ( open module )

When developing entity-heavy applications or migrating enterprise microservices, specifying individual opens directives for dozens of model packages becomes tedious. JPMS provides the open module syntax to open an entire module at once: open module com.example.portal {

  • requires java.sql;
  • exports com.example.portal.api;
  • }

Rules Governing Open Modules:

All Packages Opened : Every package in an open module is automatically opened for deep reflection at runtime, including packages created dynamically or added in subsequent builds.

Selective Exporting : Packages within an open module are not automatically exported for compile-time access. You still use explicit exports directives to declare which packages form the public compile-time API.

Prohibition of opens Inside an Open Module : Because every package in an open module is already open, declaring an opens directive inside an open module produces a compile-time error : open module com.example.portal {

  • // COMPILE ERROR: 'opens' only allowed in strong modules
  • opens com.example.portal.internal;
  • }
  • Comparison: exports vs. opens

The following matrix contrasts the capabilities and rules of exports and opens directives:

  • Capability / Rule exports <pkg> opens <pkg> open module <name>
  • Target Argument Package name Package name Entire module
  • Compile-Time Public Access Yes No No (requires explicit exports )
  • Runtime Public Access Yes Yes (via reflection) Yes (via reflection)

Runtime Deep Reflection ( setAccessible ) No ( InaccessibleObjectException ) Yes (all members) Yes (all packages, all members)

  • Qualified Syntax ( to ... ) Supported ( exports ... to ) Supported ( opens ... to ) Not applicable
  • Permitted Inside open module ? Yes No (Compile error) Not applicable
  • CLI Flags and Programmatic Reflection Overrides

During testing or when transitioning legacy libraries that cannot be recompiled with modular descriptors, developers can dynamically modify module boundaries at JVM launch:

--add-opens <module>/<package>=<target-module> : Dynamically opens a package to a designated module (or ALL-UNNAMED for tools running on the classpath) for deep reflection at runtime without editing module-info.java .

--add-exports <module>/<package>=<target-module> : Dynamically exports a package to a designated module or ALL-UNNAMED , granting public access to types at compile time or runtime.

Inspecting Module Boundaries via java.lang.Module

Java provides runtime reflection over the module graph via the java.lang.Module class: Module myModule = getClass().getModule();

  • boolean isModelOpen = myModule.isOpen("com.example.billing.entities", frameworkModule);
  • boolean isApiExported = myModule.isExported("com.example.billing.api");
  • boolean readsSql = myModule.canRead(javaSqlModule);

These programmatic inspection methods allow runtime frameworks to verify that appropriate opens or exports directives have been configured before initiating deep reflective binding operations.