Browse Certification Practice Tests by Exam Family

Oracle Java SE 21 1Z0-830 Practice Test

Practice Oracle Java SE 21 Developer Professional 1Z0-830 with free sample questions, timed mock exams, code-reading drills, and detailed answer explanations in IT Mastery.

Oracle Java SE 21 Developer Professional (1Z0-830) focuses on practical Java correctness across object-oriented design, generics, collections, streams, exceptions, and concurrency basics.

IT Mastery practice for 1Z0-830 is live now. Use this page to start the web simulator, review the exam snapshot, work through 24 public sample questions, and continue into the full question bank with the same account on web, iOS, iPadOS, macOS, or Android.

Interactive Practice Center

Start a practice session for Oracle Java SE 21 Developer Professional (1Z0-830) below, or open the full app in a new tab. For the best experience, open the full app in a new tab and navigate with swipes/gestures or the mouse wheel—just like on your phone or tablet.

Open Full App in a New Tab

A small set of questions is available for free preview. Subscribers can unlock full access by signing in with the same account they use on web and mobile.

Prefer to practice on your phone or tablet? Download the IT Mastery – AWS, Azure, GCP & CompTIA exam prep app for iOS or IT Mastery app on Google Play (Android) and use the same account across web and mobile.

What this 1Z0-830 practice page gives you

  • a direct route into the live IT Mastery simulator for Oracle Java SE 21 Developer Professional
  • 24 on-page sample questions with detailed explanations
  • code-reading drills and mixed sets across OOP, generics, collections, streams, exceptions, concurrency, and Java APIs
  • a clear free-preview path before you subscribe
  • the same account across web and mobile

Who 1Z0-830 is for

  • Java developers who need exam-style precision across Java SE 21 language and API behavior
  • candidates who want the newer Oracle Java professional route instead of Java SE 17
  • teams moving from routine Java development into exact code-output, compiler-rule, and API-semantics reasoning

1Z0-830 exam snapshot

  • Vendor: Oracle
  • Official certification lane: Oracle Certified Professional: Java SE 21 Developer Professional
  • Official exam name: Java SE 21 Developer Professional
  • Exam code: 1Z0-830
  • Focus: Java language correctness, API semantics, and code-reading precision
  • Question style: scenario-based code analysis and Java API judgment
  • Current IT Mastery status: live practice available

1Z0-830 questions usually reward the option that follows the exact generic bounds, stream behavior, and exception rules in the code instead of the one that seems right after a quick skim.

Topic coverage for 1Z0-830 practice

  • Core OOP and type rules: inheritance, interfaces, polymorphism, encapsulation, and conversions
  • Generics and collections: variance, ordering, mutability, and common API edge cases
  • Streams and lambdas: pipelines, intermediate vs terminal operations, and result reasoning
  • Exceptions and resources: checked vs unchecked flow, try-with-resources, and propagation
  • Concurrency basics: threads, executors, synchronization, and practical correctness

How to use the 1Z0-830 simulator efficiently

  1. Start with generics, streams, exceptions, and nested-type rules because those areas punish quick skim-reading.
  2. Trace snippets line by line until you can explain whether the code compiles, what it prints, or which exception path is taken.
  3. Move into mixed sets once you can switch between compiler rules, runtime behavior, library semantics, and concurrency assumptions without guessing.
  4. Finish with timed runs so Java 21 code-reading precision holds under exam pressure.

Free preview vs premium

  • Free preview: a smaller web set so you can validate the question style and explanation depth.
  • Premium: the full 1Z0-830 practice bank, focused drills, mixed sets, timed mock exams, detailed explanations, and progress tracking across web and mobile.

Good next pages after 1Z0-830

Official sources

24 1Z0-830 sample questions with detailed explanations

These sample questions are drawn from the current local bank for this exact exam code. Use them to check your readiness here, then continue into the full IT Mastery question bank for broader timed coverage.

Question 1

Topic: Using Object-Oriented Concepts in Java

No imports are required. What is the result of compiling and running this Java 21 code?

public class Project {
    private final String name;
    Project(String name) { this.name = name; }

    class Task {
        String label() { return name + "-task"; }
    }

    static class TaskFactory {
        Task create(Project p) {
            return new Task();
        }
    }

    public static void main(String[] args) {
        var factory = new TaskFactory();
        System.out.print(factory.create(new Project("alpha")).label());
    }
}
  • A. It does not compile; new Task() must be qualified as p.new Task().
  • B. It prints alpha-task.
  • C. It throws NullPointerException when label() is called.
  • D. It does not compile; TaskFactory must be instantiated through a Project object.

