SaveMyCert
Log in
5 of 5 free questions left today·for 30 a day
Packaging and Deploying Java Code

Java Modules, Services and Module Imports (1Z0-831)

24 min read1Z0-831 · Packaging and Deploying Java CodeUpdated

A Java module is a named group of packages with a module-info.java declaration that states which modules it reads, which packages it exports or opens, and which services it uses or provides, and Oracle 1Z0-831 objective 7.1 asks you to define modules, expose their content (including to reflection), declare dependencies, wire services with ServiceLoader and use module import declarations. This lesson explains the three checks behind every cross-module access, how exports, qualified exports and opens differ, what requires transitive and requires static do at compile time and at launch, the graph rules that stop a module system from starting, how consumers find providers they never require, and exactly what import module brings into scope. Compiling, packaging into JARs, jlink and migrating class-path code belong to objective 7.2.

What you’ll learn
  • Write module declarations with requires, exports, opens, uses and provides, and predict whether code in another module compiles
  • Use per-package and qualified exports, and explain why subpackages, wildcards and duplicate exports fail
  • Choose between requires, requires transitive and requires static, and trace implied readability through a module graph
  • Recognise cyclic requires and split packages, and say whether each fails at compile time or at launch
  • Distinguish exports, opens and open modules for reflection, and predict InaccessibleObjectException
  • Define a service, a valid provider and a consumer, and trace ServiceLoader's lazy, cached behaviour
  • State what import module imports, when it compiles in a named module, and how name clashes between imports resolve

What a Java module is and what module-info.java declares

A Java module is a named, self-describing set of packages whose module-info.java file states which other modules it needs and which of its packages other code may use. The file sits at the root of the module's source tree, compiles to module-info.class, and contains a module declaration: the optional modifier open, the keyword module, a name (usually reverse-DNS style, such as com.acme.inventory) and a body of directives in any order. An empty body is legal.

DirectiveMeaning
requires m;This module reads module m, at compile time and run time
requires transitive m;Reads m, and every module that reads this one also reads m (implied readability)
requires static m;Reads m; m is mandatory at compile time, optional at run time
exports p; / exports p to m1, m2;Public types of package p are accessible to all modules / only to the named modules
opens p; / opens p to m1;Package p is open to deep reflection at run time (all members, including private)
uses S;This module consumes service S through ServiceLoader
provides S with Impl;This module supplies Impl as a provider of service S

The point of all this is strong encapsulation: inside a module, public now means public to the module. A public class in a package the module does not export is invisible to every other module, both to javac and to the JVM. Every module implicitly depends on java.base (the descriptor records it as requires java.base mandated), so java.lang, java.util, java.io, java.time and the other core packages need no directive. Any other platform module, such as java.sql, java.logging or java.net.http, must be required explicitly.

Exporting packages: the three checks for cross-module access

Code in one module can use a type from another module only when three checks all pass: the using module reads the other module, the type's package is exported to the using module, and the type (and the member used) is public. The checks are applied by javac and again at run time; failing any one of them is a compile error, not a warning.

exports works on exactly one package. Packages are not hierarchical as far as the module system is concerned: com.shop.api and com.shop.api.spi are unrelated, so each needs its own directive. There are no wildcards (exports com.shop.*; does not compile) and no comma list of packages. Exporting a package that has no types in the module is a compile error (package is empty or does not exist). Unexported packages are concealed: code inside the same module still uses them freely, which is how an exported class can delegate to an internal helper.

In the example below, catalog.format is concealed. Product may call Caps, but a reference to catalog.format.Caps from shop fails to compile with package catalog.format is not visible. Compiled with javac --module-source-path and run with java -p out -m shop/shop.Main, the program prints LAMP.

// catalog/module-info.java
module catalog {
    exports catalog;
}

// catalog/catalog/Product.java
package catalog;

public class Product {
    public String label(String name) {
        return catalog.format.Caps.upper(name);
    }
}

// catalog/catalog/format/Caps.java
package catalog.format;

