Free Java 21 OCP Practice Test 2

10 advanced exam questions for 1Z0-830 — Structured Concurrency, Unnamed Variables & More

10
Questions
20 min
Suggested Time
~65%
To Pass
Free
No Signup

📋 About This Test

Test 2 covers advanced Java 21 topics beyond Test 1: Structured Concurrency, Unnamed Variables, Nested Record Patterns, Switch on null, and Concurrent Collections.

🆕 Questions marked green cover features NEW in Java 21 — not on the 1Z0-829 (Java 17) exam.

Select an answer for each question, then click Submit & Show Answers for your score and detailed explanations.

🆕 Question 1 — Structured Concurrency

What is the purpose of StructuredTaskScope in Java 21?

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Subtask<String> user = scope.fork(() -> fetchUser()); Subtask<Integer> orderCount = scope.fork(() -> fetchOrderCount()); scope.join(); scope.throwIfFailed(); System.out.println(user.get() + " has " + orderCount.get() + " orders"); }

✅ Correct Answer: C

Structured Concurrency (preview in Java 21) treats a group of related concurrent tasks as a single unit of work. Key properties:

(1) All forked subtasks share the scope's lifetime — they're cancelled if the scope exits.
(2) Failures propagate predictably — ShutdownOnFailure cancels siblings if any subtask fails.
(3) join() waits for all subtasks; throwIfFailed() rethrows if any failed.

This replaces the chaotic "fire and forget" model of executor services with a more disciplined approach.

🆕 Question 2 — Unnamed Variables

What is the output of this code using unnamed variables?

import java.util.*; List<String> names = List.of("Alice", "Bob", "Charlie"); int count = 0; for (var _ : names) { count++; } System.out.println(count);

✅ Correct Answer: A

Java 21 introduces unnamed variables (preview): the underscore _ can be used when you need to declare a variable but don't intend to use its value.

Here, the loop iterates over each name in the list but ignores it (the variable is unnamed). The loop runs 3 times, so count becomes 3.

Unnamed variables are also useful in: pattern matching where some bindings aren't needed, lambda parameters that are unused, catch blocks where the exception isn't used, and try-with-resources.

🆕 Question 3 — Nested Record Patterns

What is the output?

record Address(String city, String zip) {} record Person(String name, Address address) {} Object obj = new Person("Alice", new Address("Bengaluru", "560001")); if (obj instanceof Person(String name, Address(String city, String zip))) { System.out.println(name + " lives in " + city); }

✅ Correct Answer: C

Java 21 record patterns support nested deconstruction. The pattern Person(String name, Address(String city, String zip)) matches the outer record AND simultaneously deconstructs the inner Address record.

This binds name = "Alice", city = "Bengaluru", and zip = "560001" all in one expression. Output: "Alice lives in Bengaluru".

🆕 Question 4 — Switch on null

What is the output when this code is executed?

Object obj = null; String result = switch (obj) { case null -> "got null"; case Integer i -> "integer: " + i; case String s -> "string: " + s; default -> "unknown"; }; System.out.println(result);

✅ Correct Answer: A

Java 21 finalized pattern matching for switch, which now allows an explicit case null label. Without it, a null selector in a pattern switch throws NullPointerException. With it, null is handled gracefully.

Here, obj is null, so the case null branch matches and "got null" is returned.

Note: case null can also be combined with default: case null, default -> ...

🆕 Question 5 — reversed()

What is the output?

import java.util.*; List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5)); List<Integer> reversed = list.reversed(); reversed.set(0, 99); System.out.println(list.get(4));

✅ Correct Answer: C

Java 21's List.reversed() (from SequencedCollection) returns a reverse-ordered VIEW of the list — not a copy. Modifications to the view affect the original list.

reversed.set(0, 99) modifies the first element of the reversed view, which corresponds to the LAST element of the original list (index 4). So list.get(4) returns 99.

This live-view behavior is a common exam trap — many developers assume reversed() creates a copy.

Question 6 — Sealed Classes

What is the purpose of declaring a subclass as non-sealed?

sealed class Vehicle permits Car, Truck, ElectricVehicle {} final class Car extends Vehicle {} sealed class Truck extends Vehicle permits PickupTruck {} non-sealed class ElectricVehicle extends Vehicle {} final class PickupTruck extends Truck {} class Tesla extends ElectricVehicle {} // is this allowed?