Best answer: A

Explanation: Task is a non-static inner class, so each Task object is tied to a Project instance. The static nested TaskFactory has no implicit Project.this, even though create() receives a Project parameter. The allocation must use that parameter explicitly, as in p.new Task(). Member nested classes split into static nested classes and non-static inner classes. A static nested class is not associated with an instance of the enclosing class. A non-static inner class is associated with an enclosing instance, so creating it from a context without an implicit Project instance requires a qualified allocation, such as p.new Task(). In the snippet, TaskFactory is static; its create() method has a Project parameter, but the compiler will not infer that new Task() should use p. The Task object’s access to name would be valid after a correctly qualified creation.


Question 2

Topic: Additional Oracle Candidate Expectations: Logging and Standard Annotations

Given the following Java 21 program, what is the result of compiling and running it?

import java.util.logging.*;

public class LogDemo {
    static class H extends Handler {
        public void publish(LogRecord r) {
            if (isLoggable(r))
                System.out.print(r.getLevel().getName() + ":" + r.getMessage() + " ");
        }
        public void flush() {}
        public void close() {}
    }

    public static void main(String[] args) {
        Logger log = Logger.getLogger("exam.demo");
        log.setUseParentHandlers(false);
        var h = new H();
        h.setLevel(Level.INFO);
        log.addHandler(h);
        log.setLevel(Level.FINE);
        log.fine("fine");
        log.info("info");
        log.warning("warn");
    }
}
  • A. FINE:fine INFO:info WARNING:warn
  • B. WARNING:warn
  • C. INFO:info WARNING:warn
  • D. The program does not compile because Handler cannot be subclassed directly.

Best answer: C

Explanation: The program compiles and uses Java SE’s java.util.logging API directly. The logger level and handler level both matter here: the logger allows the records to be created, and the handler’s isLoggable() check decides which records are printed. In java.util.logging, a Logger level is one filtering point, and each Handler can also filter records. This code sets the logger level to FINE, so fine, info, and warning calls are loggable by the logger. The custom handler is set to INFO, and its publish() method explicitly calls isLoggable(r), so it prints only records at INFO or higher. setUseParentHandlers(false) prevents extra output from parent console handlers. The result is that the FINE message is skipped, while the INFO and WARNING messages are printed in call order.


Question 3

Topic: Working with Streams and Lambda Expressions

An order service needs the alphabetically first 5 distinct SKU strings beginning with BK- across all orders. The current Java 21 code does not compile because it treats each order’s SKU list as if it were a single SKU. Which refactor is the best fix?

record Order(String id, List<String> skus) {}

var result = orders.stream()
    .map(Order::skus)
    .filter(s -> s.startsWith("BK-"))
    .distinct()
    .sorted()
    .limit(5)
    .toList();
  • A. Use flatMap(o -> o.skus().stream()), then filter, distinct, sorted, and limit.
  • B. Use map(o -> o.skus().stream()), then filter, distinct, sorted, and limit.
  • C. Use peek(o -> o.skus().stream()), then continue with the existing filter.
  • D. Keep map(Order::skus), then cast each mapped value to String.

Best answer: A

Explanation: The pipeline needs to flatten nested SKU lists into a stream of individual strings. flatMap is the appropriate operation because it maps each order to a stream of SKUs and then flattens those streams into one Stream<String>. After that, filter, distinct, sorted, and limit operate on SKUs as intended. The original map(Order::skus) produces a Stream<List<String>>, so the next lambda parameter is a list, not a String. Since startsWith() is a String method, the pipeline does not compile. Use flatMap when each input element produces multiple output elements and the downstream operations should see one flattened stream. A suitable pipeline is: java orders.stream() .flatMap(o -> o.skus().stream()) .filter(s -> s.startsWith("BK-")) .distinct() .sorted() .limit(5) .toList(); sorted() before limit() is important here because the requirement asks for the alphabetically first 5 matching distinct SKUs.


Question 4

Topic: Using Java I/O API

Assume this Java 21 code is compiled and run as one source file. What is the result?

import java.io.*;