public class Caps {
    public static String upper(String s) {
        return s.toUpperCase(java.util.Locale.ROOT);
    }
}

// shop/module-info.java
module shop {
    requires catalog;
}

// shop/shop/Main.java
package shop;

import catalog.Product;

public class Main {
    public static void main(String[] args) {
        System.out.println(new Product().label("lamp"));
    }
}

Hidden return types. An exported method may still declare a return type from a concealed package. Calling the method compiles, but using the result as that hidden type does not: with var the variable takes the hidden type, and calling a method on it is rejected because the type's package is not exported. Declare the variable with an exported supertype (an interface the hidden class implements) and the call compiles.

Qualified exports

A qualified export, exports p to m1, m2;, makes package p accessible to the named modules only, at compile time and at run time. Everyone else is treated exactly as if p were not exported, even a module that reads this one through a chain of requires transitive: reading a module and being exported to are separate checks.

module engine {
    exports engine.api;                 // every module
    exports engine.debug to console, profiler;
}
  • One directive per package, with a comma list of target modules. Two exports of the same package are a compile error (duplicate or conflicting exports).
  • A target module that does not exist only produces a javac warning (module not found); at run time it changes nothing, because qualified-export targets are never resolved.
  • Here engine.debug is visible to console and profiler only. If neither module exists yet, engine still compiles, with one module not found warning per missing target. Qualified exports are meant for tightly coupled modules of one product, such as a library and its own tooling.

Declaring dependencies: requires, requires transitive and requires static

A requires directive makes the declaring module read another module, and readability is not passed on: if report requires charts and charts requires geometry, report does not read geometry. That matters as soon as a public method of charts returns or accepts a geometry type, because callers then need to name or use that type. Inferring the type with var does not get around it: calling a member of a type from a module you do not read still fails to compile.

requires transitive geometry in charts fixes this with implied readability: every module that reads charts also reads geometry. The rule of thumb is that a module whose exported API exposes another module's types should require that module transitively. Exporting or opening a package is no substitute: those directives only apply to the module's own packages, and they never make another module readable. Implied readability follows only transitive edges, and it chains: in app → web (plain), web → http (transitive), http → codec (transitive), app reads web, http and codec. Make the last edge a plain requires codec and a codec type in app no longer compiles (package codec.x is not visible), while web and http types still do. Only the edges after the reader's own one matter: app's own requires can be plain.

requires static m is a compile-time dependency that is optional at run time, typical for annotations read only by tools or for optional integrations. At launch the module system does not follow static edges, so m is left out of the module graph even if it sits in the module-path directory, unless some other module requires it normally or the launch adds it with --add-modules m. When m is resolved by one of those routes, the module with the static requires reads it as usual.

Directive in appCompile timeRun timeReaders of app also read m?
requires mm requiredm required (launch fails if missing)No
requires transitive mm requiredm requiredYes
requires static mm requiredOptional; resolved only if something else brings it inNo

If an optional module is absent, code that touches its classes throws NoClassDefFoundError at the first such reference; the statements before it have already run. Guard the optional path by asking the boot layer whether the module was resolved:

// report/module-info.java
module report {
    requires static audit;
}

// report/report/Main.java
package report;

public class Main {
    public static void main(String[] args) {
        boolean auditing = ModuleLayer.boot()
            .findModule("audit")
            .isPresent();
        System.out.println("audit: " + auditing);
        if (auditing) audit.Log.write("run");
    }
}

Here audit exports a class audit.Log whose write method prints logged and its argument. With both modules compiled into out, java -p out --add-modules audit -m report/report.Main prints audit: true and then logged run. Leave out --add-modules and it prints only audit: false: the module is observable but not resolved, and the guard skips the call that would otherwise throw NoClassDefFoundError.

Module resolution and graph rules: java.base, cycles and split packages

