Java SE 21 Developer

Question bank

Oracle logo

Java SE 21 Developer

1Z0-830

45 Java SE 21 Developer questions with the correct answer and a written explanation, organised across 11 exam domains.

45 questions
11 domains
Foundation level

Question index

Jump straight to any question. Answers and explanations are inline.

Arrays and Collections

4 questions in this domain

  1. Question 1Single Choice

    What must be called on a Thread object to begin separate concurrent execution?

    • ACall the run() method directly.
    • BCall the start() method once.Correct answer
    • CCreate the Thread subclass.
    • DOverride the toString() method.

    Why this is the answer

    Calling start() makes the JVM schedule a new thread; run() is invoked inside that new thread. Calling run() directly does not launch concurrency.

  2. Question 2Multiple Select

    Which statements are true about calling run() versus start() on a Thread object? Select all that apply.

    • AA direct run() call executes the thread body in the current thread.Correct answer
    • BA direct start() call creates a new execution context and runs the task asynchronously.Correct answer
    • CCalling start() more than once on the same Thread object is valid.
    • DA direct run() call always starts a new daemon thread.

    Why this is the answer

    run() is synchronous when invoked directly; start() schedules a new thread. A Thread cannot be restarted, so a second start() throws IllegalThreadStateException.

  3. Question 3Interactive

    The code block submits a Callable that throws an exception, then calls Future.get() inside a try-catch. What is the class name printed by the code?

    • Ajava.lang.IllegalStateException
    • Bjava.util.concurrent.ExecutionExceptionCorrect answer
    • Cjava.lang.RuntimeException
    • Djava.lang.Exception

    Why this is the answer

    submit() stores task failure in the Future. Future.get() surfaces the failure as ExecutionException, with IllegalStateException as its cause.

  4. Question 4Single Choice

    Why should a ReentrantLock acquired inside a method be unlocked in a finally block?

    • ALock the object again before exiting the method.
    • BPlace the unlock() call in a finally block.Correct answer
    • CRely on synchronized to release the lock automatically.
    • DUse System.gc() to force lock release.

    Why this is the answer

    Explicit Lock does not use synchronized's automatic monitor release; unlock() must be called, and finally ensures it runs whether the guarded code succeeds or throws.

Concurrency

7 questions in this domain

  1. Question 5Single Choice

    In Java, what is the result of creating a Thread object without calling start()?

    • AIt represents the thread, but execution is not started.Correct answer
    • BIt immediately invokes the run() method.
    • CIt allocates a new running thread.
    • DIt throws an exception until start() is called.

    Why this is the answer

    A Thread object is a handle to the execution state. The actual task runs only after start() is called.

  2. Question 6Multiple Select

    Which two statements are true when calling a Thread's run() method directly instead of using start()?

    • AIt executes run() in the current thread.Correct answer
    • BIt does not create a new thread of execution.Correct answer
    • CIt starts a new daemon thread.
    • DIt can be called only once after start().

    Why this is the answer

    Directly calling run() invokes its body synchronously in the caller's thread and does not start a separate thread.

  3. Question 7Interactive

    Refer to the code block. What is the behavior of the operation that retrieves the Future's result?

    • AIt blocks until the result is available and then returns it.Correct answer
    • BIt returns a default value if the result is not ready.
    • CIt throws an exception only if the task was cancelled.
    • DIt polls the task once and returns immediately.

    Why this is the answer

    Future.get() is blocking: it waits until the task completes and then returns the result or throws an execution-related exception.

  4. Question 8Single Choice

    Why can extending Thread make it harder to reuse a task class?

    • AA Java class can extend only one superclass.Correct answer
    • BThread methods cannot be overridden.
    • CExtending Thread prevents execution on a virtual thread.
    • DThread must be subclassed inside its own file.

    Why this is the answer

    Java allows only one superclass, so extending Thread prevents the task class from extending another useful class.

  5. Question 9Multiple Select

    Which two statements correctly compare the execute() and submit() methods of ExecutorService?

    • Aexecute() returns void, while submit() returns a Future.Correct answer
    • BBoth execute() and submit() can capture the exception in a Future.
    • CAn exception from execute() cannot be read through Future.get().Correct answer
    • Dsubmit() rethrows task exceptions immediately on the caller's thread.

    Why this is the answer

    execute() has no Future, so async exceptions are not captured for later retrieval; submit() returns a Future that can hold the exception.

  6. Question 10Interactive

    Refer to the code block. Which condition must be true before the await call is made?

    • AThe associated Condition's Lock must be held.Correct answer
    • BA synchronized monitor must be held.
    • CThe lock must already be unlocked.
    • DThe thread must be marked as a daemon thread.

    Why this is the answer

    Condition.await() must be called while holding the Lock associated with that Condition, just as wait() requires a synchronized monitor.

  7. Question 11Single Choice

    What is the relationship between virtual threads and ExecutorService in Java 21?

    • AExecutorService is still used for per-task virtual thread execution.Correct answer
    • BVirtual threads make ExecutorService unnecessary.
    • CExecutorService can only run platform threads.
    • DVirtual threads require a fixed-size thread pool.

    Why this is the answer

    Virtual threads change how threads are created, but ExecutorService remains useful, for example via newVirtualThreadPerTaskExecutor().

