Java Platform Module System (JPMS) practice questions

From OCP Java SE 25 (1Z0-831) · 18 questions on this topic

Java Platform Module System (JPMS) practice questions from OCP Java SE 25 (1Z0-831). This pack has 18 questions tagged Java Platform Module System (JPMS), drawn from its timed mock exams. 8 of them are worked through in full below — the question, every option, why each is right or wrong, and the explanation.

Worked examples for Java Platform Module System (JPMS)

  1. Question 1

    A developer imports the whole of java.base with a module import declaration and adds an on-demand import for java.awt. What is the result of compiling and running this program? ```java import module java.base; import java.awt.*; public class Main { public static void main(String[] args) { List<String> items = new ArrayList<>(); items.add("go"); System.out.println(items.size()); } } ```

    1. A. 1

      Assumes the module import wins so the name means the collection type; a type-import-on-demand shadows a module import, so the name means the AWT type.

    2. B. Compilation failsCorrect answer

      The on-demand import of the AWT package shadows the module import, so the name resolves to the non-generic AWT type, and the parameterised use fails to compile.

    3. C. 0

      Assumes the compiler prefers the generic candidate and the program runs; the name resolves to the non-generic AWT type, a compile error.

    4. D. Throws ClassCastException

      Assumes a runtime cast failure; the conflict is a compile-time shadowing error, so nothing runs.

    Explanation

    This is shadowing, not ambiguity: a type-import-on-demand declaration shadows a same-named type brought in by a module import declaration, which sits lowest in import precedence. So the simple name resolves to the AWT type, and using it with type arguments, since that type is not generic, is a compile-time error. A single-type import would shadow both and select the intended type.

  2. Question 2

    What is the jlink tool used for?

    1. A. Linking .class files into a single executable fat JAR

      Bundling classes into a fat JAR is a build-tool or `jar` task; jlink produces a runtime image, not a JAR.

    2. B. Generating a candidate module-info.java for a plain JAR by analysing its bytecode dependencies

      Analysing bytecode to propose a module-info.java is what `jdeps --generate-module-info` does, not jlink.

    3. C. Downloading and resolving module dependencies from a remote repository at build time

      jlink resolves modules from the local module path; it is not a dependency downloader like a package manager.

    4. D. Assembling a set of modules and their transitive dependencies into a custom, self-contained runtime imageCorrect answer

      Correct. From `--module-path` and `--add-modules`, jlink resolves the named modules plus everything they transitively require and links them into a trimmed, self-contained runtime image with its own launcher.

    Explanation

    jlink is the Java linker: given a module path and a set of root modules, it resolves them together with their transitive dependencies and assembles them into a custom, self-contained runtime image that contains only the modules the application needs. That is distinct from packaging classes into a JAR, proposing a module descriptor, or fetching dependencies from a repository, each of which is another tool's job.

  3. Question 3

    Which task is the jdeps tool designed for?

    1. A. Creating a reduced runtime image containing only the modules an application uses

      Building a reduced runtime image is jlink's job, not jdeps's.

    2. B. Analysing the class and package dependencies of code, and generating a candidate module-info.java with --generate-module-infoCorrect answer

      Correct. jdeps reports package- and class-level dependencies of class files or JARs and with `--generate-module-info` emits a proposed module-info.java based on the dependencies it observes.

    3. C. Packaging compiled classes and resources into a .jmod file for the module path

      Producing .jmod files is the jmod tool's job.

    4. D. Launching a modular application by resolving the initial module from the module path

      Launching a modular application is done with `java --module-path ... -m module/MainClass`, not jdeps.

    Explanation

    jdeps is the class-dependency analyser: it reports the package- and class-level dependencies of compiled code and, with its generate-module-info option, proposes a module descriptor from the dependencies it actually observes — the usual first step in modularising a legacy JAR. It only reads dependencies and never builds runtime images, packages archives, or launches applications.

  4. Question 4

    A reporting module is declared as follows. ```java module com.acme.report { requires static com.acme.annotations; requires java.xml; exports com.acme.report.api to com.acme.web; opens com.acme.report.dto to com.acme.web; } ``` Which TWO statements about this declaration are correct? (Choose two.)

    1. A. A module com.acme.batch that declares `requires com.acme.report` still cannot compile against the types in com.acme.report.api.Correct answer

      Correct: exports ... to com.acme.web is a qualified export, and readability and accessibility are separate gates; com.acme.batch reads the module but is not a named target, so it cannot compile against com.acme.report.api.

    2. B. com.acme.web can compile against the public types in com.acme.report.dto, because that package is opened to com.acme.web.

      Incorrect: opens grants only deep reflective access at run time, not compile-time accessibility, so importing a type from com.acme.report.dto fails even though the same module can reflect on it.

    3. C. Any module that declares `requires com.acme.report` also reads java.xml, so it may use org.w3c.dom types without requiring java.xml itself.

      Incorrect: this confuses requires with requires transitive; a plain requires java.xml is not re-exported, so a consumer of com.acme.report does not read java.xml and cannot use org.w3c.dom without its own requires.

    4. D. com.acme.annotations must be on the module path when com.acme.report is compiled, but com.acme.report still resolves and the application starts if that module is absent at run time.Correct answer

      Correct: requires static declares a dependence that is mandatory at compile time and optional at run time, so com.acme.annotations must be present to compile but the app still starts if it is absent at run time.

    Explanation

    Why `com.acme.annotations must be on the module path when com.acme.report is compiled ...` is correct: `requires static` declares a dependence that is mandatory at compile time and optional at run time. Compiling com.acme.report without com.acme.annotations available fails with `module not found: com.acme.annotations`; but once compiled, deleting that module from the module path and running the application succeeds, because static requires are not resolved at run time unless something else pulls them in. Why `A module com.acme.batch that declares requires com.acme.report still cannot compile ...` is correct: `exports com.acme.report.api to com.acme.web;` is a qualified export. Readability (`requires`) and accessibility (`exports`) are two separate gates and a module must pass both. com.acme.batch reads com.acme.report but is not a named target, so javac rejects it with `package com.acme.report.api is not visible ... which does not export it to module com.acme.batch`. Why the others are wrong: `Any module that declares requires com.acme.report also reads java.xml ...` confuses `requires` with `requires transitive`. A plain `requires java.xml` is not re-exported, so readability stops at com.acme.report; a consumer naming org.w3c.dom.Node gets `package org.w3c.dom is not visible ... but module com.acme.web does not read it`. `com.acme.web can compile against the public types in com.acme.report.dto ...` confuses `opens` with `exports`. `opens` grants only deep reflective access at run time — it does not make the package compile-time accessible. Compiling `import com.acme.report.dto.Row;` from com.acme.web fails with `package com.acme.report.dto is not visible ... which does not export it`, even though the very same module can call `setAccessible(true)` on Row's private fields at run time. Exam tip: keep the four gates separate. `requires` = readability; `exports` = compile-time + run-time access to public API; `opens` = run-time deep reflection only; `requires static` = compile-time only. The classic reverse trap is assuming a package opened to you can be imported — reflection frameworks like Jackson need `opens`, never `exports`.

  5. Question 5

    Another team drops `billing.jar`, a modular JAR, into your `libs` directory. No sources are supplied. Before you wire it into your own module you need to see exactly which packages it exports and which modules it requires. Which single command prints that information without running any application code?

    1. A. jdeps --list-deps libs/billing.jar

      Lists which modules this JAR's own code depends on, the opposite question; it reveals nothing about which packages the module exports or which modules it requires in its descriptor.

    2. B. jlink --module-path libs --add-modules com.acme.billing --output image

      Builds a stripped-down runtime image containing the module rather than reporting anything; it does not print the module's exports and requires.

    3. C. java -p libs --describe-module com.acme.billingCorrect answer

      Prints the module descriptor — its exports (including qualified ones), requires (flagged transitive/mandated), plus uses, provides and opens — then exits without launching a main class, with -p making the JAR observable on the module path (JDK 25 java(1)).

    4. D. java -p libs --list-modules

      Prints only the names (with version and location) of the observable modules, never the descriptor, so it does not show the exported packages or required modules being sought.

    Explanation

    `java --describe-module <name>` prints the module's descriptor — its `exports` (including qualified `exports ... to`), its `requires` (flagged `transitive`/`mandated`), plus `uses`, `provides`, `opens` and `contains` — and then exits without launching a main class; `-p` puts the JAR on the module path so the module is observable. The near-miss is `--list-modules`: it prints only the NAMES (and version/location) of the observable modules, e.g. `com.acme.billing file:///.../billing.jar`, never the descriptor. `jdeps --list-deps` answers the opposite question — which modules this JAR's *code depends on* — and says nothing about what it exports; `jlink` builds a stripped-down runtime image.

  6. Question 6

    What is the result of compiling this module declaration? ```java open module com.data { opens com.data.model; } ```

    1. A. It compiles; the opens directive is redundant but harmless

      The directive is not merely redundant; combining it with `open module` is a compile-time error, not a harmless warning.

    2. B. It does not compile: 'opens' is only allowed in a strong (non-open) module — an open module already opens every packageCorrect answer

      Correct. An open module already opens every package for deep reflection, so an explicit `opens` directive is contradictory and javac rejects it with "'opens' only allowed in strong modules".

    3. C. It does not compile: an open module must also declare at least one exports directive

      There is no rule requiring an open module to export anything; opening and exporting are independent concerns.

    4. D. It compiles; only com.data.model is opened for reflection and the module's other packages stay encapsulated

      An open module cannot narrow openness to a single package; it opens all of them, which is precisely why the extra directive is disallowed.

    Explanation

    An open module opens every one of its packages for deep reflection at run time, so an explicit `opens` directive inside it is contradictory and rejected at compile time. Selective `opens` directives belong only to a strong (ordinary) module, which is closed by default; you choose whole-module openness or selective opening, never both in one declaration.

  7. Question 7

    What does this program print? ```java import module java.base; public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(List.of(3, 1, 2)); TreeMap<String, Integer> ages = new TreeMap<>(); ages.put("kim", 30); ages.put("ana", 25); String csv = nums.stream().sorted().map(String::valueOf).collect(Collectors.joining(",")); Duration d = Duration.ofMinutes(2); System.out.print(csv + " first=" + ages.firstKey() + " sec=" + d.toSeconds()); } } ```

    1. A. 1,2,3 first=kim sec=120

      Assumes `firstKey()` returns the first inserted key. A TreeMap is sorted, so the smallest key comes first and the first key is "ana", not "kim".

    2. B. 1,2,3 first=ana sec=120Correct answer

      Correct. `import module java.base;` supplies List, ArrayList, TreeMap, Stream, Collectors and Duration; `sorted()` orders the values as 1,2,3; a TreeMap's `firstKey()` is the natural-order smallest key "ana"; and `Duration.ofMinutes(2).toSeconds()` is 120.

    3. C. 3,1,2 first=ana sec=120

      Ignores the `sorted()` call and prints the numbers in insertion order instead of ascending order.

    4. D. Compilation fails: List, ArrayList, TreeMap, Collectors and Duration all need explicit imports

      This would be true without the module import, but `import module java.base;` supplies exactly these types, so the program compiles with no explicit imports.

    Explanation

    A single `import module java.base;` makes every public top-level type of java.base's exported packages resolvable, so the collection, stream, and time types used here need no further imports. Streaming the numbers through `sorted()` yields ascending order; a TreeMap keeps its keys in natural (sorted) order, so its first key is the alphabetically smallest one rather than the first inserted; and two minutes converts to 120 seconds.

  8. Question 8

    Modules com.a and com.b each contain and export the package com.shared. A third module declares: ```java module com.c { requires com.a; requires com.b; } ``` What happens when you compile com.c together with com.a and com.b?

    1. A. It compiles; com.c sees the union of the public types from both copies of com.shared

      There is no merging of split packages; the reader is not allowed to see the package from two modules at all, so no union of types occurs.

    2. B. It compiles, but a LinkageError is thrown at run time when com.shared is first loaded

      The conflict is caught at compile/resolution time, not deferred to a run-time LinkageError.

    3. C. It does not compile: module com.c reads package com.shared from both com.a and com.b (a split package)Correct answer

      Correct. com.a and com.b both export com.shared and com.c requires both, so com.c would read com.shared from two modules; the compiler rejects this split package at resolution time.

    4. D. It compiles; the later requires (com.b) shadows com.a for the split package

      `requires` directives are unordered and there is no shadowing rule; a duplicate readable package is simply an error, not something the later requires resolves.

    Explanation

    The module system requires every package a module reads to come from a single source module. Because two required modules both export the same package, the reader would receive that package from two modules at once — a split package — which is rejected during resolution at compile time. Ordering the requires directives does not help, since they are unordered and there is no shadowing rule for duplicate readable packages.

Practise all 18 Java Platform Module System (JPMS) questions

OCP Java SE 25 has the full set, inside timed mock exams that mirror real exam conditions — every question with a worked explanation.

Open OCP Java SE 25

Other topics in this pack