At launch the module system builds the boot layer by resolution: it starts from the root module named in -m, follows every non-static requires edge through the modules it can observe on the module path and in the runtime image, and then binds services (next sections). A module that is merely observable, sitting in the directory but reached by no edge, is not part of the graph. The resulting graph must obey a few rules.

  • java.base is implicit. It is always read; no declaration needs, or should add, requires java.base.
  • No cycles in requires. Types may reference each other freely inside one module, but the requires directives between modules must form an acyclic graph. Two modules that require each other fail to compile with cyclic dependence involving the module. Fix it by merging the modules or by moving the shared types into a third module that both require.
  • No split packages. In a layer, each package belongs to exactly one module. If modules inventory and pricing both contain a package shared, each compiles, even with shared concealed in both, but when one root module requires the pair, creating the boot layer fails with LayerInstantiationException (Package shared in both module inventory and module pricing), so the JVM does not start. Renaming one package or merging the modules fixes it.

Errors therefore arrive at two different times. Missing readability, missing exports and cycles are compile errors. A missing required module at launch, or a split package, stops the JVM while initializing the boot layer, before main runs. An optional module that was never resolved shows up only when the code reaches it.

Exposing content to reflection: exports, opens and open modules

Reflection follows the same encapsulation, with one extra level: exports grants access to public types and public members (normally and reflectively), while opens grants deep reflection, meaning setAccessible(true) succeeds on any member, including private fields and constructors. Frameworks that inject or serialize private fields (dependency injection, ORMs, JSON mappers) need their target packages opened to them.

Declaration for package pCompile against p from another moduleReflect on public members at run timesetAccessible(true) on private members
Neither exported nor openedNoNo (IllegalAccessException)No (InaccessibleObjectException)
exports pYesYesNo (InaccessibleObjectException)
opens pNoYesYes
exports p and opens pYesYesYes
open module (every package opened)Only packages it also exportsYesYes

Loading a class by name with Class.forName works across modules either way; the checks happen when you access members. Here store.entity is exported but not opened, and the code asks politely with trySetAccessible():

// store/module-info.java
module store {
    exports store.entity;
}

// store/store/entity/Product.java
package store.entity;

public class Product {
    private String sku = "A-1";
    public String name() { return "lamp"; }
}

// ui/module-info.java
module ui {
    requires store;
}

// ui/ui/Main.java
package ui;

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("store.entity.Product");
        Object p = c.getDeclaredConstructor().newInstance();
        System.out.println(c.getMethod("name").invoke(p));
        Field f = c.getDeclaredField("sku");
        boolean ok = f.trySetAccessible();
        System.out.println(ok);
        if (ok) System.out.println(f.get(p));
    }
}

It prints lamp (the public method is reachable through the export) and then false: the export does not cover the private field, and trySetAccessible() reports that instead of throwing. Calling f.setAccessible(true) at the same point throws java.lang.reflect.InaccessibleObjectException, an unchecked exception whose message says module store does not "opens store.entity" to module ui. Adding opens store.entity to ui; to store (or an unqualified opens store.entity;, or declaring open module store) makes the same program print lamp, true and A-1.

  • The module that owns the class opens it. When a framework module reflects on your entity classes, your module declares opens my.model to framework;. Exporting the package to the framework is not enough.
  • opens takes a package name, like exports, with an optional to list. Naming a package that does not exist behaves differently for the two: exporting it is a compile error, while opening it compiles with only a warning (package is empty or does not exist), but the resulting module descriptor is invalid, so the launch fails while initializing the boot layer with FindException (caused by InvalidModuleDescriptorException: Package … not found in module). Either way the program never runs. A class name is not a package name, so opens my.model.Order falls into this case too.
  • An open module opens every package, exported or not, at run time, but it still grants compile-time access only to the packages it exports. It may contain exports, but an opens directive inside it is a compile error ('opens' only allowed in strong modules).
  • A launcher option, --add-opens, can open a package without changing the descriptor; it is a deployment workaround, not part of the module's design.

Defining services: consumers, providers and the service interface