Controlling Program Flow

2 questions in this domain

  1. Question 12Single Choice

    What happens when you instantiate a Thread object in Java?

    • AImmediately executes run() on the current thread.
    • BStarts the new execution flow immediately.
    • CCreates Thread instance; execution flow starts on start().Correct answer
    • DBlocks until another thread calls notify().

    Why this is the answer

    Creating a Thread object does not start a new execution; the new flow begins only when start() is called. Calling run() directly executes synchronously, so a Thread object is not the same as running code.

  2. Question 13Multiple Select

    Which statements correctly describe CompletableFuture methods for controlling flow? (Select all that apply.)

    • A.thenAccept() returns CompletableFuture<Void>.Correct answer
    • B.thenApply() returns CompletableFuture<Void>.
    • C.allOf() returns CompletableFuture<Void> after all inputs finish.Correct answer
    • D.allOf() collects input results into one future.

    Why this is the answer

    thenAccept() consumes a result and returns CompletableFuture<Void>; allOf() also returns CompletableFuture<Void> and does not combine values. thenApply() transforms a result, and allOf() never bundles individual results into a result-bearing future.

Handling Date, Time, Text, Numeric and Boolean Values

4 questions in this domain

  1. Question 14Single Choice

    Which statement correctly describes the relationship between a Thread object and the actual execution it may perform?

    • ACalling start() schedules separate execution.Correct answer
    • BConstructing a Thread starts it automatically.
    • CCalling run() creates a new thread.
    • DConstructing a Thread invokes run() immediately.

    Why this is the answer

    Creating a Thread only creates an object; start() schedules the separate execution, while run() alone runs in the current thread.

  2. Question 15Multiple Select

    Which statements about ExecutorService methods and Future behavior are true? Choose all that apply.

    • Asubmit() returns a Future.Correct answer
    • Bexecute() returns a Future.
    • CFuture.get() may block waiting for the result.Correct answer
    • DFuture.get() is always non-blocking.

    Why this is the answer

    submit() returns a Future that can represent the task result, while execute() returns void. Calling get() can block until the computation completes.

  3. Question 16Interactive

    Based on the supplied code, what is the type of the variable that receives the returned value?

    • ACompletableFuture<Void>Correct answer
    • BCompletableFuture<String>
    • CString
    • Dvoid

    Why this is the answer

    The final call applies a Consumer for side effects and returns a CompletableFuture<Void>, not a future containing the original result.

  4. Question 17Single Choice

    Which statement is true about CompletableFuture.allOf()?

    • AIt returns CompletableFuture<Void> after all complete.Correct answer
    • BIt returns all results in a collection.
    • CIt returns the first future's value.
    • DIt requires a combining function.

    Why this is the answer

    CompletableFuture.allOf() returns a CompletableFuture<Void> that completes when all supplied futures complete; it does not combine their values.

Handling Exceptions