✅ Correct Answer: B

A non-sealed subclass opens up the hierarchy again, allowing unrestricted extension. Tesla extends ElectricVehicle is valid because ElectricVehicle is non-sealed.

The three options for sealed class subclasses:
final — cannot be extended further (Car, PickupTruck)
sealed — extended only by permitted subclasses (Truck)
non-sealed — open to any extension (ElectricVehicle)

This gives library designers fine-grained control over their type hierarchy.

Question 7 — ConcurrentHashMap

What is the result of this code?

import java.util.concurrent.*; ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.compute("a", (k, v) -> v + 10); map.compute("b", (k, v) -> (v == null) ? 1 : v + 10); map.computeIfAbsent("c", k -> 100); System.out.println(map.get("a") + " " + map.get("b") + " " + map.get("c"));

✅ Correct Answer: A

Three atomic operations on ConcurrentHashMap:

(1) compute("a", ...): key exists with v=1. Lambda returns 1+10=11.
(2) compute("b", ...): key absent, v is null. Lambda returns 1 (the v==null branch).
(3) computeIfAbsent("c", ...): key absent. Lambda returns 100.

Final values: a=11, b=1, c=100.

Key difference: compute always invokes the lambda (even for absent keys with v=null). computeIfAbsent only invokes if the key is absent or maps to null.

Question 8 — Unmodifiable Collectors

What happens when this code executes?

import java.util.*; import java.util.stream.*; Map<String, Integer> nameToAge = Stream.of("Alice", "Bob", "Charlie") .collect(Collectors.toUnmodifiableMap( name -> name, name -> name.length() )); nameToAge.put("Dave", 4); System.out.println(nameToAge);

✅ Correct Answer: C

Collectors.toUnmodifiableMap creates a Map that cannot be modified after collection. Any mutating operation (put, remove, clear, putAll) throws UnsupportedOperationException.

The map is correctly constructed first: {Alice=5, Bob=3, Charlie=7}. But the put("Dave", 4) call throws UnsupportedOperationException before reaching the println.

Use this collector when you want to guarantee callers can't modify the result. It's a defensive coding best practice.

Question 9 — CompletableFuture

What is the output?

import java.util.concurrent.*; CompletableFuture<Integer> future = CompletableFuture .supplyAsync(() -> 10) .thenApply(x -> x * 2) .thenApply(x -> x + 5); System.out.println(future.get());

✅ Correct Answer: B

CompletableFuture pipelines apply transformations sequentially.

(1) supplyAsync: produces 10
(2) First thenApply: 10 * 2 = 20
(3) Second thenApply: 20 + 5 = 25

Each thenApply receives the previous result and returns a new value. The get() call blocks until the entire pipeline completes, then returns 25.

Note: assume try-catch for the checked InterruptedException and ExecutionException in real code.

Question 10 — Locale & NumberFormat

What is the output of this localization code?

import java.text.*; import java.util.*; NumberFormat fmt = NumberFormat.getCurrencyInstance(Locale.GERMANY); String result = fmt.format(1234.5); System.out.println(result.contains("€"));

✅ Correct Answer: A

NumberFormat.getCurrencyInstance(Locale.GERMANY) returns a formatter configured for German currency (Euro). When formatting 1234.5, the output is "1.234,50 €" — German uses comma as decimal separator and period as thousand separator.

The Euro symbol "€" is included, so contains("€") returns true.

Localization (Locale, NumberFormat, DateTimeFormatter, ResourceBundle) is a meaningful chunk of the OCP exam — around 4-6 questions typically.

Answered 0 of 10 questions

🎉 Test Complete!

0/10

Great work! Review your answers below.

Completed All 4 Free Tests?

You've now seen 40 sample questions across our 4 free tests. The full JavaOCP platform has 2,074 Java 21 OCP questions (1Z0-830) plus 1,884 Java 17 OCP questions (1Z0-829).

That's 78 full certification tests total with detailed explanations, covering every exam topic at every difficulty level.

Start 3-Day Free Trial → View Complete Syllabus

From ₹1,400 / $16 • 6 months unlimited access