A service is a type, usually an interface in an exported package, that a consumer module uses without knowing which provider modules implement it. The consumer declares uses S; and asks ServiceLoader for implementations; each provider module declares provides S with Impl; (a comma list is allowed: provides S with A, B;). The service name always comes first and the implementation after with.

// notice/module-info.java
module notice {
    exports notice;
}

// notice/notice/Sender.java
package notice;

public interface Sender {
    String send(String msg);
}

// sms/module-info.java
module sms {
    requires notice;
    provides notice.Sender with sms.Text, sms.Push;
}

// sms/sms/Text.java
package sms;

public class Text implements notice.Sender {
    public Text() { System.out.println("init Text"); }
    public String send(String m) { return "text: " + m; }
}

// sms/sms/Push.java
package sms;

public class Push implements notice.Sender {
    public Push() { System.out.println("init Push"); }
    public String send(String m) { return "push: " + m; }
}

// alerts/module-info.java
module alerts {
    requires notice;
    uses notice.Sender;
}

// alerts/alerts/Main.java
package alerts;

import java.util.ServiceLoader;
import notice.Sender;

public class Main {
    public static void main(String[] args) {
        var loader = ServiceLoader.load(Sender.class);
        loader.stream()
              .filter(p -> p.type().getSimpleName()
                            .equals("Push"))
              .map(ServiceLoader.Provider::get)
              .forEach(s -> System.out.println(s.send("hi")));
        System.out.println(loader.stream().count());
    }
}

Three things make this design work:

  • No requires edge to the provider. alerts requires only notice. During resolution the module system binds services: for every service a resolved module uses, it adds the observable modules that provide it. So sms joins the boot layer just because it is in out. Delete it and the same program runs with an empty loader.
  • The provider stays encapsulated. sms exports nothing; ServiceLoader creates Text and Push on the consumer's behalf, and consumers only ever see the Sender interface. A provider may even live in a concealed package, such as sms.internal.Push. The ServiceLoader documentation recommends exactly this. The provider module must read the service's module, and a module can only name providers that are in the module itself.
  • The consumer must declare uses. javac does not check the ServiceLoader.load call, so a named module without uses notice.Sender compiles, but ServiceLoader.load throws ServiceConfigurationError (module alerts does not declare `uses`) at run time. uses is only a declaration of intent: like provides, it creates no read edge, so the consumer still needs requires on the module that contains the service type (without it, uses java.sql.Driver; itself fails with package java.sql is not visible). Code in the unnamed module (the class path) needs no uses, and providers on the class path are registered in a META-INF/services/ file named after the service instead of with provides.

The program prints init Push, push: hi and 2, one per line. Text is never instantiated: the stream inspected both provider classes through type(), and only the one passed to get() was created. Counting the providers creates nothing either. That laziness is the subject of the ServiceLoader section below.

What a provider class must look like

The type named after with must be public, top level or a static nested type (never an inner class), and javac checks that it can actually produce a service instance in one of two ways.

  1. A provider method. If the type declares a public static method named provider with no parameters, whose return type is the service or a subtype of it, ServiceLoader calls that method. The type itself then need not implement the service, and it may even be an interface or an abstract class. The method takes precedence: a class that has both a provider method and a public no-argument constructor is obtained only through provider(), and the constructor is never called by the loader.
  2. A provider constructor. Otherwise the type must be a concrete class assignable to the service with a public no-argument constructor. A public class's implicit default constructor qualifies, and so does the canonical constructor of a public record with no components. An enum never qualifies, because enum constructors cannot be public, and an interface with only default methods has nothing to call.

A provider method needs all three of public, static and no parameters. Interface methods are implicitly public, so static Sender provider() in an interface counts, while the same method in a class without public is package-private and is ignored. The abstract class below cannot be instantiated at all, yet it is a valid provider because of its method. With mail as the only provider of notice.Sender on the module path, a consumer that loops over ServiceLoader.load(Sender.class) and prints s.send("hi") prints mail: hi.

// mail/module-info.java
module mail {
    requires notice;
    provides notice.Sender with mail.Mailer;
}