class Address implements Serializable {
    String city;
    Address(String city) { this.city = city; }
}
class User implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    transient String token;
    Address address;
    User(String n, String t, Address a) { name = n; token = t; address = a; }
}
class Main {
    public static void main(String[] args) throws Exception {
        var bytes = new ByteArrayOutputStream();
        var u = new User("Ada", "T1", new Address("Paris"));
        try (var out = new ObjectOutputStream(bytes)) { out.writeObject(u); }
        u.name = "Grace"; u.token = "T2"; u.address.city = "Rome";
        try (var in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
            var copy = (User) in.readObject();
            System.out.println(copy.name + ":" + copy.token + ":" + copy.address.city);
        }
    }
}
  • A. It prints Grace:T2:Rome.
  • B. It throws java.io.NotSerializableException.
  • C. It prints Ada:null:Paris.
  • D. It fails to compile because Address lacks serialVersionUID.

Best answer: C

Explanation: Serialization writes the non-transient object graph when writeObject is called. The later field mutations affect the original object, not the byte array already written, and the transient token field is restored as null. Java serialization writes the non-transient Serializable object graph at the time writeObject is called. Here, User.name and the referenced Address.city are serialized as Ada and Paris; later mutations to u do not change bytes already written. The token field is marked transient, so it is not serialized and is restored to its default value, null, when the object is read back. serialVersionUID is a static version identifier used for compatibility checks; Address may omit it, though declaring one is recommended. The key takeaway is that deserialization reads stored stream data, not the currently mutated original instance.


Question 5

Topic: Handling Exceptions

What is the result of compiling and running this Java 21 code?

import java.io.IOException;

class Job {
    static void fetch() throws IOException {
        throw new IOException("network");
    }
    static void validate() {
        throw new IllegalStateException("bad state");
    }
    static void runJob() {
        fetch();
        validate();
    }
    public static void main(String[] args) {
        runJob();
    }
}
  • A. The code does not compile because runJob() must handle IllegalStateException.
  • B. The code compiles and throws IOException at runtime.
  • C. The code compiles and throws IllegalStateException at runtime.
  • D. The code does not compile because runJob() must handle IOException.

Best answer: D

Explanation: The compile-time issue is the checked IOException thrown by fetch(). Since runJob() calls fetch() without catching or declaring that checked exception, compilation fails before any runtime behavior can occur. Java requires checked exceptions to be either caught or declared by any method that can throw them. fetch() declares throws IOException, and IOException is checked, so runJob() must handle it with a try/catch or declare throws IOException. By contrast, IllegalStateException is unchecked because it extends RuntimeException, so validate() does not force callers to catch or declare it. The key takeaway is to decide checked-exception legality at compile time before considering which statement would execute at runtime.


Question 6

Topic: Implementing Localization

A reporting service stores event times as Instant. For one report, the same instant is formatted for two audiences:

var event = Instant.parse("2025-07-01T22:30:00Z");
var f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);

System.out.println(f.withLocale(Locale.FRANCE)
    .format(event.atZone(ZoneId.of("Europe/Paris"))));
System.out.println(f.withLocale(Locale.US)
    .format(event.atZone(ZoneId.of("America/New_York"))));

Which statement correctly compares the roles of Locale and ZoneId here?

  • A. Locale selects the zone; ZoneId only affects zone-name text.
  • B. Locale controls localized rendering; ZoneId controls local date/time.
  • C. ZoneId controls localized rendering; Locale controls UTC offset.
  • D. Both values change the stored Instant before formatting.

Best answer: B

Explanation: Locale and ZoneId solve different problems. The locale affects presentation conventions such as language, date order, and separators, while the time zone affects the local calendar date and clock time derived from the same Instant. An Instant is a point on the UTC timeline. Calling event.atZone(ZoneId.of(...)) creates a ZonedDateTime by applying the selected zone’s offset rules, so Paris and New York can show different local dates or times for the same instant. Calling withLocale(...) on the formatter changes how those temporal fields are rendered, such as localized month text, ordering, and formatting conventions. It does not choose the time zone or alter the underlying instant. The key distinction is conversion to local time versus localized display.


Question 7

Topic: Handling Date, Time, Text, Numeric, and Boolean Values

A developer reviews this Java 21 method. Which change fixes the compile-time error without introducing another one?

void check() {
    var label = "ready=" + true;
    boolean ready = true | false;
    char marker = "Y";
    String note = """
            done
            """;
}
  • A. Replace "Y" with 'Y'.
  • B. Replace true | false with true + false.
  • C. Replace the text block with """done""".
  • D. Replace "ready=" + true with "ready=".concat(true).

Best answer: A

