Try 50 free Java 17 1Z0-829 questions across the exam domains, with explanations, then continue with full IT Mastery practice.
This free full-length Java 17 1Z0-829 practice exam includes 50 original IT Mastery questions across the exam domains.
These questions are for self-assessment. They are not official exam questions and do not imply affiliation with the exam sponsor.
Count note: this page uses the full-length practice count maintained in the Mastery exam catalog. Some certification vendors publish total questions, scored questions, duration, or unscored/pretest-item rules differently; always confirm exam-day rules with the sponsor.
Need concept review first? Read the Java 17 1Z0-829 Cheat Sheet on Tech Exam Lexicon, then return here for timed mocks and full IT Mastery practice.
Open the matching IT Mastery practice page for timed mocks, topic drills, progress tracking, explanations, and full practice.
Try Java 17 1Z0-829 on Web View full Java 17 1Z0-829 practice page
| Domain | Weight |
|---|---|
| Handling Date, Time, Text, Numeric, and Boolean Values | 11% |
| Controlling Program Flow | 6% |
| Utilizing Java Object-Oriented Approach | 20% |
| Handling Exceptions | 7% |
| Working with Arrays and Collections | 10% |
| Working with Streams and Lambda Expressions | 10% |
| Packaging, Deploying, and Java Platform Module System | 6% |
| Managing Concurrent Code Execution | 10% |
| Using Java I/O API | 6% |
| Accessing Databases Using JDBC | 4% |
| Implementing Localization | 3% |
| Logging API and Standard Annotations | 7% |
Use this as one diagnostic run. IT Mastery gives you timed mocks, topic drills, analytics, code-reading practice where relevant, and full practice.
Topic: Handling Exceptions
A developer is refactoring a method so Java 17 try-with-resources closes the selected reader. The current code does not compile and must be fixed without opening an extra unclosed reader. Which refactor is best?
static int first(Path path, boolean utf16) throws IOException {
Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
if (utf16) {
reader = Files.newBufferedReader(path, StandardCharsets.UTF_16);
}
try (reader) {
return reader.read();
}
}
Options:
A. Choose the charset first, create reader once, then use try (reader).
B. Keep the code but change the header to try (Reader r = reader).
C. Add final to reader and keep the conditional reassignment.
D. Keep the reassignment and change only Reader to var.
Best answer: A
Explanation: Java 17 allows an existing local variable in a try-with-resources header only if that variable is final or effectively final. The original reader is reassigned, so try (reader) is illegal. Selecting the charset first lets the code create one effectively final reader and then reuse it safely.
A try-with-resources resource may be a declaration or a reference to an existing final or effectively final variable. A variable is not effectively final if it is reassigned after initialization. In the original method, reader may be assigned once for UTF-8 and then reassigned for UTF-16, so it cannot be used as try (reader). The best refactor is to compute the charset first, then create reader exactly once and use that variable in the resource list.
Declaring a new resource variable from the reassigned reader can compile, but it does not fix the extra unclosed reader opened before reassignment.
final local variable cannot be conditionally reassigned.var changes type inference only; it does not make a reassigned variable effectively final.Topic: Working with Arrays and Collections
A defect appears in this Java 17 code. The method must be able to store a Double value in the same array.
class Metrics {
static void update(Number[] values) {
values[0] = 3.5;
}
public static void main(String[] args) {
Integer[] values = {1, 2, 3};
update(values);
System.out.println(values[0]);
}
}
Which refactor is the best fix?
Options:
A. Declare Number[] values = new Integer[] {1, 2, 3};
B. Create a Number[]: Number[] values = {1, 2, 3}; update(values);
C. Change update to accept Object[] and keep Integer[].
D. Keep Integer[] and call update((Number[]) values);
Best answer: B
Explanation: Java arrays are covariant, so an Integer[] can be passed where a Number[] is expected. However, the array still has runtime type Integer[], so storing a Double causes an ArrayStoreException. Creating an actual Number[] fixes the runtime incompatibility.
The core issue is the difference between an array’s declared type and its runtime component type. In the original code, update(Number[]) accepts the call because Integer[] is a subtype of Number[]. But the object is still an Integer[], and arrays check stores at runtime. Assigning 3.5 boxes to a Double, which cannot be stored in an Integer[], so the program fails with ArrayStoreException.
Declaring and creating a Number[] gives the array a runtime component type of Number, allowing both Integer and Double elements. Merely changing the reference type or adding a cast does not change the actual array object.
Integer[], so storing a Double remains invalid.Integer[] able to store non-Integer objects.Topic: Working with Arrays and Collections
Which statement correctly describes Java 17 generic collections that use raw types and wildcard bounds?
Options:
A. A raw List prevents heap pollution because generic type checks are performed at runtime.
B. A raw List can cause unchecked warnings and heap pollution; List<? extends Number> cannot accept added Integer values.
C. List<? super Integer> returns Integer from get() without a cast because it accepts Integer values.
D. List<? extends Number> accepts any Number subtype because all elements are readable as Number.
Best answer: B
Explanation: Java generics use compile-time checks with type erasure, so raw types bypass important generic checking and can create heap pollution. An extends wildcard is producer-oriented: you can read values as the upper bound, but you cannot safely add specific subtype values such as Integer.
A raw type such as List removes the generic type argument from compile-time checking. Assigning or writing through a raw reference can produce unchecked warnings, and adding the wrong element type can pollute an object that is also viewed as a List<String> or another parameterized type. With List<? extends Number>, the actual list might be a List<Integer>, List<Double>, or some other subtype list, so adding an Integer is not type-safe. The only universally safe value to add is null. In contrast, List<? super Integer> can accept Integer values, but values read from it have type Object.
Number does not mean writable with every Number subtype.super wildcard supports safe writes but only guarantees Object when reading.Topic: Implementing Localization
Assume needed imports exist. A report stores event times as Instant. For a French-speaking reviewer, the report must use French month/currency formatting, but the displayed event time must be the UTC calendar date and clock time for the instant. Which replacements for the placeholders meet both requirements?
var event = Instant.parse("2024-01-01T00:30:00Z");
var total = 1234.56;
var dateFmt = DateTimeFormatter.ofPattern("MMM d, uuuu HH:mm", /* date locale */)
.withZone(/* date zone */);
var moneyFmt = NumberFormat.getCurrencyInstance(/* money locale */);
System.out.println(dateFmt.format(event));
System.out.println(moneyFmt.format(total));
Options:
A. Date locale Locale.FRANCE; date zone ZoneId.of("UTC"); money locale Locale.FRANCE
B. Date locale Locale.US; date zone ZoneId.of("UTC"); money locale Locale.FRANCE
C. Date locale Locale.FRANCE; date zone ZoneId.of("UTC"); money locale Locale.US
D. Date locale Locale.FRANCE; date zone ZoneId.of("Europe/Paris"); money locale Locale.FRANCE
Best answer: A
Explanation: Locale affects presentation conventions such as month names, currency symbols, and separators. ZoneId affects how an Instant is converted into displayed calendar date and clock time. The requirement needs French formatting with a UTC temporal projection.
Locale-sensitive formatting and time-zone-sensitive temporal behavior are separate concerns. In this code, Locale.FRANCE makes MMM use French month text and makes NumberFormat.getCurrencyInstance() use French currency formatting conventions. The ZoneId supplied to DateTimeFormatter.withZone() determines which local date and time fields are computed from the Instant. To display the instant as UTC time, the formatter must use ZoneId.of("UTC"), not the reviewer’s regional zone.
Using Europe/Paris would still represent the same instant, but it would display Paris local clock time rather than UTC clock time.
MMM would use US English month text instead of French text.Topic: Working with Streams and Lambda Expressions
A service needs a method that returns the doubled length of each name as a List<Integer>. The length calculation should use a primitive stream before collecting.
static List<Integer> sizes(List<String> names) {
// choose one return statement
}
Which return statement meets the requirement?
Options:
A. return names.stream().map(String::length).map(n -> n * 2).boxed().toList();
B. return names.stream().mapToInt(String::length).map(n -> n * 2).boxed().toList();
C. return names.stream().mapToInt(String::length).map(n -> n * 2).toList();
D. return names.stream().mapToInt(String::length).boxed().mapToInt(n -> n * 2).toList();
Best answer: B
Explanation: The correct pipeline crosses from an object stream to a primitive stream with mapToInt, performs primitive int mapping, then crosses back with boxed(). In Java 17, toList() is available on Stream<T>, not on IntStream.
Object streams and primitive streams have separate APIs. Stream<String>.mapToInt(String::length) returns an IntStream, whose map method takes an IntUnaryOperator and still returns an IntStream. To collect into List<Integer> using toList(), the primitive stream must be converted back to Stream<Integer> with boxed(). Calling boxed() on a regular Stream<Integer> is invalid, and calling toList() directly on IntStream is invalid in Java 17. The key distinction is the stream type at each step, not the lambda arithmetic.
boxed() is not a method on Stream<Integer>.mapToInt returns an IntStream, which does not have toList() in Java 17.IntStream before collection again leaves no toList() method available.Topic: Working with Streams and Lambda Expressions
A batch job assigns shard numbers. The method must create a stream containing the primitive int values 1 through 5, inclusive, and pass it to an API that requires an IntStream. With java.util.* and java.util.stream.* imported, which declaration correctly applies the Java SE 17 stream source rule?
Options:
A. IntStream ids = List.of(1, 2, 3, 4, 5).stream();
B. IntStream ids = IntStream.range(1, 5);
C. IntStream ids = IntStream.rangeClosed(1, 5);
D. IntStream ids = Stream.of(1, 2, 3, 4, 5);
Best answer: C
Explanation: Primitive range streams use IntStream.range() or IntStream.rangeClosed(). Because the requirement includes both 1 and 5 and needs a primitive IntStream, rangeClosed(1, 5) matches the bounds and stream type.
Java provides primitive stream factories for numeric ranges. IntStream.range(startInclusive, endExclusive) excludes the second argument, while IntStream.rangeClosed(startInclusive, endInclusive) includes it. Since the stream must contain exactly 1, 2, 3, 4, and 5 as primitive int values, the inclusive range factory is the direct match. Factory methods such as Stream.of(1, 2, 3) and collection stream() methods create object streams, such as Stream<Integer>, not IntStream. Those streams can be converted with mapToInt(Integer::intValue), but they are not directly assignable to IntStream.
range(1, 5) produces 1 through 4 only.Stream.of(1, 2, 3, 4, 5) produces Stream<Integer>.List.of(...).stream() also produces a boxed Stream<Integer>, not an IntStream.Topic: Handling Exceptions
A developer writes the following Java 17 code. No imports are needed. What is the result of compiling and running it?
class MissingConfigException extends Exception {}
class BadConfigException extends RuntimeException {}
public class App {
static void load(boolean present)
throws MissingConfigException, BadConfigException {
if (!present) throw new MissingConfigException();
throw new BadConfigException();
}
static void start() {
load(true);
}
public static void main(String[] args) {
start();
}
}
Options:
A. Compilation fails; start() must handle MissingConfigException.
B. It compiles, then throws BadConfigException.
C. Compilation fails; start() must handle both exception types.
D. It compiles and exits normally.
Best answer: A
Explanation: The compile-time rule is based on the declared exception type, not on the argument value or expected runtime path. Because load() declares the checked exception MissingConfigException, start() must catch it or declare it. BadConfigException is unchecked and does not require handling.
Checked exceptions must be either caught or declared by any method that can invoke code declaring them. Here, MissingConfigException extends Exception, so it is checked. The call load(true) is still treated as a call to a method that declares throws MissingConfigException; the compiler does not ignore that declaration based on the literal argument. BadConfigException extends RuntimeException, so it is unchecked and may be caught, declared, or ignored.
The key takeaway is that checked-exception handling is enforced from the method signature at compile time.
BadConfigException ignores that compilation is checked before runtime behavior.Topic: Controlling Program Flow
Which statement is a correct Java 17 rule for a switch expression that uses arrow labels and is used to initialize a variable?
Options:
A. Arrow-labeled cases fall through unless they end with break.
B. It must be exhaustive, and an arrow block must yield a value or throw.
C. An arrow block returns its value with break value;.
D. A default label is required for every switch expression.
Best answer: B
Explanation: A Java 17 switch expression must be exhaustive and produce a value compatible with the target type, unless the selected path throws. With arrow labels, a block body does not implicitly return its last expression; it must use yield to provide the result.
A switch expression is an expression, so Java must be able to determine a result for the assignment. Arrow rules do not fall through. A rule may directly provide an expression, throw an exception, or use a block. When a block is used to produce the expression’s value, the block uses yield value;. Exhaustiveness is also required, but a default is not always mandatory, such as when all constants of an enum are covered.
yield, not break value;, for switch expression results.default.Topic: Working with Streams and Lambda Expressions
A developer wants to process primitive int values without boxing. Given this Java 17 code, which declaration can replace the comment so the program compiles and prints 12?
import java.util.function.*;
import java.util.stream.*;
public class Report {
public static void main(String[] args) {
// insert declaration here
var total = IntStream.of(1, 2, 3)
.map(calc)
.sum();
System.out.println(total);
}
}
Options:
A. IntFunction<Integer> calc = n -> n * 2;
B. Function<Integer, Integer> calc = n -> n * 2;
C. IntUnaryOperator calc = n -> n * 2;
D. UnaryOperator<Integer> calc = n -> n * 2;
Best answer: C
Explanation: IntStream uses primitive functional interfaces, not the generic boxed interfaces used by Stream<T>. The map() method on IntStream expects an IntUnaryOperator, so doubling 1, 2, 3 produces 2, 4, 6, and the sum is 12.
Primitive streams have specialized method signatures to avoid boxing. For IntStream, map() is declared to take an IntUnaryOperator, whose functional method accepts an int and returns an int. Java does not automatically convert a variable typed as Function<Integer, Integer> or UnaryOperator<Integer> into an IntUnaryOperator, even if the lambda body looks compatible. IntFunction<Integer> also takes an int, but it returns an object and is used with APIs such as mapToObj(), not IntStream.map().
The key rule is to match the functional interface type required by the stream method’s signature.
UnaryOperator<Integer> is for object values, not IntStream.map().Function<Integer, Integer> matches boxed Stream<Integer> operations, not primitive IntStream.map().IntFunction<Integer> returns an object and fits mapToObj(), not map().Topic: Managing Concurrent Code Execution
A service keeps a read-mostly list of listeners. One thread iterates over the list to send notifications while other threads may occasionally add or remove listeners. Which Java 17 collection rule best supports safe iteration without caller-side locking?
Options:
A. ArrayList fail-fast iterators make concurrent iteration safe.
B. CopyOnWriteArrayList iterators use a stable snapshot.
C. ConcurrentHashMap iterators block updates until completion.
D. Collections.synchronizedList synchronizes iteration automatically.
Best answer: B
Explanation: CopyOnWriteArrayList is designed for read-mostly cases where iteration should be safe while occasional updates occur. Its iterators are snapshot-based, so they do not require external locking and do not fail because another thread modifies the list.
Concurrent collection choice depends on both update safety and iterator behavior. CopyOnWriteArrayList creates a new internal array on each structural modification, and its iterator reads the array snapshot that existed when the iterator was created. This makes iteration safe without caller-side locking, but updates are relatively expensive, so it fits read-mostly workloads.
The closest alternative, Collections.synchronizedList, synchronizes individual method calls but requires the caller to manually synchronize on the list while iterating.
ArrayList iterators are not a concurrency safety mechanism.ConcurrentHashMap iterators are weakly consistent, not locking iterators.Topic: Handling Date, Time, Text, Numeric, and Boolean Values
A scheduler stores deadlines as dates only. This code fails while adding one grace day, and the design should remain date-only:
import java.time.*;
class Scheduler {
public static void main(String[] args) {
var due = LocalDate.of(2024, 2, 28);
var grace = Duration.ofDays(1);
System.out.println(due.plus(grace));
}
}
It throws java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds. What is the best fix?
Options:
A. Change due to LocalTime.of(0, 0).
B. Use Duration.ofHours(24) for grace.
C. Use Period.ofDays(1) for grace.
D. Call due.plusSeconds(grace.toSeconds()).
Best answer: C
Explanation: LocalDate represents a date without time, so it supports date-based adjustments such as days, months, and years. Duration is time-based and applies seconds and nanoseconds, which a LocalDate cannot handle.
The Date-Time API separates date-based amounts from time-based amounts. Period represents years, months, and days, making it appropriate for adjusting LocalDate. Duration represents an exact amount of time in seconds and nanoseconds; even Duration.ofDays(1) means exactly 24 hours, not a calendar day amount. When LocalDate.plus(Duration) is called, the duration tries to add seconds, producing the unsupported unit exception. For a date-only deadline, Period.ofDays(1) keeps the model date-based and produces 2024-02-29.
Duration, so it remains time-based and still tries to add seconds to LocalDate.LocalTime loses the actual date, so it does not meet the date-only deadline requirement.LocalDate; seconds require a time-bearing type such as LocalDateTime or Instant.Topic: Managing Concurrent Code Execution
A metrics class is used by many worker tasks. Assume main declares any checked exceptions and that imports exist. The program sometimes prints a number less than 100000.
class Metrics {
private int completed;
void done() { completed++; }
int get() { return completed; }
}
var metrics = new Metrics();
var pool = Executors.newFixedThreadPool(4);
var futures = new ArrayList<Future<?>>();
for (int i = 0; i < 100000; i++)
futures.add(pool.submit(metrics::done));
for (var f : futures) f.get();
pool.shutdown();
System.out.println(metrics.get());
Which change most directly fixes the concurrency problem?
Options:
A. Call Thread.sleep() before printing.
B. Synchronize only the get() method.
C. Use AtomicInteger and call incrementAndGet().
D. Declare completed as volatile.
Best answer: C
Explanation: The problem is a lost update caused by the non-atomic completed++ operation. AtomicInteger provides an atomic read-modify-write operation, so concurrent worker tasks cannot overwrite each other’s increments.
completed++ looks like one operation, but it is a read, an addition, and a write. When multiple threads execute it concurrently, two threads can read the same old value and both write back the same new value, losing one increment. Waiting on each Future ensures the submitted tasks have completed before printing, but it does not make the increments atomic. AtomicInteger.incrementAndGet() is designed for this kind of shared counter and provides a thread-safe atomic update without explicit locking.
The key distinction is visibility versus atomicity: seeing a value is not enough if the update itself can be interleaved.
completed++ still remains a non-atomic read-modify-write.done() from racing with each other.Topic: Using Java I/O API
A developer serializes a Login object to a byte array and immediately deserializes it using the same class definitions. Assume the helper methods correctly use ObjectOutputStream and ObjectInputStream.
class Settings implements java.io.Serializable {
private static final long serialVersionUID = 1L;
String theme;
Settings(String theme) { this.theme = theme; }
}
class Login implements java.io.Serializable {
private static final long serialVersionUID = 1L;
String user = "guest";
transient String token = "none";
Settings settings = new Settings("light");
}
Before serialization, the object is changed to user = "Mia", token = "T7", and settings.theme = "dark". Which comparison is correct after deserialization?
Options:
A. Deserialization fails unless serialVersionUID is auto-generated.
B. user and settings.theme are restored; token is null.
C. Only user is restored; settings becomes null.
D. All three values are restored, including token.
Best answer: B
Explanation: Serialization saves non-transient instance fields, including references to other serializable objects. The transient field is not saved, so after deserialization its value is the default for its type. The declared serialVersionUID supports version compatibility and does not need to be auto-generated.
Java object serialization records the state of non-transient instance fields. Here, user is a normal String field, and settings refers to a Settings object that also implements Serializable, so both values are part of the serialized object graph. The token field is marked transient, so its value is skipped. During deserialization, the Login constructor and field initializers are not used to restore token; it gets the default value for String, which is null. A matching declared serialVersionUID is valid and commonly used to control compatibility checks.
transient excludes the field value from serialization.serialVersionUID is valid and preferred.Topic: Accessing Databases Using JDBC
A developer is troubleshooting this JDBC update. The done column is SQL BOOLEAN, and id is SQL INTEGER; the team wants type-compatible bindings and no SQL concatenation. It throws java.sql.SQLException: Parameter index out of range (0 < 1) before executing the update.
String sql = "UPDATE task SET done = ? WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(0, "true");
ps.setInt(1, taskId);
ps.executeUpdate();
}
Which change is the best fix?
Options:
A. Use ps.setBoolean(1, true); and ps.setInt(2, taskId);
B. Use ps.setString(1, "true"); and ps.setInt(2, taskId);
C. Use ps.setBoolean(0, true); and keep ps.setInt(1, taskId);
D. Change the SQL placeholders to ?0 and ?1.
Best answer: A
Explanation: PreparedStatement parameter indexes start at 1, not 0. The first ? is for done, so it should be bound at index 1 with a boolean-compatible setter, and the second ? should be bound at index 2 with setInt.
JDBC binds PreparedStatement parameters by the position of each ? placeholder, starting with index 1. In this SQL statement, done = ? is the first placeholder and id = ? is the second. Because done is a SQL BOOLEAN, setBoolean(1, true) is the type-compatible binding. Because id is a SQL INTEGER, setInt(2, taskId) binds the second placeholder correctly.
The exception occurs before execution because index 0 is invalid for JDBC parameter binding. Changing the placeholder text or relying on string conversion does not address the one-based indexing rule and the requested type-compatible binding.
PreparedStatement placeholders are plain ?, not ?0 or ?1.Topic: Utilizing Java Object-Oriented Approach
A team is adding the Truck(String id) constructor. It must call the Vehicle(int) constructor with value 18 and then store id. Which replacement for // INSERT CODE correctly applies Java constructor chaining rules?
class Vehicle {
Vehicle(int wheels) {}
}
class Truck extends Vehicle {
private String id;
Truck() {
this("fleet");
}
// INSERT CODE
}
Options:
A. Truck(String id) { this.id = id; }
B. Truck(String id) { super(18); this.id = id; }
C. Truck(String id) { this.id = id; super(18); }
D. Truck(String id) { this(); this.id = id; }
Best answer: B
Explanation: A constructor may explicitly call either this(...) or super(...), and that call must be the first statement. Here, the required superclass constructor is Vehicle(int), so super(18) must appear first before assigning id.
Java constructor chaining requires the first statement of a constructor to be either an explicit same-class constructor call, this(...), or an explicit superclass constructor call, super(...). If neither appears, the compiler inserts super(). In this class, Vehicle has only Vehicle(int), so an implicit no-argument super() would not compile. The constructor that starts with super(18) correctly invokes the available superclass constructor and then performs normal instance initialization.
A this(...) call would delegate to another Truck constructor, not directly to Vehicle(int), and it can create a constructor cycle here because Truck() already calls Truck(String).
super(18) cannot appear after an assignment.Truck() already delegates to Truck(String), creating a constructor cycle.super(), but Vehicle has no no-argument constructor.Topic: Accessing Databases Using JDBC
Assume the imports shown are complete. At runtime, no registered JDBC driver accepts URLs beginning with jdbc:inventory:. What happens when this program is run?
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DbPing {
public static void main(String[] args) throws SQLException {
var url = "jdbc:inventory://db.example.com:5432/app";
try (Connection con =
DriverManager.getConnection(url, "app", "secret")) {
System.out.print(con.isValid(2));
}
}
}
Options:
A. It throws IllegalArgumentException because the URL includes a host.
B. It prints true after creating a connection from the URL.
C. It does not compile because Class.forName() is missing.
D. It compiles, then getConnection() throws SQLException.
Best answer: D
Explanation: The code compiles because the JDBC API calls and imports are valid. At runtime, DriverManager.getConnection() must find a registered driver that accepts the connection URL. If none does, it throws a SQLException before the try block body runs.
DriverManager does not create a JDBC driver merely from the text of a URL. It asks registered drivers whether they accept the supplied JDBC URL, then uses the matching driver to attempt the connection. In this scenario, no registered driver accepts jdbc:inventory:..., so DriverManager.getConnection() fails with a SQLException, commonly reported as no suitable driver found. The call to con.isValid(2) is never reached.
Modern JDBC drivers are often auto-registered when present, so an explicit Class.forName() is not a compile-time requirement. The key issue is whether an appropriate driver is available and accepts the URL.
Class.forName() fails because the program can compile and modern JDBC drivers can auto-register when present.IllegalArgumentException rule.Topic: Controlling Program Flow
An application needs the method below to compile on Java SE 17 and return a String for every possible int input. Which replacement for the comment correctly uses an arrow-label switch expression?
static String label(int code) {
// replacement goes here
}
Options:
- B. ```java
return switch (code) {
case 1 -> "one";
case 2 -> "two";
};
- D. ```java
return switch (code) {
case 1 -> "one";
case 2 -> {
yield "two";
}
default -> "other";
};
Best answer: D
Explanation: A Java 17 switch expression must be exhaustive and must produce a compatible result from each selected arm. An arrow arm can directly provide an expression result, but an arrow block must use yield to provide the switch expression’s value.
In Java 17, an arrow-label switch expression is itself an expression, so it must produce a value compatible with the target type or complete abruptly. A simple arrow expression supplies the result directly. When the right side is a block, Java does not use the block’s last expression as a value; the block must execute yield with the result. Because the selector type is int, the listed case constants cannot cover every possible value, so a default arm is needed for exhaustiveness. break with a value is not valid Java 17 syntax for producing a switch expression result.
yield, not break, to supply a switch expression result.int is not exhaustive with only two constant labels.Topic: Packaging, Deploying, and Java Platform Module System
A team is migrating an application to JPMS. The third-party JAR lib/acme-util-1.0.jar contains package com.acme.util, has no module-info.class, and has no Automatic-Module-Name manifest entry.
module com.store.app {
requires acme.util;
}
Compile command includes:
--class-path lib/acme-util-1.0.jar
--module-path mods
Error:
module not found: acme.util
Which is the best next fix?
Options:
A. Run the application with java -jar instead.
B. Change the requirement to requires com.acme.util;.
C. Add exports com.acme.util; to the app module.
D. Put the library JAR on the module path.
Best answer: D
Explanation: The JAR is currently on the class path, so it is part of the unnamed module and cannot satisfy a requires directive. Placing a non-modular JAR on the module path makes it an automatic module. Its derived name is based on the JAR file name, so acme-util-1.0.jar can be required as acme.util.
JPMS resolves requires clauses only from modules observable on the module path or system modules. A regular JAR on the class path is not a named module; it belongs to the unnamed module and is not found by requires acme.util. When the same non-modular JAR is placed on the module path, Java treats it as an automatic module. With no Automatic-Module-Name, the name is derived from the JAR file name, so acme-util-1.0.jar becomes acme.util after removing the version and converting separators. The key distinction is class path versus module path, not the package name inside the JAR.
requires uses module names, not package names such as com.acme.util.java -jar does not make the dependency a named module for JPMS resolution.Topic: Working with Arrays and Collections
A developer is troubleshooting this Java 17 code. The intent is to publish a read-only snapshot of the current task names.
var tasks = new ArrayList<>(List.of("parse", "load"));
List<String> published = Collections.unmodifiableList(tasks);
tasks.add("index");
System.out.println(published);
published.set(0, "skip");
At runtime, it prints [parse, load, index] and then throws UnsupportedOperationException. Which option best explains the cause and fix?
Options:
A. tasks became immutable; remove the tasks.add("index") call.
B. published is a view; use List.copyOf(tasks) for a snapshot.
C. published is fixed-size; use new ArrayList<>(published) instead.
D. published allows set; replace set with add.
Best answer: B
Explanation: Collections.unmodifiableList(tasks) does not create an independent immutable collection. It creates an unmodifiable view backed by tasks, so later changes to tasks are visible through published. Use List.copyOf(tasks) when the requirement is a read-only snapshot.
The key distinction is between an unmodifiable view and an immutable snapshot. Collections.unmodifiableList(tasks) prevents mutations through the returned reference, so published.set(...) throws UnsupportedOperationException. However, the wrapper still reads from the original tasks list, so tasks.add("index") changes what published displays. List.copyOf(tasks) creates a new unmodifiable list containing the current elements, so later structural changes to tasks are not reflected in that returned list. The closest trap is thinking “unmodifiable” means “independent immutable copy,” but here it only blocks mutation through the wrapper.
Collections.unmodifiableList() is not the same as Arrays.asList() and is still backed by the original list.tasks does not make the original ArrayList immutable.set and add are mutating operations blocked by the unmodifiable wrapper.Topic: Managing Concurrent Code Execution
Given the following Java 17 program, which statement about the printed line is guaranteed? Assume the program is run without interruption.
import java.util.concurrent.atomic.AtomicInteger;
public class CounterDemo {
static final int N = 100_000;
static final AtomicInteger atomic = new AtomicInteger();
static volatile int vol;
static int plain;
static void work() {
for (int i = 0; i < N; i++) {
atomic.incrementAndGet();
vol++;
plain++;
}
}
public static void main(String[] args) throws Exception {
var t1 = new Thread(CounterDemo::work);
var t2 = new Thread(CounterDemo::work);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(atomic.get() + " " + vol + " " + plain);
}
}
Options:
A. Only the first value is guaranteed to be 200000.
B. No value is guaranteed to be 200000.
C. All three values are guaranteed to be 200000.
D. The first two values are guaranteed to be 200000.
Best answer: A
Explanation: The atomic counter uses an atomic read-modify-write operation, so both threads contribute all increments. A volatile int provides visibility for individual reads and writes, but ++ is still not atomic. The ordinary shared field also has unsynchronized concurrent updates.
The core distinction is atomicity versus visibility. AtomicInteger.incrementAndGet() performs the increment as one atomic operation, so after both threads complete, atomic.get() is guaranteed to be 200000. The expression vol++ performs a volatile read and a volatile write, but the combined read-modify-write sequence can interleave with another thread and lose an update. The ordinary plain++ has the same lost-update problem, without even volatile visibility between worker threads. The join() calls ensure the main thread observes completed thread actions, but they do not make the increments atomic. The closest trap is treating volatile as a replacement for an atomic counter; it is not.
vol++ is not a single atomic operation.join() does not prevent lost updates between the worker threads.AtomicInteger provides an atomic increment method.Topic: Handling Date, Time, Text, Numeric, and Boolean Values
Inside a method, a developer needs a constant for a counter value of 2,147,483,648, exactly one more than Integer.MAX_VALUE. The code must compile in Java SE 17 without casts. Which declaration correctly applies Java’s primitive literal rules?
Options:
A. var count = 2_147_483_648;
B. int count = 2_147_483_648L;
C. long count = 2_147_483_648;
D. long count = 2_147_483_648L;
Best answer: D
Explanation: Decimal integer literals are int by default unless they have an L suffix. The value 2,147,483,648 is outside the int range, so it must be written as a long literal to compile.
Java checks an integer literal’s type before considering the assignment target. A decimal integer literal with no suffix is an int, and it must fit in the int range from -2,147,483,648 to 2,147,483,647. The value in the stem is one greater than the maximum int, so the unsuffixed literal is invalid even when assigned to a long or used with var. Adding L makes the literal a long, and long can store the value. A long literal cannot be assigned to an int without a narrowing cast.
long does not rescue an unsuffixed literal that is too large for int.long literal cannot be assigned to int without narrowing.var cannot infer a type from an invalid unsuffixed integer literal.Topic: Logging API and Standard Annotations
A Java 17 service configures its audit logger as follows:
import java.util.logging.*;
class Audit {
private static final Logger LOG = Logger.getLogger(Audit.class.getName());
static {
var handler = new ConsoleHandler();
LOG.setUseParentHandlers(false);
LOG.setLevel(Level.WARNING);
handler.setLevel(Level.INFO);
LOG.addHandler(handler);
}
static void run() {
LOG.info("created");
LOG.warning("retry");
}
}
The requirement is to publish both messages through the added ConsoleHandler. Do not re-enable parent handlers. Which refactor is the best fix?
Options:
A. Add another ConsoleHandler with level INFO.
B. Change the logger level to Level.INFO.
C. Change the handler level to Level.ALL.
D. Change the handler level to Level.WARNING.
Best answer: B
Explanation: The INFO record is blocked by the Logger level before it reaches the handler. Since the handler already allows INFO and higher, lowering the logger threshold to Level.INFO lets both info() and warning() messages be published.
In java.util.logging, both the Logger and each Handler apply level filtering. A record must first be loggable by the logger. Only then is it passed to the attached handlers, where each handler applies its own level. Here, LOG.setLevel(Level.WARNING) rejects the INFO message immediately, even though the handler is set to Level.INFO. Changing the logger level to Level.INFO allows both INFO and WARNING records to reach the handler, and the handler level already permits both.
The key takeaway is that a handler cannot publish a record the logger has already filtered out.
WARNING would continue to publish only the warning message.ALL does not help because the logger still rejects INFO records.INFO record still never reaches any handler.Topic: Utilizing Java Object-Oriented Approach
Given this Java 17 code, which statement is true?
class Counter {
private int value;
Counter(int value) { this.value = value; }
void add(int x) { value += x; }
void add(int... xs) {
for (var x : xs) value += x;
}
}
public class Test {
public static void main(String[] args) {
var c = new Counter(1);
c.add();
c.add(2);
System.out.println(c.value);
}
}
Options:
A. It does not compile because value is private in Counter.
B. It compiles and prints 3.
C. It does not compile because c.add() is ambiguous.
D. It does not compile because var cannot infer Counter.
Best answer: A
Explanation: The compilation failure is caused by encapsulation, not by var or varargs. The field value is declared private, so code in the separate Test class cannot access it directly even though the classes are in the same package.
In Java, private members are accessible only inside the class that declares them. Here, Counter.value can be read or modified by methods of Counter, such as add, but Test.main cannot access c.value directly. The local variable declaration var c = new Counter(1); is valid because var is allowed for local variables with an initializer. The overloads are also valid: c.add() invokes the varargs method with zero arguments, and c.add(2) chooses the fixed-arity add(int) method as the more specific match. A public accessor such as get() would be needed to print the value from Test.
var can infer the type Counter from the initializer.add(int...), not add(int).Topic: Using Java I/O API
A developer is troubleshooting a utility that must display attributes of a symbolic link itself, not its target. The file system supports symbolic links. logs/current.log is a symbolic link to logs/app.log, and logs/app.log is a regular file of size 1,024 bytes. The code compiles but reports attributes for the target file.
Path p = Path.of("logs/current.log");
BasicFileAttributes a =
Files.readAttributes(p, BasicFileAttributes.class);
System.out.println(a.isSymbolicLink());
System.out.println(a.isRegularFile());
System.out.println(a.size());
Which next fix best matches the intended behavior?
Options:
A. Replace BasicFileAttributes with PosixFileAttributes.
B. Add LinkOption.NOFOLLOW_LINKS to readAttributes.
C. Use p.toRealPath() before reading attributes.
D. Call p.normalize() before reading attributes.
Best answer: B
Explanation: NIO.2 file attribute reads follow symbolic links by default. To inspect the symbolic link itself, pass LinkOption.NOFOLLOW_LINKS to Files.readAttributes() so the returned BasicFileAttributes describe the link rather than its target.
The core issue is symbolic link handling in NIO.2 attribute reads. Files.readAttributes(path, BasicFileAttributes.class) follows links unless told otherwise, so the attributes describe logs/app.log. Adding LinkOption.NOFOLLOW_LINKS changes the lookup to the link object at logs/current.log, allowing isSymbolicLink() to reflect the link itself.
The fixed call is:
BasicFileAttributes a = Files.readAttributes(
p, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
Path cleanup or choosing a more specific attribute interface does not change the default link-following behavior.
NOFOLLOW_LINKS is used.Topic: Utilizing Java Object-Oriented Approach
A Java 17 application must model a set of workflow states: DRAFT, REVIEW, APPROVED, and REJECTED. The set is fixed by the business process, and each state may later need fields and methods such as a display label. Which construct best fits this model?
Options:
A. A class with public String constants
B. An enum with one constant per state
C. A sealed interface with one implementation per state
D. A record with a String name component
Best answer: B
Explanation: A Java enum is the best model when the valid values are a fixed set of named constants. Enum constants are type-safe instances, and an enum can include fields, constructors, and methods for behavior such as display labels.
Java enums are intended for closed sets of named values such as workflow states, days, directions, or categories. Each enum constant is a singleton instance of the enum type, so variables cannot accidentally hold unrelated strings. An enum can also declare fields, constructors, and methods, which supports adding a display label or behavior without changing the modeling approach.
The key takeaway is that a fixed set of named constants is a direct enum use case, not a string-based or open-ended value model.
String can be assigned.Topic: Handling Exceptions
Assume this Java 17 code is compiled and run. No imports are required. What is the result?
public class App {
static String call(int n) {
try {
if (n == 1) return "try";
throw new IllegalArgumentException("try");
} finally {
if (n == 1) throw new IllegalStateException("finally");
return "finally";
}
}
public static void main(String[] args) {
try { System.out.print(call(1)); }
catch (Exception e) { System.out.print(e.getClass().getSimpleName()); }
System.out.print(",");
try { System.out.print(call(2)); }
catch (Exception e) { System.out.print(e.getClass().getSimpleName()); }
}
}
Options:
A. It prints try,finally.
B. It prints try,IllegalArgumentException.
C. The code does not compile.
D. It prints IllegalStateException,finally.
Best answer: D
Explanation: A finally block runs before a pending return or exception from the try block is completed. If the finally block itself returns or throws, that later abrupt completion replaces the earlier one. That is why the first call reports an exception and the second call returns finally.
The core rule is that finally can override a pending result from try. In call(1), the try block starts to return try, but the finally block throws IllegalStateException, so the caller observes that exception. In call(2), the try block throws IllegalArgumentException, but the finally block returns finally, so that exception is discarded and the returned value is printed.
Returning or throwing from finally is legal, but it can mask earlier returns and exceptions.
try return in call(1) is replaced by the exception thrown from finally.return in finally for call(2) masks the IllegalArgumentException.return and throw statements in a finally block.Topic: Utilizing Java Object-Oriented Approach
A request-processing library has this Java 17 class. Audit tokens may be cached, and cached tokens must not keep any Request instance alive. The token value must use the supplied request’s id.
class Request {
private final String id;
Request(String id) { this.id = id; }
class AuditToken {
String value() { return id; }
}
static AuditToken tokenFor(Request request) {
return new AuditToken(); // compile-time error
}
}
Which refactor is the best correction?
Options:
A. Make id static so a static nested AuditToken can read it.
B. Make AuditToken static, store a final id, and return new AuditToken(request.id).
C. Return request.new AuditToken() from tokenFor.
D. Keep the inner class and call new Request(request.id).new AuditToken().
Best answer: B
Explanation: A non-static inner class instance requires an enclosing instance and keeps an implicit reference to it. Since cached tokens must not keep any Request alive, the best refactor is a static nested class that copies the needed id value.
The core issue is the difference between a non-static inner class and a static nested class. new AuditToken() is invalid in the static method because AuditToken is a non-static inner class and requires an enclosing Request instance. Qualifying construction with a Request would compile, but the resulting token would keep an implicit reference to that Request. Since the requirement says cached tokens must not keep any Request instance alive, the nested type should be made static and given its own copied state, such as a final id field initialized from request.id.
The key takeaway is that fixing the syntax alone is not enough when the inner class lifecycle is also part of the requirement.
Request.Request, just a different one.Topic: Managing Concurrent Code Execution
A service uses the following methods to build a list of uppercase IDs. The source List is not modified while either method runs, and the required result is a deterministic list containing all mapped IDs in source encounter order.
class Report {
static List<String> a(List<String> ids) {
var out = new ArrayList<String>();
ids.parallelStream()
.map(String::toUpperCase)
.forEach(out::add);
return out;
}
static List<String> b(List<String> ids) {
return ids.parallelStream()
.map(String::toUpperCase)
.collect(Collectors.toList());
}
}
Which statement correctly applies the Java SE 17 parallel stream rule?
Options:
A. Both methods are safe for this goal.
B. Only a() is safe for this goal.
C. Only b() is safe for this goal.
D. Neither method is safe for this goal.
Best answer: C
Explanation: Parallel stream operations should avoid unsafe mutation of shared external state. Method a() passes ArrayList::add to forEach, allowing concurrent mutation and nondeterministic ordering. Method b() uses a collector-based reduction, which is the safe aggregation pattern for this goal.
The core rule is that stream pipeline actions should be non-interfering and should not rely on unsafely shared mutable state. In a(), multiple worker threads may call out.add(...) on the same ArrayList, which is not thread-safe, and forEach does not preserve encounter order in a parallel stream. In b(), collect(Collectors.toList()) is a reduction operation: the stream framework manages accumulation and combines partial results safely. Because the source is an ordered List and the pipeline is not made unordered, the collected result preserves encounter order. The key takeaway is to aggregate parallel stream results with collectors rather than external mutation.
forEach(out::add) mutates one unsynchronized ArrayList from parallel tasks.a() has both a data-race risk and no forEach encounter-order guarantee.b() is a safe way to produce the list.Topic: Utilizing Java Object-Oriented Approach
A payroll system must represent employee work status as exactly four values: ACTIVE, ON_LEAVE, RETIRED, and TERMINATED. Each value needs a display label and a method that determines whether the employee can receive assignments. The set is fixed by company policy and should not be extended by client code. Which Java model best fits this scenario?
Options:
A. A sealed interface WorkStatus with one class per status
B. A record WorkStatus(String label, boolean assignable)
C. A class WorkStatus with public static final String constants
D. An enum WorkStatus with constants, fields, and methods
Best answer: D
Explanation: An enum is the clearest Java model for a fixed set of named constants. Java enums are type-safe and can include constructors, fields, and methods, so they can store the label and assignment behavior for each status.
Java enums are designed for cases where the valid values are known, named, and fixed at compile time. Each enum constant is a singleton instance of the enum type, and the enum can define a private constructor, instance fields, and methods. That fits the payroll statuses because client code should choose only among the declared constants, not create new values.
String constants do not provide type safety, records represent data carriers that can usually be instantiated with arbitrary values, and a sealed hierarchy is more complex than needed for simple fixed named constants.
String can still be passed where a status-like value is expected.Topic: Handling Date, Time, Text, Numeric, and Boolean Values
A test fails with StringIndexOutOfBoundsException when this Java 17 code runs. The constructor capacity was chosen to leave room for the final !.
public class Demo {
public static void main(String[] args) {
var tag = new StringBuilder(8);
tag.append("ab")
.insert(2, "cd")
.delete(0, 1)
.reverse()
.insert(8, "!");
System.out.println(tag);
}
}
What is the best cause or next fix?
Options:
A. Insert at tag.length() or use append("!").
B. Use delete(0, 0) because the end index is inclusive.
C. Move reverse() earlier because it creates a new builder.
D. Increase the initial capacity to at least 9.
Best answer: A
Explanation: StringBuilder capacity is only allocated storage, not current content length. Chained methods mutate the same builder, and valid insert positions are from 0 through the current length(). After the chain, index 8 is outside the valid range.
StringBuilder operations such as append, insert, delete, and reverse mutate the same object and usually return that object for chaining. Here, the content changes from "ab" to "abcd", then to "bcd", then to "dcb"; its length is 3. The constructor argument 8 sets initial capacity, but it does not create eight characters or make index 8 valid. An insertion offset must be between 0 and length() inclusive, so the final operation should insert at tag.length() or simply append "!".
reverse() mutates and returns the same StringBuilder, not a separate builder.delete(start, end) uses an exclusive end index, so delete(0, 1) removes the first character.Topic: Utilizing Java Object-Oriented Approach
An order service needs an enum Priority whose constants each store an SLA value in hours and expose it through an hours() method. Which declaration is valid in Java SE 17?
Options:
- B. ```java
enum Priority {
private final int hours;
LOW(48), HIGH(4);
Priority(int hours) { this.hours = hours; }
int hours() { return hours; }
}
- D. ```java
enum Priority {
LOW(48), HIGH(4);
private final int hours;
Priority(int hours) { this.hours = hours; }
int hours() { return hours; }
}
Best answer: D
Explanation: An enum may define fields, constructors, and methods, but its constants must be declared before other members. When constants are followed by fields or methods, the constant list must end with a semicolon. Enum constructors cannot be public or protected.
Java enum declarations have a special member order: enum constants, if present, must come first. If the enum has additional fields, constructors, or methods after the constants, the constant list is terminated with a semicolon. Each constant may pass arguments to an enum constructor, such as LOW(48), and the constructor initializes instance fields for that constant. Enum constructors are only used by the enum constants themselves, so they cannot be declared public or protected; omitting the modifier is valid. The valid declaration satisfies all of these rules while providing an instance method that returns the stored value.
public or protected.Topic: Packaging, Deploying, and Java Platform Module System
A module com.shop.domain contains package com.shop.order. Module com.shop.ui must compile code that imports public types from com.shop.order. Module com.shop.mapper must access private fields in that package reflectively at runtime. No other named module should receive either kind of access. Assuming consumer modules declare needed dependencies, which directives belong in com.shop.domain’s module-info.java?
Options:
A. exports com.shop.order to com.shop.ui, com.shop.mapper;
B. exports com.shop.order to com.shop.ui; and opens com.shop.order to com.shop.mapper;
C. opens com.shop.order to com.shop.ui; and exports com.shop.order to com.shop.mapper;
D. opens com.shop.order to com.shop.ui, com.shop.mapper;
Best answer: B
Explanation: JPMS separates public type access from deep reflective access. A qualified exports directive limits compile-time access to named target modules, while a qualified opens directive limits runtime reflective access to named target modules.
In JPMS, exports and opens solve different access problems. exports com.shop.order to com.shop.ui lets only com.shop.ui compile against public types in that package. opens com.shop.order to com.shop.mapper lets only com.shop.mapper perform deep reflection, such as accessing private fields with reflection APIs. Using both directives on the same package is valid when the access needs are different for different modules. The key distinction is that opens does not replace exports for normal compilation, and exports does not grant private reflective access.
opens does not let com.shop.ui compile imports, and exports does not allow private-field reflection.com.shop.mapper but still blocks deep reflection.com.shop.ui compile-time imports.Topic: Managing Concurrent Code Execution
An audit service stores records in priority order. Assume no other thread writes to standard output.
var records = List.of("A", "B", "C", "D");
Which pipeline is guaranteed by Java SE 17 to print BC in that order, even though the stream is parallel?
Options:
A. records.parallelStream().skip(1).limit(2).forEachOrdered(System.out::print);
B. records.parallelStream().skip(1).limit(2).findAny().ifPresent(System.out::print);
C. records.parallelStream().unordered().skip(1).limit(2).forEachOrdered(System.out::print);
D. records.parallelStream().skip(1).limit(2).forEach(System.out::print);
Best answer: A
Explanation: A List stream has a defined encounter order. For an ordered stream, skip and limit select elements using that order, and forEachOrdered preserves it even when the stream runs in parallel.
Encounter order is the source-defined traversal order of a stream. List.of("A", "B", "C", "D") supplies an ordered stream, so skip(1).limit(2) selects B and C in that order unless the stream is made unordered. In a parallel stream, forEach may perform actions in any order, but forEachOrdered performs actions according to the encounter order when one exists. findAny is also intentionally flexible and is not a way to process all selected elements. The key rule is that ordered intermediate selection does not make forEach ordered; the terminal operation still matters.
forEach may print the selected elements in either order when the stream is parallel.unordered() removes the requirement that skip and limit refer to the original list positions.findAny prints at most one selected element and does not guarantee the first one.Topic: Handling Date, Time, Text, Numeric, and Boolean Values
A scheduling service stores user-entered local times with region-based time zones. In America/New_York, on March 10, 2024, clocks jump from 02:00 to 03:00, and the offset changes from -05:00 to -04:00.
var zone = ZoneId.of("America/New_York");
var local = LocalDateTime.of(2024, 3, 10, 2, 30);
var zdt = local.atZone(zone);
Which statement about zdt correctly applies the Java SE 17 Date-Time API rule?
Options:
A. It is 2024-03-10T02:30-05:00[America/New_York].
B. It is 2024-03-10T02:30-04:00[America/New_York].
C. It throws DateTimeException.
D. It is 2024-03-10T03:30-04:00[America/New_York].
Best answer: D
Explanation: The local time 02:30 falls inside the supplied daylight-saving gap. LocalDateTime.atZone(ZoneId) does not keep an impossible wall-clock time; it adjusts forward to 03:30 and uses the offset after the gap.
A LocalDateTime has no offset or time zone, so converting it with atZone applies the zone rules. When the local time is nonexistent because of a daylight-saving gap, Java resolves it by shifting the local time forward by the length of the gap. Here, 02:30 is in the skipped hour, so it becomes 03:30. The stem states that the offset after the jump is -04:00, giving 2024-03-10T03:30-04:00[America/New_York].
The key takeaway is that atZone resolves a gap rather than preserving an invalid local time.
02:30 does not exist with offset -05:00 on that date.02:30 local time.atZone adjusts gap times instead of throwing.Topic: Packaging, Deploying, and Java Platform Module System
Which statement correctly describes a Java 17 JPMS rule for compiling code in one named module that refers to a public class in another named module?
Options:
A. The source module must read the target module, and the target package must be exported to it.
B. An opens directive alone gives normal compile-time access to the package.
C. Duplicate requires directives are accepted and treated as one dependency.
D. The module path order selects one package when resolved modules split a package.
Best answer: A
Explanation: JPMS separates module readability from package accessibility. Code in one named module can compile against a public type in another named module only when the source module reads the target module and the target exports that package appropriately.
In Java 17, requires establishes readability between modules, but readability is not enough by itself. The referenced type must also be public, and its package must be exported by the target module, either unqualified or qualified to the source module. An opens directive is mainly for reflective access at run time and does not replace exports for normal compilation.
The key takeaway is that missing readability and non-exported packages produce different JPMS access problems, but ordinary cross-module compilation needs both readability and export.
opens does not provide ordinary compile-time access to package types.requires directives in a module declaration are not valid source.Topic: Working with Streams and Lambda Expressions
An engineer wants text scores converted to Score records in numeric order. This Java 17 code fails to compile at the middle pipeline line:
import java.util.*;
record Score(Integer value) {}
public class Demo {
public static void main(String[] args) {
var result = List.of("10", "2", "30").stream()
.map(Integer::parseInt)
.map(Integer::compareTo)
.map(Score::new)
.toList();
System.out.println(result);
}
}
Which change is the best fix?
Options:
A. Use .map(i -> new Score(i)) for the last operation.
B. Use .map(Integer::valueOf) for the first operation.
C. Use .map(Integer.valueOf(0)::compareTo) for the middle operation.
D. Use .sorted(Integer::compareTo) for the middle operation.
Best answer: D
Explanation: The middle method reference is the problem. Integer::compareTo is an unbound instance reference, so it needs both the receiver and the comparison argument, matching Comparator<Integer> for sorted, not Function<Integer, R> for map.
A method reference is resolved using its target functional interface. map needs a one-argument Function, but Integer::compareTo refers to an instance method where the receiver is unbound: it behaves like (a, b) -> a.compareTo(b). That shape matches Comparator<Integer>, which is what sorted expects. The surrounding references are fine: Integer::parseInt is a static reference used as a Function<String, Integer>, and Score::new is a constructor reference used as a Function<Integer, Score>. The key is choosing the stream operation whose target type matches the method reference shape.
Integer.valueOf(0)::compareTo compiles as a one-argument function but maps scores to comparison results instead of sorting them.Integer::parseInt is already a valid static method reference in this map call.Score::new with an equivalent lambda does not address the incompatible middle reference.Topic: Logging API and Standard Annotations
A Java 17 service uses java.util.logging. The catch block must compile and include both an explanatory message and the caught exception in the log record, without assuming any handler or level configuration will display it.
import java.io.IOException;
import java.util.logging.Logger;
class Importer {
private static final Logger LOGGER =
Logger.getLogger(Importer.class.getName());
void run() {
try {
load();
} catch (IOException ex) {
LOGGER.warning("Import failed", ex);
}
}
void load() throws IOException { throw new IOException("disk"); }
}
Assume any needed java.util.logging imports can be added. Which refactor is best?
Options:
A. Use LOGGER.warning(ex.toString());
B. Use LOGGER.setLevel(Level.WARNING); LOGGER.warning("Import failed");
C. Use LOGGER.log(Level.WARNING, "Import failed", ex);
D. Use LOGGER.log(Level.WARNING, ex);
Best answer: C
Explanation: Logger.warning() is a convenience method for logging a message, not a message plus a Throwable. To attach the exception to the log record, use log(Level, String, Throwable). Whether that record is displayed is controlled outside this call by logger and handler configuration.
The Java Logging API provides convenience methods such as warning(String) for simple messages. When code needs to record an exception object, the appropriate method is Logger.log(Level level, String msg, Throwable thrown). That call creates a log record containing the severity, message, and associated exception. The method call should not assume anything about console output, handler setup, or effective logging level; those are configuration concerns separate from using the correct logging API.
The key takeaway is to choose the overload that captures the Throwable, not a convenience method that only logs text.
Throwable is not attached to the record.Logger has no log(Level, Throwable) method.Topic: Working with Streams and Lambda Expressions
A developer needs to turn nested batch data into one stream and then append a marker. Which replacement for the comment compiles and produces [red, blue, green, audit]?
var batches = List.of(
List.of("red", "blue"),
List.of("green"));
Stream<String> result = // replacement
System.out.println(result.toList());
Options:
A. Stream.concat(batches.stream().flatMap(List::stream), Stream.of("audit"));
B. Stream.concat(batches.stream().map(List::stream), Stream.of("audit"));
C. Stream.concat(batches.stream(), Stream.of(List.of("audit")));
D. Stream.of(batches.stream().flatMap(List::stream), Stream.of("audit"));
Best answer: A
Explanation: The nested lists must first be decomposed into String elements. flatMap(List::stream) produces a Stream<String>, and Stream.concat joins that stream with Stream.of("audit") to produce the required single stream.
map preserves one output element per input element, so mapping each list to List::stream creates a stream whose elements are streams. flatMap is used when each input element supplies another stream and those streams must be flattened into one stream. After flattening batches into a Stream<String>, Stream.concat(first, second) creates a lazily concatenated stream containing all elements from the first stream followed by all elements from the second stream. The declared type Stream<String> also matters: both arguments to concat must combine to produce a compatible stream of strings.
map leaves nested Stream<String> values rather than producing the required String elements.Stream.of creates a stream containing two stream objects, not a concatenated stream of their elements.Stream<List<String>>, not the required flattened Stream<String>.Topic: Handling Exceptions
Consider this Java 17 code. Which statement correctly compares what process() must do for the two exception types?
class Batch {
static void load() throws java.io.IOException {
throw new java.io.IOException("missing file");
}
static void validate(String s) {
if (s == null) throw new IllegalArgumentException("bad input");
}
static void process() {
load();
validate(null);
}
}
Options:
A. Catch or declare IllegalArgumentException; IOException may be ignored.
B. Ignore both exception types.
C. Catch or declare both exception types.
D. Catch or declare IOException; IllegalArgumentException may be ignored.
Best answer: D
Explanation: Java requires checked exceptions to be handled or declared by a method that can throw them. load() declares IOException, so process() must catch or declare it. IllegalArgumentException is unchecked, so process() can compile without mentioning it.
The core distinction is checked versus unchecked exceptions. A checked exception, such as java.io.IOException, must be caught in the current method or declared in that method’s throws clause. An unchecked exception, such as IllegalArgumentException, extends RuntimeException, so the compiler does not require it to be caught or declared. This does not mean the unchecked exception cannot occur; it only means Java does not enforce compile-time handling for it. In the snippet, the compile-time problem in process() is the call to load(), not the call to validate(null).
IOException is checked and cannot be ignored by process().IOException from load() must be handled or declared.Topic: Controlling Program Flow
Which statement describes a valid Java 17 loop rule?
Options:
A. for(;;) is valid and loops indefinitely unless exited.
B. while(0) is valid because 0 means false.
C. while(false) compiles and simply skips its body.
D. do { } while(false) skips its body entirely.
Best answer: A
Explanation: Java allows a basic for statement to omit its initializer, condition, and update clauses. When the condition is omitted, it is treated as true, so for(;;) is a valid infinite loop unless break, return, or an exception exits it.
A Java for loop has three optional parts: initializer, condition, and update. If the condition is absent, Java treats it as true, making for(;;) a valid infinite loop. By contrast, while and do-while conditions must have type boolean or Boolean; Java does not use numeric truth values such as 0 or 1. A while(false) loop body is unreachable when the condition is the constant expression false, so it does not compile as a simple skipped loop. A do-while loop checks its condition after the body, so its body runs at least once.
while(false) makes the loop body unreachable at compile time.do-while body runs before its condition is tested.Topic: Handling Date, Time, Text, Numeric, and Boolean Values
A Java 17 method should return values such as true:A-ready. The current code fails to compile.
static String label(boolean active, char code) {
String suffix = "-ready";
return active + ':' + code + suffix;
}
Which replacement for the return statement is the best fix?
Options:
A. return (active && code == 'A') + ":" + suffix;
B. return active + ":" + code + suffix;
C. return active + ':' + String.valueOf(code) + suffix;
D. return (active ? code : ':') + suffix;
Best answer: B
Explanation: The compile-time problem is the first + operation: boolean + char is not legal. Using ":" makes the first operation string concatenation, so Java converts the boolean and char values to text and preserves the intended output format.
In Java, + is evaluated left to right. It performs string concatenation only when at least one operand of that particular + operation is a String. In the original expression, active + ':' is evaluated first, and a boolean cannot be added to a char. Changing the delimiter from the character literal ':' to the string literal ":" makes the first operation boolean + String, which is valid string concatenation. After that, the rest of the expression is also concatenation.
The key takeaway is that adding a later String does not fix an earlier invalid + operation.
active + ':' is checked before String.valueOf(code) is reached.code is 'A' instead of including the code value.active text and the required colon separator.Topic: Working with Arrays and Collections
What is the result of compiling and running the following Java 17 code?
import java.util.*;
public class Demo {
public static void main(String[] args) {
List<String> fixed = Arrays.asList("A", "B");
List<String> view = Collections.unmodifiableList(fixed);
List<String> copy = List.copyOf(fixed);
fixed.set(0, "X");
System.out.print(view + " ");
System.out.print(copy + " ");
try {
fixed.add("C");
} catch (RuntimeException e) {
System.out.print(e.getClass().getSimpleName());
}
}
}
Options:
A. The code throws UnsupportedOperationException at fixed.set(0, "X").
B. [X, B] [A, B] UnsupportedOperationException
C. [X, B] [X, B] UnsupportedOperationException
D. [A, B] [A, B] UnsupportedOperationException
Best answer: B
Explanation: The code compiles and prints the changed fixed-size list through the unmodifiable view. List.copyOf() made a separate unmodifiable copy before the change, so it still shows the original contents. Adding to the fixed-size list throws UnsupportedOperationException.
Arrays.asList() returns a fixed-size list backed by an array: element replacement with set() is allowed, but structural changes such as add() or remove() are not. Collections.unmodifiableList(fixed) returns a view-backed wrapper, so changes made through the original fixed reference are visible through view. List.copyOf(fixed) creates an unmodifiable copy that is not backed by the original list, so later fixed.set(0, "X") does not change copy.
The key distinction is fixed-size versus unmodifiable versus view-backed copy behavior.
Collections.unmodifiableList() does not copy the list; it wraps the original list.List.copyOf() does not reflect later element replacement in the source list.Arrays.asList() allows replacing existing elements with set().Topic: Using Java I/O API
Assume the current directory is writable, contains no symbolic links named a.txt or b.txt, and all necessary imports exist. What is the result of running this code?
public class Test {
public static void main(String[] args) throws Exception {
Path a = Path.of("a.txt");
Path b = Path.of("b.txt");
Files.writeString(a, "A");
Files.writeString(b, "B");
Files.copy(a, b, StandardCopyOption.REPLACE_EXISTING);
Files.writeString(b, "C", StandardOpenOption.APPEND);
Files.writeString(a, "D", StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE);
System.out.print(Files.readString(b));
}
}
Options:
A. It prints DC.
B. It throws FileAlreadyExistsException at the copy() call.
C. It prints AC.
D. It throws FileAlreadyExistsException before printing.
Best answer: D
Explanation: The copy call succeeds because REPLACE_EXISTING allows b.txt to be overwritten with the contents of a.txt. The append call then adds C to b.txt, but the later CREATE_NEW write targets an existing a.txt, causing FileAlreadyExistsException before anything is printed.
NIO.2 file options control whether an existing target is accepted, replaced, appended to, or rejected. The first two writeString() calls use default behavior, creating or truncating the files. Files.copy(a, b, REPLACE_EXISTING) replaces b.txt with the contents of a.txt, so b.txt becomes A. Files.writeString(b, "C", APPEND) appends to b.txt, making it AC. However, Files.writeString(a, "D", CREATE_NEW, WRITE) requires that a.txt not already exist. Since a.txt still exists after a copy operation, this call throws FileAlreadyExistsException. The final print statement is never reached.
b.txt does become AC, but the later exception prevents printing it.CREATE_NEW rejects an existing file instead of replacing it.REPLACE_EXISTING allows the existing target b.txt to be replaced.Topic: Logging API and Standard Annotations
An import job should fail when load() cannot read its configuration. During troubleshooting, a test run logs a SEVERE message with a stack trace and then prints DONE, so the scheduler records the run as successful. What is the best next fix?
import java.io.*;
import java.util.logging.*;
class ImportJob {
private static final Logger LOG =
Logger.getLogger(ImportJob.class.getName());
static void load() throws IOException {
throw new IOException("missing config");
}
public static void main(String[] args) {
try {
load();
} catch (IOException e) {
LOG.log(Level.SEVERE, "Import failed", e);
}
System.out.println("DONE");
}
}
Options:
A. Use LOG.severe(e.getMessage()) instead of LOG.log(...).
B. Declare main with throws IOException and rethrow e.
C. Move System.out.println("DONE") into a finally block.
D. Replace the logger call with System.out.println(e).
Best answer: B
Explanation: A Logger call records diagnostic information; it does not handle, rethrow, or convert an exception into a failed process. To stop normal completion, the code must explicitly change control flow, such as by rethrowing the caught exception from main.
Java logging, exception handling, and standard output are separate mechanisms. The catch block consumes the IOException and only writes a log record, so execution continues after the catch block and DONE is printed. If the job must fail, the catch block should log the useful diagnostic details and then rethrow the exception, with main declaring throws IOException or otherwise returning a failure status. Printing to System.out is only text output and does not signal failure to Java control flow or to a scheduler by itself.
The key takeaway is that logging can document a failure, but exception handling decides whether execution continues.
System.out.println(e) only writes text and still lets execution continue.LOG.severe(e.getMessage()) logs less exception detail and still does not fail the job.finally block would make DONE print even more reliably after the exception path.Topic: Utilizing Java Object-Oriented Approach
In Java 17, consider this record declaration:
public record Book(String title, int pages) {
public Book {
if (pages < 1) throw new IllegalArgumentException();
title = title.strip();
}
}
Which statement describes the record correctly?
Options:
A. It compiles only if equals, hashCode, and toString are declared.
B. It fails because records require accessors named getTitle() and getPages().
C. It compiles; the compact constructor may reassign component parameters before automatic field assignment.
D. It fails because the compact constructor must assign this.title and this.pages.
Best answer: C
Explanation: The record compiles because the body is a compact canonical constructor. In a compact constructor, component parameters are available for validation or normalization, and the compiler performs the final field assignments after the constructor body completes.
A record header declares the record components. For each component, Java creates a private final field and a public accessor named exactly like the component, such as title() and pages(). A compact constructor is a shortened form of the canonical constructor: it has no parameter list, but the component parameters are in scope in its body. The constructor may validate values or reassign parameters such as title = title.strip();. It must not explicitly assign the component fields with this.title = ...; the compiler inserts those assignments after the compact constructor body. Generated equals, hashCode, and toString are also provided unless explicitly declared.
title() and pages(), not getTitle() and getPages().equals, hashCode, and toString.Topic: Working with Arrays and Collections
What is the result of compiling and running this Java 17 code?
import java.util.*;
public class Demo {
public static void main(String[] args) {
int[] nums = {3, 1, 2};
Arrays.sort(nums);
var list = Arrays.asList(nums);
nums[0] = 9;
System.out.println(list.size() + " " + Arrays.toString(list.get(0)));
}
}
Options:
A. 3 [1, 2, 3]
B. 3 [9, 2, 3]
C. The code does not compile.
D. 1 [9, 2, 3]
Best answer: D
Explanation: The code compiles and prints one list element: the int[] array itself. Arrays.asList() does not box a primitive array into individual Integer elements. After sorting, changing nums[0] is visible through the list because the list stores the same array reference.
Arrays.asList(T...) creates a fixed-size list backed by the provided reference elements. With int[] nums, the single argument is the primitive array object, so the inferred list type is List<int[]>, not List<Integer>. Arrays.sort(nums) first changes the array to [1, 2, 3]. The later assignment nums[0] = 9 mutates the same array object stored as the list’s only element. Therefore list.size() is 1, and Arrays.toString(list.get(0)) prints the current contents of that array. The key takeaway is that primitive arrays are not expanded or boxed by Arrays.asList().
Arrays.asList() does not turn int[] into three Integer elements.list.get(0) is an int[], which is valid for Arrays.toString(int[]).Topic: Utilizing Java Object-Oriented Approach
What is the result of compiling and running the following Java 17 code? No imports are needed.
public class Report {
public static void main(String[] args) {
int bonus = 2;
StringBuilder log = new StringBuilder("S");
class Worker {
void add() { log.append(bonus); }
}
Runnable task = new Runnable() {
public void run() { log.append(":").append(bonus); }
};
log.append("T");
bonus++;
new Worker().add();
task.run();
System.out.println(log);
}
}
Options:
A. It prints ST3:3.
B. It prints ST2:2.
C. It does not compile because bonus is not effectively final.
D. It does not compile because log is mutated after capture.
Best answer: C
Explanation: Local and anonymous classes may capture local variables only if those variables are final or effectively final. The variable bonus is captured inside both nested class bodies and later modified with bonus++, so the code fails to compile.
A local variable captured by a local class or anonymous class must not be reassigned or incremented anywhere in its scope after initialization. Here, bonus is used inside Worker.add() and inside the anonymous Runnable, but bonus++ changes the local variable, so it is not effectively final. By contrast, log is still effectively final because the reference variable is assigned only once; mutating the StringBuilder object does not reassign log.
The key distinction is between changing a captured local variable and changing the state of an object referenced by that variable.
append() mutates the object, not the captured local reference log.Topic: Utilizing Java Object-Oriented Approach
Which Java 17 record declaration is valid? Assume each option is considered as a separate top-level declaration.
Options:
A. record Point(int x, int y) extends Object { }
B. record Point(int x, int y) { int z; }
C. record Point(int x, int y) { static int count; int sum() { return x + y; } }
D. record Point(int x, int y) { { System.out.println(x); } }
Best answer: C
Explanation: Java records have strict rules about their bodies. A record can declare static members and instance methods, but it cannot declare extra instance fields, instance initializer blocks, or an explicit superclass.
A record declaration automatically defines private final fields for its record components, along with accessors and other standard members. In the valid declaration, count is a static field, which records may have, and sum() is an instance method that can read the component fields x and y. A record cannot add its own non-static instance field such as z, cannot use an instance initializer block, and cannot include an extends clause because every record implicitly extends java.lang.Record.
The key distinction is that records restrict additional instance state, not all additional members.
z.java.lang.Record and cannot use extends Object.Topic: Utilizing Java Object-Oriented Approach
A team wants this Java 17 record to reject null or blank account IDs and store the stripped value. The record currently fails to compile. Which refactor is the best fix while keeping the compact constructor form?
public record AccountId(String value) {
public AccountId {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("missing id");
}
this.value = value.strip();
}
}
Options:
A. Keep this.value = value.strip(); in the compact constructor
B. Add private String value; and assign that field
C. Replace the last line with value = value.strip();
D. Add a setter and call it from the constructor
Best answer: C
Explanation: Record component fields are final and are not assigned directly inside a compact constructor. The compact constructor body works with the implicit constructor parameters, and Java assigns the component fields after the body completes.
A compact constructor is a canonical constructor without an explicit parameter list. Inside its body, the component names such as value refer to the implicit constructor parameters, which may be validated or reassigned for normalization. After the compact constructor body finishes, the compiler assigns the record’s final component fields from those parameter values. Therefore, assigning value = value.strip(); stores the normalized value, while this.value = ... is not allowed in the compact constructor body.
The key takeaway is to normalize the parameter, not the record component field.
Topic: Logging API and Standard Annotations
Assume this Java 17 source file is compiled with the shown import, and warnings are not treated as errors. What is the result?
import java.util.List;
class Report {
@SafeVarargs
void printAll(List<String>... groups) {
for (var group : groups)
System.out.print(group.size());
}
public static void main(String[] args) {
new Report().printAll(List.of("a"), List.of("b", "c"));
}
}
Options:
A. It does not compile because generic varargs are prohibited.
B. It does not compile because printAll is overridable.
C. It compiles but throws ClassCastException.
D. It compiles and prints 12.
Best answer: B
Explanation: The decisive rule is the valid use of @SafeVarargs. The method is varargs, but it is an instance method that is not final, static, or private, so the annotation is rejected at compile time.
@SafeVarargs documents that a varargs method or constructor does not perform unsafe operations on its varargs parameter. Java restricts this annotation to declarations that cannot be overridden: constructors, final methods, static methods, and private methods. In the snippet, printAll is a package-access instance method and can be overridden, so applying @SafeVarargs causes a compile-time error before main can run.
Generic varargs themselves are not automatically illegal; without this invalid annotation, the code could compile with an unchecked warning. The key issue is the annotation’s permitted target and intent.
main runs.Use the Java 17 1Z0-829 Practice Test page for the full IT Mastery route, mixed-topic practice, timed mock exams, explanations, and web/mobile app access.
Try Java 17 1Z0-829 on Web View Java 17 1Z0-829 Practice Test
Read the Java 17 1Z0-829 Cheat Sheet on Tech Exam Lexicon for concept review before another timed run.