// mail/mail/Mailer.java
package mail;

import notice.Sender;

public abstract class Mailer implements Sender {
    public static Sender provider() {
        return m -> "mail: " + m;
    }
}
Provider type named after withValid?What javac checks
Public abstract class with public static Sender provider()YesProvider method; the class is never instantiated
Public static nested class implementing the service, no constructor declared (with mail.Mailer.Smtp)YesImplicit public no-argument constructor
Non-static inner class implementing the serviceNothe service implementation is an inner class
Public abstract class implementing the service, no provider methodNothe service implementation is an abstract class
Class whose public static provider() returns ObjectNoThe return type must be a subtype of the service
Class with only public static provider(int) and a private constructorNoA method with parameters is not a provider method, so the constructor rule applies and fails
Class with an instance method public Sender provider()NoOnly a static method counts

Using the ServiceLoader API

ServiceLoader.load(S.class) returns a lazy, caching loader: it locates and instantiates providers only as you ask for them, and remembers every instance it has created. Iterating twice over the same loader therefore creates each provider once, and the second loop gets the cached objects, in the same order.

CallBehaviour
for (S s : loader) / iterator()Yields cached instances first, then locates and instantiates the rest one at a time
findFirst()Optional<S> of the first provider, using the same cache; empty if there is none
stream()Stream<ServiceLoader.Provider<S>>; type() returns the provider class without instantiating it, get() creates or obtains the instance
reload()Clears the cache, so later calls instantiate providers again

The cache is shared by every way of reading one loader: a provider that a for loop created is the same object that a later findFirst() or iterator().next() returns, and its constructor ran only once. In the services example, the stream called only type() on Text, so init Text never printed, while get() on Push created it. stream() exists so code can choose providers by class, for example by an annotation on the class, before paying for instantiation. A second loader from another ServiceLoader.load call has its own cache, and reload() empties a loader's cache, so in both cases the next request creates a fresh instance (the constructor runs again and the new object is not == to the old one).

Zero providers is not an error. With no provider on the module path, findFirst() is empty, the iterator's hasNext() is false, and calling iterator().next() anyway throws NoSuchElementException, as any iterator would. ServiceConfigurationError is reserved for real configuration failures: a missing uses, or a provider that cannot be instantiated. When several modules provide the same service, the order of modules in a layer is not defined (providers within one module come in the order its provides lists them), so do not rely on which one comes first.

Module import declarations: what import module brings in

A module import declaration, import module M;, imports on demand every public top-level type in the packages that module M exports to the current module, plus the packages exported by the modules M requires transitively. It is a standard feature in Java SE 25 (JEP 511) and is written alongside ordinary imports. Nested types are reached through their outer type as usual, for example HttpClient.Version after import module java.net.http;.

The scope follows requires transitive. java.sql declares requires transitive on java.logging, java.xml and java.transaction.xa, so import module java.sql; also imports java.util.logging and javax.xml. It does not import java.util: every module depends on java.base, but that implicit dependence is not transitive. import module java.se; is the exception that covers everything, because java.se requires all the Java SE modules transitively, java.base included.

import module java.sql;

public class Main {
    public static void main(String[] args) {
        int code = Types.INTEGER;
        Level level = Level.INFO;
        System.out.println(code + " " + level);
    }
}

Run with java Main.java, this prints 4 INFO: Types comes from package java.sql and Level from java.util.logging in java.logging. A type from java.util or java.time in the same file would fail with cannot find symbol unless the file also imports that package (or module java.base).

The scope follows exports to you. Only packages exported to the importing module count, and a qualified export counts only for the modules it names. If module kit exports kit.core to everyone, kit.beta to app and kit.admin to ops, then import module kit; in ops imports kit.core and kit.admin; a simple name from kit.beta there is cannot find symbol.