4 questions in this domain

  1. Question 18Single Choice

    Which statement accurately describes the relationship between a Thread object and the execution of its run() method?

    • ACreating a Thread object immediately begins execution of that thread.
    • BCalling start() creates a new execution path that calls run() asynchronously.Correct answer
    • CThe run() method, when called directly, executes in a new thread.
    • DThe Thread object itself is the actual operating-system thread.

    Why this is the answer

    A Thread object is only a handle. Without start(), no separate thread is created. The start() method asynchronously schedules run() to execute in a new thread, while calling run() directly is just an ordinary method call in the current thread.

  2. Question 19Multiple Select

    A developer submits tasks to an ExecutorService. Which two statements are true about exception handling with execute() and submit()?

    • AIf a task submitted with submit() throws an exception, the underlying cause is exposed when get() is called on the returned Future.Correct answer
    • BTasks submitted with execute() do not return a Future, so their exceptions are not surfaced to the caller through Future.get().Correct answer
    • CChecked exceptions from tasks submitted with submit() are wrapped in RuntimeException before being returned by get().
    • DBoth execute() and submit() store any thrown exception until the thread that submitted the task calls get().

    Why this is the answer

    submit() returns a Future that stores the failure; get() throws an ExecutionException whose cause is the original exception. execute() returns void, so no Future exists, and task exceptions propagate to the worker thread instead of being delivered through Future.get().

  3. Question 20Interactive

    The code shows a task submitted to an ExecutorService, followed by Future.get() inside a try-catch block. If the Callable throws IllegalStateException(\"boom\"), what value is printed by e.getCause().getMessage()?

    • AboomCorrect answer
    • BIllegalStateException: boom
    • CExecutionException: boom
    • DException in thread ... java.lang.IllegalStateException: boom

    Why this is the answer

    Future.get() throws ExecutionException when the task completed with an exception. The original IllegalStateException is stored as the cause, so e.getCause().getMessage() returns exactly \"boom\".

  4. Question 21Single Choice

    Why should lock.unlock() be called in a finally block when using ReentrantLock?

    • AA finally block ensures the lock is released after normal completion or after an exception.Correct answer
    • BA finally block prevents the lock from being reentrantly acquired.
    • CA finally block causes the lock to be released only when no exception is thrown.
    • DA finally block is optional because ReentrantLock releases itself automatically when the protected block exits.

    Why this is the answer

    Unlike synchronized blocks, explicit Lock implementations do not automatically release the lock. Putting unlock() in a finally block guarantees that it executes on both the normal path and the exception path, preventing the lock from being held indefinitely.

I/O and NIO.2

3 questions in this domain

  1. Question 22Single Choice

    Which statement correctly describes the behavior of Future.get() when it is called before a submitted task has finished?

    • AFuture.get() is non-blocking.
    • BFuture.get() blocks until completion.Correct answer
    • CFuture.get() cancels unfinished tasks.
    • DFuture.get() returns null when idle.

    Why this is the answer

    Future.get() is a blocking call. It waits until the task completes before returning the result, and it can throw ExecutionException if the task failed.

  2. Question 23Multiple Select

    According to the playbook, which two statements are correct about exception handling in ExecutorService execute() and submit()?

    • Aexecute() cannot return a Future.Correct answer
    • Bsubmit() returns a Future.Correct answer
    • Cexecute() always hides task exceptions.
    • Dsubmit() ignores task exceptions.

    Why this is the answer

    execute() returns void and cannot store a failure; submit() returns a Future, so Future.get throws ExecutionException for failed tasks.

  3. Question 24Interactive

    Consider the code block that creates a Thread with a Runnable and then calls the displayed method. What is the result?

    • ATask runs synchronously in the current thread.Correct answer
    • BA separate thread asynchronously executes the task.
    • CThe code does not compile.
    • DThe task is submitted to a thread pool.

    Why this is the answer

    Calling run() directly invokes the task synchronously in the caller's thread. No separate thread is started.

Localization

2 questions in this domain

  1. Question 25Single Choice

    Which statement correctly describes creating a Thread object relative to the underlying system thread or asynchronous execution?

    • AStarts a new execution automatically
    • BExecution begins only after start() is calledCorrect answer
    • CCalling run() invokes it in a new thread
    • DThread object and actual execution are identical

    Why this is the answer

    Creating a Thread object only creates an instance. Execution begins only when start() is invoked, while calling run() directly runs synchronously and does not start a new thread.

  2. Question 26Multiple Select

    Which two statements accurately describe the behavior of thenAccept and thenApply methods on CompletableFuture instances?

    • AthenAccept returns CompletableFuture<Void>Correct answer
    • BthenApply returns a CompletableFuture with transformed resultCorrect answer
    • CthenApply returns CompletableFuture<Void>
    • DthenAccept transforms the result and returns a CompletableFuture

    Why this is the answer

    thenAccept is for consuming results and returns CompletableFuture<Void>; thenApply transforms results and returns a CompletableFuture containing the transformed value.

Modules and Deployment

3 questions in this domain

  1. Question 27Single Choice

    In Java concurrency, what is the effect of merely creating an instance of the Thread class?

    • AThe JVM allocates a new operating-system thread and begins executing the associated task.
    • BA Thread object exists, but no execution begins until start() is called.Correct answer
    • CThe run() method is automatically invoked in the current thread.
    • DThe thread immediately enters the TIMED_WAITING state.

    Why this is the answer

    Creating a Thread object only creates an instance; no execution begins until start() is invoked. Calling run() would execute synchronously in the calling thread, which is a common trap.

  2. Question 28Multiple Select

    Which two statements are true about invoking run() directly on a Thread object instead of calling start()?

    • AThe code inside run() executes on the current calling thread.Correct answer
    • BThe code inside run() executes synchronously, not on a new thread.Correct answer
    • CA new thread is started and run() executes on that new thread.
    • DThe thread automatically changes from NEW to TIMED_WAITING.

    Why this is the answer

    Directly calling run() executes it as an ordinary method in the current thread. To launch a new thread of execution, start() must be used instead.

  3. Question 29Interactive

    Inspect the Java code in the code block. What is true about the call to future.get()?

    • AIt immediately returns 42 even if the task has not completed because the lambda already supplied the value.
    • BIt blocks until the task completes, and then returns the integer 42.Correct answer
    • CIt returns a Future that will be completed later.
    • DIt prevents the ExecutorService from ever shutting down.

    Why this is the answer

    Future.get() without a timeout blocks until the task is complete and then returns the result, so the main thread obtains 42 after submission.

Object-Oriented Concepts

9 questions in this domain

  1. Question 30Single Choice

    What happens when a Thread object is created?

    • AIt only creates a Thread instance.Correct answer
    • BIt immediately starts a new thread.
    • CIt automatically calls run() in parallel.
    • DIt schedules the task on the main thread.

    Why this is the answer

    Creating a Thread object only creates an instance. The start() method must be called to create and run a new execution thread.

  2. Question 31Multiple Select

    Which statements about Thread.run() are true? Select all that apply.

    • ADirect run() calls execute synchronously.Correct answer
    • BAlways call run() to launch a thread.
    • Crun() can be overridden in a subclass.Correct answer
    • Dstart() causes run() to run on another thread.Correct answer

    Why this is the answer

    Calling run() directly executes it in the caller's thread. The start() method creates the new call stack and then invokes run() on the new thread.

  3. Question 32Interactive

    Given the code block, what output is printed to the console?

    • Atask mainCorrect answer
    • Bmain task
    • Ctask task main
    • DNo output

    Why this is the answer

    The code calls t.run() directly, so the Runnable executes synchronously in the main thread. Therefore, task prints before main.

  4. Question 33Single Choice

    Which statement about Future.get() is correct?

    • AIt blocks until a result is available.Correct answer
    • BIt returns immediately with the task result.
    • CIt never blocks the calling thread.
    • DIt returns a null Future if the task succeeds.

    Why this is the answer

    Future.get() is not a non-blocking poll. It blocks the calling thread until the asynchronous task completes and the result is available.

  5. Question 34Multiple Select

    Which statements about execute() and submit() are true? Select all that apply.

    • Asubmit() accepts Callable and returns Future.Correct answer
    • BFuture.get() exposes failures from submitted tasks.Correct answer
    • CTask exceptions from execute() reach the uncaught handler.Correct answer
    • Dexecute() returns a Future like submit().

    Why this is the answer

    Unlike execute(), submit() accepts Callable and returns Future. Failures from submitted tasks are visible through Future.get(). Direct execute() routes exceptions to an uncaught handler.

  6. Question 35Interactive

    Review the code block. Which statements are correct? Select all that apply.

    • AThe text ready is printed before shutdown is called.Correct answer
    • Bget() blocks until the task is complete.Correct answer
    • Csubmit() returns a Future for the task.Correct answer
    • DnewFixedThreadPool creates virtual thread tasks.

    Why this is the answer

    submit() returns a Future, and get() blocks until the task finishes. After the value is printed, the finally block calls shutdown().

  7. Question 36Single Choice

    Which practice is essential when using an explicit Lock object?

    • ACall unlock() in a finally block.Correct answer
    • BUse synchronized blocks with every explicit lock.
    • CLet the JVM release the Lock automatically.
    • DRelease the Lock only at program shutdown.

    Why this is the answer

    An explicit Lock has no automatic release. Placing unlock() in a finally block guarantees that the lock is released even if an exception occurs.

  8. Question 37Multiple Select

    Which statements about Java object monitors are true? Select all that apply.

    • AA monitor exists for each Java object.Correct answer
    • BAll monitors in the application are shared as one.
    • CSynchronizing on the same object blocks competing threads.Correct answer
    • DA monitor checks every field access automatically.

    Why this is the answer

    Every Java object has an intrinsic monitor. Threads must synchronize on the same object to block one another; a monitor does not protect fields automatically.

  9. Question 38Interactive

    Review the code block. Which precondition is required for the method call inside the try block?

    • AThe current thread must hold the Lock.Correct answer
    • BThe current thread must also call Thread.sleep().
    • CThe current thread must be a daemon thread.
    • DThe current thread must use a synchronized method.

    Why this is the answer

    The blocking call requires the current thread to own the associated Lock. It is the lock-based counterpart of wait(), not a synchronized-method requirement.

Streams and Lambda Expressions

5 questions in this domain

  1. Question 39Single Choice

    When a Thread object is created, which invocation starts the separate execution of its task?

    • ACall start().Correct answer
    • BCall run().
    • CCall join().
    • DCall yield().

    Why this is the answer

    A new Thread object is not running. The start() method creates the new call stack and then invokes run() on that stack; calling run() directly executes it in the caller's thread.

  2. Question 40Multiple Select

    While working with an ExecutorService, which statements correctly contrast execute() and submit() regarding return values and exceptions?

    • Asubmit() returns a Future.Correct answer
    • Bexecute() returns a Future.
    • Cexecute() accepts Runnable only.Correct answer
    • Dsubmit() accepts Callable tasks.Correct answer

    Why this is the answer

    submit() accepts both Runnable and Callable and returns a Future for cancellation and result retrieval. execute() accepts Runnable, returns void, and does not expose a Future for that task.

  3. Question 41Interactive

    The source shows a CompletableFuture chain. What static type is inferred for the result variable?

    • AVoid
    • BFuture<Void>
    • CCompletableFuture<String>
    • DCompletableFuture<Void>Correct answer

    Why this is the answer

    thenAccept() takes a Consumer and returns CompletableFuture<Void>; it does not transform the value. Only a stage such as thenApply() would return CompletableFuture<String>.

  4. Question 42Single Choice

    Which Java inheritance rule prevents a class from extending both Thread and another parent class?

    • AIt cannot extend another class.Correct answer
    • BIt cannot override run().
    • CIt cannot be declared final.
    • DIt cannot implement an interface.

    Why this is the answer

    Java supports single inheritance for classes. Once a class extends Thread, it cannot extend another class, although it may implement multiple interfaces and override run().

  5. Question 43Multiple Select

    What happens when a thread calls get() on a Future that has not yet completed?

    • AIt returns immediately with null.
    • BIt blocks until the result is ready.Correct answer
    • CIt can throw ExecutionException.Correct answer
    • DIt can throw InterruptedException.Correct answer

    Why this is the answer

    Future.get() blocks until the computation completes, if necessary. It throws ExecutionException when the task has failed and InterruptedException if the waiting thread is interrupted.

Supplementary Topics

2 questions in this domain

  1. Question 44Single Choice

    Which statement correctly describes how exception handling differs between execute(Runnable) and submit(Callable<T>) tasks?

    • Aexecute() returns a Future, but submit() does not return any handle to the task.
    • BExceptions from execute() are not available to the caller; submit() captures failures in its returned Future.Correct answer
    • CBoth execute() and submit() throw ExecutionException synchronously at the call site.
    • DBoth methods require InterruptedException to be handled at the call site.

    Why this is the answer

    execute() is void, so task exceptions are not returned to the caller. submit() returns a Future, and the exception is revealed when Future.get() throws ExecutionException.

  2. Question 45Multiple Select

    Which statements about CompletableFuture methods are true? (Choose all that apply.)

    • AthenApply() is used to consume a result and returns CompletableFuture<Void>.
    • BthenAccept() consumes a stage's result and returns CompletableFuture<Void>.Correct answer
    • CallOf() returns a CompletableFuture<Void> that completes when all provided CompletableFutures complete.Correct answer
    • Dexceptionally() can recover from exceptions thrown by stages chained after it.

    Why this is the answer

    thenAccept() consumes the result and returns CompletableFuture<Void>. allOf() does not aggregate results; it returns a CompletableFuture<Void> that completes when all inputs complete. exceptionally() only handles exceptions from previous stages, not downstream stages.

Frequently asked questions

Everything about the Java SE 21 Developer question bank and how to use it.