Explanation: The compile-time error is the assignment of a String literal to a char. In Java, double quotes create a String, while single quotes create a character literal when exactly one character is present. Java distinguishes text and character literals at compile time. The expression "Y" has type String, so it cannot be assigned to char marker. The expression 'Y' has type char, making it the valid fix. The other nearby expressions are not errors: + can concatenate a String with a boolean value, and | is valid for boolean operands, though it does not short-circuit. A text block is also valid here because the opening """ is followed by a line terminator. The key rule is that literal delimiters determine the literal type before assignment compatibility is checked.


Question 8

Topic: Packaging and Deploying Java Code

An application uses named modules. com.acme.report.api exports package com.acme.report, which contains interface Formatter. Module com.acme.csv contains com.acme.csv.CsvFormatter implements Formatter. Module com.acme.app runs:

ServiceLoader.load(Formatter.class).findFirst();

Assume com.acme.app and com.acme.csv both read com.acme.report.api, and all three modules are resolved in the same layer. The implementation package should not be exported. Which directives correctly enable service loading?

  • A. In com.acme.app: requires com.acme.csv;; in com.acme.csv: exports com.acme.csv;
  • B. In com.acme.app: provides com.acme.report.Formatter with com.acme.csv.CsvFormatter;; in com.acme.csv: uses com.acme.report.Formatter;
  • C. In com.acme.app: uses com.acme.csv.CsvFormatter;; in com.acme.csv: provides com.acme.csv.CsvFormatter with com.acme.report.Formatter;
  • D. In com.acme.app: uses com.acme.report.Formatter;; in com.acme.csv: provides com.acme.report.Formatter with com.acme.csv.CsvFormatter;

Best answer: D

Explanation: JPMS service loading uses the service interface as the contract. The module calling ServiceLoader declares uses, while the module containing the implementation declares provides ... with .... The provider package does not need to be exported for discovery by ServiceLoader. Java module services separate the service contract from its implementation. The uses directive belongs in the module that calls ServiceLoader.load(Formatter.class) and names the service type, Formatter. The provides ... with ... directive belongs in the provider module and names the service type first, followed by the implementation class. Exporting the implementation package is unnecessary for service discovery; exports are for ordinary compile-time access by other modules. The stated requires relationships cover access to the API module, and the provider module is already resolved in the same layer. Directly requiring or exporting the implementation module does not register a service provider.


Question 9

Topic: Controlling Program Flow

Assume this code is in one file and no imports are needed. What is the result?

class SwitchCheck {
    public static void main(String[] args) {
        char code = 'B';
        String result = switch (code) {
            case 65 -> "alpha";
            case 'B' -> "bravo";
            case 66 -> "numeric";
            default -> "other";
        };
        System.out.println(result);
    }
}
  • A. It prints bravo.
  • B. It does not compile because case 66 duplicates case 'B'.
  • C. It prints numeric.
  • D. It does not compile because char is not a valid switch selector type.

Best answer: B

Explanation: The char type is a valid selector type for a switch expression. Integer constant labels can be used with a char selector if their values are representable as char, so 66 is allowed but duplicates 'B'. Switch case constants must be compatible with the selector type, and duplicate case values are not allowed. Here, code has type char, which is a valid switch selector type. The literal 65 can represent 'A', and 66 can represent 'B', so those numeric labels are type-compatible with char. However, case 'B' and case 66 represent the same char value, making the switch expression illegal at compile time. The key rule is that validity is checked before any runtime selection occurs.


Question 10

Topic: Working with Arrays and Collections

Given the following Java 21 code, with the import shown, what is the result of compiling and running it?

import java.util.*;

public class ErasureDemo {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        Object data = new ArrayList<Integer>(List.of(7));
        List<String> words = (List<String>) data;
        System.out.print(words.get(0).length());
    }
}
  • A. It prints 1.
  • B. It throws ClassCastException before printing anything.
  • C. It prints 7.
  • D. It does not compile because data cannot be cast to List<String>.

Best answer: B

Explanation: The code compiles because the cast from Object to List<String> is allowed as an unchecked cast, and the warning is suppressed. At run time, type erasure means the cast verifies only that the object is a List. The failure occurs when the Integer element is retrieved where a String is expected. Generic type arguments are erased at run time, so a cast to List<String> cannot verify that every element is a String. Since data refers to an ArrayList, the assignment cast succeeds after checking the raw List type. The compiler treats words.get(0) as returning String, so it inserts a run-time cast from Object to String before calling length(). The actual element is an Integer, causing ClassCastException and no output is produced. The closest trap is thinking the assignment cast validates the generic argument; it does not.


Question 11

Topic: Managing Concurrent Code Execution

A batch method receives a List<String> files and must count file extensions while using files.parallelStream(). The helper ext(String) is deterministic and returns a non-null extension. Which implementation returns exact counts without unsafe shared mutable state?

  • A. Use HashMap and call merge inside forEach.
  • B. Use Collections.synchronizedMap with getOrDefault then put.
  • C. Use ConcurrentHashMap and call merge inside forEach.
  • D. Use ConcurrentHashMap with getOrDefault then put.

Best answer: C

Explanation: ConcurrentHashMap.merge is the decisive distinction. The map supports concurrent access, and merge performs the per-key read-modify-write atomically, so multiple parallel tasks can update the same extension count safely. When a parallel stream updates shared state, the shared object must either be avoided or updated through operations designed for concurrent compound updates. A regular HashMap can be corrupted by concurrent writes. Wrapping a map with Collections.synchronizedMap protects individual method calls, but getOrDefault followed by put is still a separate read-modify-write sequence. The same lost-update problem exists if those separate calls are made on a ConcurrentHashMap. ConcurrentHashMap.merge combines the read, computation, and write as one atomic per-key operation. The key is to use a concurrent compound operation, not merely a thread-safe container.


Question 12

Topic: Using Object-Oriented Concepts in Java

An order-processing test must instantiate objects from a static utility method. Which replacement for the comment compiles and creates one Cart, one LineItem, one local Receipt, and one anonymous implementation of Discount?

class Shop {
    static class Cart {}
    class LineItem {}
    interface Discount { int apply(int price); }
}
class Demo {
    static void test() {
        Shop shop = new Shop();
        class Receipt {}
        // replace this comment
    }
}
  • A. Object[] objects = { new Shop.Cart(), new Shop.LineItem(), new Receipt(), new Shop.Discount() { public int apply(int p) { return p; } } };
  • B. Object[] objects = { new Shop.Cart(), shop.new LineItem(), new Receipt(), new Shop.Discount() { public int apply(int p) { return p; } } };
  • C. Object[] objects = { shop.new Cart(), shop.new LineItem(), new Receipt(), new Shop.Discount() { public int apply(int p) { return p; } } };
  • D. Object[] objects = { new Shop.Cart(), shop.new LineItem(), new Demo.Receipt(), new Shop.Discount() { public int apply(int p) { return p; } } };

Best answer: B

Explanation: Static nested classes are instantiated through the enclosing type, while non-static inner classes need a specific enclosing instance. The correct fragment also uses the local class inside its scope and creates an anonymous implementation with a proper public interface method. Nested-class creation syntax depends on the declaration context. Shop.Cart is a static nested class, so it is created with new Shop.Cart() and does not use an enclosing object. Shop.LineItem is a non-static inner class, so code in a static method must use an existing Shop instance: shop.new LineItem(). The local class Receipt is visible by simple name within the block after its declaration. The anonymous implementation uses new Shop.Discount() { ... }, and apply must be public because interface methods are public. The common mistakes reverse static-vs-inner creation syntax or treat a local class like a member class.


Question 13

Topic: Additional Oracle Candidate Expectations: Logging and Standard Annotations

A team adds @Override to catch accidental overloads during a refactor. Given this code, which replacement for the comment compiles and verifies an actual override?

class Report {
    void print(Object value) { }
}
class Summary extends Report {
    // insert one method declaration here
}
  • A. @Override void print(String value) { }
  • B. @Override void print(Object... values) { }
  • C. @Override int print(Object value) { return 1; }
  • D. @Override void print(Object value) { }

Best answer: D

Explanation: @Override requires the annotated method to actually override or implement another method. The subclass method with Object as its single parameter matches the superclass method signature, so the annotation is accepted. The other declarations change the parameter list or return type and fail to override correctly. The @Override annotation is a compile-time check. It does not make a method override another method; it verifies that Java’s normal overriding rules are satisfied. For this superclass method, the subclass must use the same method name and compatible parameter list: print(Object). A method with String is an overload, not an override. A varargs parameter Object... is treated as an Object[] parameter, so it also has a different signature. A method with the same parameters but return type int is invalid because void cannot be changed to a value-returning type. The annotation helps reveal these mistakes during compilation.


Question 14

Topic: Working with Streams and Lambda Expressions

A developer is reviewing this Java 21 code. Assume the file name matches the public class. What is the result?

import java.util.function.IntSupplier;

public class CaptureDemo {
    private int bonus = 1;

    public static void main(String[] args) {
        int base = 10;
        int[] box = {2};
        var demo = new CaptureDemo();
        IntSupplier calc = () -> base + box[0] + demo.bonus;
        box[0]++;
        demo.bonus++;
        base++;
        System.out.println(calc.getAsInt());
    }
}
  • A. It prints 15.
  • B. It prints 16.
  • C. It throws IllegalStateException.
  • D. It does not compile because base is not effectively final.

Best answer: D

Explanation: A local variable referenced from a lambda body must be final or effectively final. The variable base is captured by the lambda and later incremented, so it is not effectively final. The program fails at compile time before any output occurs. Lambda bodies may capture local variables only when those variables are final or effectively final. A variable is effectively final when it is assigned once and not reassigned, incremented, or otherwise written afterward. Here, base is used inside the lambda expression and then changed with base++, so the compiler rejects the lambda capture. Mutating box[0] and demo.bonus is different: the captured local variables box and demo are not reassigned; only the objects they refer to are mutated. The decisive failure is the write to the captured local variable base.


Question 15

Topic: Using Java I/O API

Assume this program is run on an unspecified Java SE 21 platform using the default file system. What is the most accurate statement about its result?

import java.nio.file.Path;

public class PathDemo {
    public static void main(String[] args) {
        Path p = Path.of("reports", "2025").resolve("q1.txt");
        System.out.print(p);
    }
}
  • A. It prints reports/2025/q1.txt.
  • B. It prints reports\2025\q1.txt.
  • C. It throws NoSuchFileException.
  • D. It compiles, but the exact separator characters are platform-dependent.

Best answer: D

Explanation: The code compiles and creates a Path; it does not access the file system to verify that the file exists. The printed form of a Path depends on the default file system, so an exam answer should not assume / or \ unless the platform is stated. Path.of("reports", "2025") creates a path from name elements, and resolve("q1.txt") appends another element. These operations do not require the file to exist. When System.out.print(p) runs, it calls p.toString(), whose text form is based on the default file system’s path syntax. On different platforms, separators may be shown differently, so a fixed string with / or \ is not guaranteed by the Java SE API in this stem. The key takeaway is to avoid platform-specific path assumptions unless the question explicitly provides the platform or tests an API with platform-independent behavior.


Question 16

Topic: Handling Exceptions

A developer is comparing possible replacement methods in class CsvParser extends Parser. Given these types, which replacement compiles?

class DataException extends Exception { }
class MissingDataException extends DataException { }

class Parser {
    void parse() throws DataException { }
}
  • A. @Override void parse() throws MissingDataException { }
  • B. @Override void parse() throws Exception { }
  • C. @Override void parse() { try { super.parse(); } catch (DataException e) { } catch (MissingDataException e) { } }
  • D. @Override void parse() { try { super.parse(); } catch (MissingDataException | DataException e) { } }

Best answer: A

Explanation: The valid replacement narrows the checked exception from DataException to MissingDataException. Java allows an overriding method to declare fewer checked exceptions or more specific checked exceptions than the overridden method. Broader checked exceptions and invalid catch hierarchy arrangements do not compile. For overriding, the throws clause of the overriding method cannot introduce broader or unrelated checked exceptions. Since MissingDataException extends DataException, declaring throws MissingDataException is compatible with Parser.parse(). Catch blocks also follow hierarchy rules: a superclass catch before a subclass catch makes the subclass catch unreachable, and multi-catch alternatives cannot be related by subclassing. The key distinction is whether the exception relationship narrows a checked exception legally or makes a handler impossible to reach.


Question 17

Topic: Implementing Localization

What is the output of this Java 21 program?

import java.text.MessageFormat;
import java.util.Locale;

public class Demo {
    public static void main(String[] args) {
        var pattern = "User {1} set '{0}' to ''{2}''";
        var mf = new MessageFormat(pattern, Locale.US);
        System.out.println(mf.format(new Object[] {"mode", "Ari", "debug"}));
    }
}
  • A. User Ari set mode to 'debug'
  • B. User mode set Ari to 'debug'
  • C. The code throws IllegalArgumentException.
  • D. User Ari set {0} to 'debug'

Best answer: D

Explanation: MessageFormat uses numeric placeholders to choose arguments by index, not by their order in the pattern. Single quotes quote pattern text, while doubled single quotes produce a literal apostrophe, so only {1} and {2} are substituted here. In a MessageFormat pattern, {0}, {1}, and similar placeholders refer to argument array positions. A single quote starts or ends quoted literal text, so '{0}' is printed as the literal text {0} rather than being replaced by mode. Two consecutive single quotes represent one literal apostrophe, so ''{2}'' becomes a single quote, the formatted value of argument 2, and another single quote. The key takeaway is that placeholder order and argument index are separate, and quoting controls whether braces are interpreted as placeholders.


Question 18

Topic: Handling Date, Time, Text, Numeric, and Boolean Values

No imports are required. What is the result of compiling and running this Java 21 code?

public class Demo {
    public static void main(String[] args) {
        String word = """
                go
                """.strip();
        boolean ready = word.length() == 2 && !word.isEmpty();
        char code = 'A';
        code = code + 1;
        System.out.println(word + ":" + ready + ":" + code);
    }
}
  • A. It does not compile because code + 1 is an int expression.
  • B. It prints go:true:B.
  • C. It prints go:true:66.
  • D. It does not compile because string concatenation cannot include a boolean.

Best answer: A

Explanation: The code fails at compile time before any output is produced. In Java, arithmetic on a char and an int promotes the char to int, so code + 1 cannot be assigned back to char without an explicit cast. The core rule is binary numeric promotion. A char is an integral primitive, and in code + 1 the char value is promoted to int because 1 is an int literal. The result is not a compile-time constant of type char, so simple assignment back to code is not allowed without an explicit cast. The text block creates a String, strip() returns a String, the boolean expression is valid, and string concatenation can include boolean and char values. The compile-time error occurs at the assignment to code.


Question 19

Topic: Packaging and Deploying Java Code

A team is migrating an application to JPMS. legacy.jar has no module-info.class, but its manifest contains Automatic-Module-Name: legacy.util. It contains legacy.Util.name().

// module-info.java
module com.acme.app {
    requires legacy.util;
}

// com/acme/Main.java
package com.acme;
import legacy.Util;

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

The application was compiled successfully with legacy.jar on the module path. What is the result of this launch command?

java --module-path mods -cp legacy.jar -m com.acme.app/com.acme.Main
  • A. It prints the value returned by Util.name().
  • B. It fails at compile time because automatic modules cannot be required.
  • C. It fails because legacy.util is required but not on the module path.
  • D. It starts main() and then throws NoClassDefFoundError.

Best answer: C

Explanation: The launch fails during module resolution. legacy.jar was placed on the class path, but com.acme.app is a named module that requires legacy.util, so the required module must be observable on the module path. JPMS treats the class path as the unnamed module, and named modules do not read the unnamed module just because a JAR is placed with -cp. Since com.acme.app declares requires legacy.util, the launcher must find a module named legacy.util during boot layer resolution. An automatic module can satisfy that requirement only when the JAR is on the module path. Here, legacy.jar is on the class path, so resolution fails before main() runs.


Question 20

Topic: Controlling Program Flow

In Java 21, what is the result of running this code?

class Demo {
    static String classify(Object value) {
        return switch (value) {
            case String s -> "string";
            default -> "other";
        };
    }
    public static void main(String[] args) {
        System.out.print(classify(null));
    }
}
  • A. It throws NullPointerException at run time.
  • B. It prints other.
  • C. It prints string.
  • D. It does not compile because case null is required.

Best answer: A

Explanation: The switch expression compiles because default makes it exhaustive for non-null Object values. However, Java 21 switch pattern matching does not route a null selector to default; without case null, the switch throws NullPointerException. In Java 21, pattern matching in switch supports type patterns such as case String s, but null handling is still explicit. If the selector expression evaluates to null, the switch can handle it only with a case null label. A default label handles otherwise unmatched non-null values, not null. In the snippet, classify(null) evaluates the selector to null before any pattern can match, so execution fails with NullPointerException. The key takeaway is that default is not a substitute for case null in a pattern switch.


Question 21

Topic: Working with Arrays and Collections

A report component fails to compile in Java 21:

import java.util.*;

class ReportLoader {
    void load(List<String> keys)  { }
    void load(List<Integer> keys) { }
}

Callers must keep one method name, and the API may add one argument. The new API should reject mismatched element declarations at compile time and choose behavior even when the list is empty. Which refactor is the best fix?

  • A. Overload load(List<? extends CharSequence>) and load(List<? extends Number>).
  • B. Dispatch with keys instanceof List<String> inside load(List<?> keys).
  • C. Use <T> void load(List<T> keys, Class<T> elementType).
  • D. Overload load(List<String>[] keys) and load(List<Integer>[] keys).

Best answer: C

Explanation: Java erases generic type arguments, so List<String> and List<Integer> cannot distinguish overloaded methods. A Class<T> token supplies reifiable runtime information while the generic method signature keeps the list and token types aligned. It also works for empty lists because dispatch does not depend on inspecting elements. Generic type arguments such as String and Integer are not preserved as distinct runtime method parameter types. Both original overloads erase to load(List), causing a name clash. Runtime checks cannot use instanceof List<String> because List<String> is non-reifiable. A Class<T> parameter, however, is reifiable and can be compared at runtime, while <T> void load(List<T>, Class<T>) forces normal generic callers to pass a matching list type and token. An implementation might branch on elementType.equals(String.class) or elementType.equals(Integer.class). The key takeaway is to avoid pretending erased element types are available at overload resolution or runtime.


Question 22

Topic: Managing Concurrent Code Execution

A service replaces a fixed platform-thread pool with Executors.newVirtualThreadPerTaskExecutor(). Each submitted task logs Thread.currentThread() and its job number. The tasks are independent, and the team is comparing assumptions used by its diagnostics. Which assumption is INCORRECT for Java 21 virtual threads?

  • A. Completion order is not guaranteed by submission order.
  • B. The executor reuses virtual Thread objects across tasks.
  • C. The current thread inside the task is a virtual thread.
  • D. A new virtual thread is created for each submitted task.

Best answer: B

Explanation: Executors.newVirtualThreadPerTaskExecutor() is a per-task executor, not a virtual-thread pool. Each submitted task gets its own virtual thread, and normal scheduling does not guarantee start or completion order. Java 21 virtual threads are Thread objects, but a virtual-thread-per-task executor creates a new virtual thread for each submitted task. After a virtual thread completes, that Thread instance is done and cannot be restarted for another task. The JVM may mount virtual threads on different carrier platform threads internally, but that carrier reuse is not the same as reusing the virtual thread identity visible through Thread.currentThread(). The key distinction from a fixed platform-thread pool is that worker thread reuse is not the model here.


Question 23

Topic: Using Object-Oriented Concepts in Java

A code review compares statements about this Java 21 type. Assume the test code is in another top-level class that can access Outer. Which statement is invalid?

public class Outer {
    private int id = 7;

    public static class Tool {
        public int size() { return 1; }
    }

    public class Part {
        public int value() { return id; }
    }
}
  • A. new Outer.Tool() can create a Tool without an Outer object.
  • B. new Outer().new Part() can create a Part tied to that Outer object.
  • C. new Outer.Part() can create a Part without an Outer object.
  • D. Part.value() can read id from its enclosing Outer object.

Best answer: C

Explanation: Part is an inner class because it is a non-static member class. Each Part object must be associated with a specific Outer object, so new Outer.Part() is not a valid instantiation expression. A static nested class is created like a normal nested type: new Outer.Tool(). A non-static member class is different: it has an enclosing instance, so code outside Outer must qualify creation with an object, such as new Outer().new Part() or outer.new Part(). Because Part is associated with an Outer, its methods can access that enclosing object’s private instance fields, including id. The key distinction is the static modifier on the nested class declaration.


Question 24

Topic: Additional Oracle Candidate Expectations: Logging and Standard Annotations

Assume this code is compiled and run on Java 21, with no compiler option treating warnings as errors. What is the result?

import java.util.List;

public class Demo {
    @SafeVarargs
    private void show(List<String>... lists) {
        Object[] a = lists;
        a[0] = List.of(100);
        System.out.println(lists[0].get(0).toUpperCase());
    }

    public static void main(String[] args) {
        new Demo().show(List.of("java"));
    }
}
  • A. It prints JAVA.
  • B. It compiles, then throws ClassCastException.
  • C. It does not compile because show is not final.
  • D. It throws ArrayStoreException at the assignment.

Best answer: B

Explanation: In Java 21, @SafeVarargs is permitted on private varargs methods, so the annotation itself is legal here. The annotation suppresses relevant varargs heap-pollution warnings; it does not make the method body safe. The polluted array is detected later when an Integer is read as a String. @SafeVarargs may be applied to constructors and to varargs methods that are static, final, or private. The show method is private, so Java 21 permits the annotation even though it is not final. Inside the method, lists is really a List[], and assigning it to Object[] allows another List to be stored. List.of(100) passes the array store check because it is still a List. The failure occurs when lists[0].get(0) is used as a String; the compiler inserts a cast to String, and the actual value is an Integer.

Revised on Sunday, April 26, 2026