A module import grants no readability. In a named module, import module M; compiles only if the module already reads M, either directly (requires M) or through implied readability, by requiring a module that declares requires transitive M. For java.sql that includes requires java.se (which requires every Java SE module transitively) and requires java.sql.rowset (whose descriptor declares requires transitive java.sql, because the rowset API extends JDBC types). Otherwise javac reports module app does not read: java.sql. Requiring a module that java.sql itself depends on, such as java.xml, points the wrong way and does not help; neither does uses on a java.sql type, which creates no read edge and fails to compile itself. Code in the unnamed module, such as a single file run with java Main.java, reads every resolved module, and the default set of resolved platform modules includes those that export an API, such as java.sql. java.se itself exports no packages, so it is not resolved by default: import module java.se; in such a file fails with unnamed module does not read: java.se unless the launch adds --add-modules java.se.

Name clashes: import shadowing and compact source files

When two imports supply the same simple name, Java resolves it by priority: a single-type import (import org.w3c.dom.Element;) shadows on-demand imports, and an on-demand package import (import javax.swing.text.*;) shadows module imports. Module imports are the weakest kind. Between imports of the same kind there is no winner, so using the clashing name is a compile error (reference to Element is ambiguous). The error is reported where the name is used, not at the import, the order of the import lines makes no difference, and an unused clash is harmless.

The table uses Element, which java.xml exports as org.w3c.dom.Element and java.desktop exports twice (in javax.swing.text and javax.swing.text.html.parser). Because java.desktop requires java.xml transitively, a single module import of java.desktop already brings in both modules' packages.

Imports in the fileElement means
import module java.xml;org.w3c.dom.Element
import module java.xml; + import javax.swing.text.*;javax.swing.text.Element (on-demand beats module import)
import module java.desktop; aloneAmbiguous: compile error at the use
import module java.desktop; + import org.w3c.dom.Element;org.w3c.dom.Element (single-type import wins)
import module java.xml;
import module java.desktop;
import org.w3c.dom.Element;

public class Main {
    public static void main(String[] args) {
        System.out.println(Element.class.getName());
    }
}

This prints org.w3c.dom.Element; delete the third import and it no longer compiles. Importing some other type, such as org.w3c.dom.Node, does not settle an Element clash, because shadowing applies only to the name being imported. The same priorities decide every clash, including names that java.base shares with another module (java.util.List and java.awt.List, or java.util.Date and java.sql.Date). In an ordinary class that imports only module java.sql, Date is java.sql.Date, because nothing imports java.util.

Compact source files (a file with top-level methods and no class declaration, such as void main() { ... }, part of objective 7.2) behave as if they began with import module java.base;. This file prints [INFO, FINE]: Level comes from the explicit import and List from the implicit one.

import module java.logging;

void main() {
    List<Level> levels = List.of(Level.INFO, Level.FINE);
    IO.println(levels);
}

The implicit import has module-import priority, so it clashes with any explicit module import that exports a type of the same simple name. A compact source file with import module java.desktop; that uses List fails as ambiguous (both class java.awt.List in java.awt and interface java.util.List in java.util match), while the same import in an ordinary class gives java.awt.List. A single-type import settles it either way. (IO is the java.lang.IO console class, imported like everything in java.lang.)

Tip. Questions on this objective are mostly multi-module code: several module-info.java files and a few classes, followed by 'What is the result?' or 'Which declaration makes it work?'. Expect to decide whether a reference compiles from readability, exports and public access together, to tell apart compile errors, boot-layer failures and run-time exceptions (NoClassDefFoundError, InaccessibleObjectException, ServiceConfigurationError, NoSuchElementException), and to pick which directives satisfy a stated access requirement. Service items test the uses/provides split, provider validity and ServiceLoader's lazy caching; module-import items test what import module covers, when it needs readability, and how clashing names resolve, including inside compact source files.

Key takeaways
  • Cross-module access needs three things: the module reads the other module, the package is exported to it, and the type and member are public.
  • exports names exactly one package; subpackages are unrelated, wildcards do not exist, and a duplicate export of the same package is a compile error.
  • exports p to m1, m2 limits access to the named modules; a missing target only warns, and readability through requires transitive never widens it.
  • Readability is not transitive unless declared: requires transitive gives implied readability and belongs wherever your API exposes another module's types.
  • requires static is mandatory at compile time and optional at run time; the module is resolved only if something else requires it or --add-modules adds it.
  • Cyclic requires fail to compile; a package in two modules (even concealed) stops the boot layer from initializing.
  • exports gives public access; opens (or open module) is needed for setAccessible on private members, otherwise InaccessibleObjectException; opening a missing package warns at compile time but fails at launch; open modules cannot contain opens.
  • Consumers declare uses (or ServiceLoader throws ServiceConfigurationError), providers declare provides S with Impl, and service binding pulls providers in without any requires; uses and provides never create readability.
  • A provider is public and either has a public static provider() method, which wins, or is a concrete subtype of the service with a public no-argument constructor.
  • import module M imports the packages M (and its transitive dependencies) export to you, grants no readability, and loses to on-demand and single-type imports; compact source files import module java.base implicitly.

Frequently asked questions

What is the difference between exports and opens in a Java module?

exports makes a package's public types and public members accessible to other modules at compile time and at run time. opens makes every member of the package, including private ones, available to deep reflection at run time (setAccessible(true) succeeds), but grants no compile-time access. A package can be both exported and opened, and either directive can be qualified with to followed by module names.

When should a Java module use requires transitive?

Use requires transitive m when your module's exported API exposes types from module m, for example a public method that returns or accepts an m type. Every module that reads yours then also reads m, so callers can use those types without adding requires m themselves. Implied readability passes only along transitive edges; a plain requires in the chain stops it.

What does requires static mean in module-info.java?

requires static m makes module m mandatory when compiling but optional at run time. At launch the module system does not follow static edges, so m is resolved only if another module requires it normally or the command line adds it with --add-modules. If it is absent, code that reaches m's classes throws NoClassDefFoundError, so optional code usually checks ModuleLayer.boot().findModule("m") first.

Why does setAccessible throw InaccessibleObjectException in a modular application?

setAccessible(true) on a non-public member, such as a private field, requires the member's package to be opened to the calling module, through opens p, opens p to caller, or an open module declaration. Exporting the package is not enough, because exports covers only public members. The owner of the class must open it; InaccessibleObjectException is an unchecked exception in java.lang.reflect.

Does a service consumer module need to require the provider module?

No. The consumer requires the module that contains the service type and declares uses for it. At launch, service binding adds every observable module that declares provides for a used service, so the provider joins the module graph without any requires edge, and the provider's package does not need to be exported. If no provider is present, ServiceLoader simply finds nothing. The consumer does still need requires on the module that contains the service type, because uses and provides create no readability.

What happens if a module calls ServiceLoader.load without a uses directive?

The code compiles, because javac does not check ServiceLoader calls, but at run time ServiceLoader.load throws ServiceConfigurationError reporting that the module does not declare uses for that service. Every named module that loads a service must declare uses with the service type. Code in the unnamed module on the class path is exempt.

What does import module java.sql import?

It imports on demand all public top-level types in the packages java.sql exports (java.sql and javax.sql) plus the packages of the modules java.sql requires transitively: java.logging, java.xml and java.transaction.xa. It does not import java.util or any other java.base package, because the implicit dependence on java.base is not transitive. In a named module the importing module must also read java.sql.

How does Java resolve a name that two imports both provide?

A single-type import shadows on-demand imports, and an on-demand package import such as import javax.swing.text.* shadows module imports. If the competing imports have the same priority, for example two module imports that both supply a type called Element, using the simple name is a compile error saying the reference is ambiguous. Adding a single-type import for the intended class resolves the clash; importing a different class from the same package does not.

Source

This lesson covers the "Packaging and Deploying Java Code" domain of the official 1Z0-831 exam guide. Vendors revise their guides — check the source for the current version.

Test yourself on this topic
Practice now

Sign up free to mark lessons complete, bookmark topics and track your exam readiness.

Spotted a mistake in this lesson?