SaveMyCert
Log in
5 of 5 free questions left today·for 30 a day
1Z0-831 practice

1Z0-831 sample questions — 25 answered and explained

25 1Z0-831 sample questions written to the official Oracle Certified Professional: Java SE 25 Developer exam guide, grouped by exam domain and weighted like the real exam. Each shows the correct answer, why every other option is wrong, and the lesson that teaches it. None of them repeat the examples on the other 1Z0-831 practice pages.

On this page
25
answers included
Full bank
953
on the practice page
Domains
10
weighted like the exam
Free, no account
5/day
a free account allows 30 a day

Domain 1: Handling Date, Time, Text, Numeric and Boolean Values

11% of the 1Z0-831 exam, so 3 of the 25 questions on this page.

Question 1Handling Date, Time, Text, Numeric and Boolean Values

Given:public class Main { public static void main(String[] args) { int a = 2, b = 3, c = 4; int r = a + b * c % 5 - c / a; System.out.println(r); } }What is the result?

Choose one.

  • a
    It prints 2 Correct

    *, / and % share the highest precedence and run left to right: b * c = 12, 12 % 5 = 2, c / a = 2. Then 2 + 2 - 2 = 2.

  • b
    It prints 12

    12 comes from evaluating c % 5 first (4) and then b * 4. Operators of equal precedence associate left to right, so b * c is computed first.

  • c
    It prints -2

    -2 comes from adding before multiplying, ((a + b) * c) % 5 - c / a. Multiplicative operators bind tighter than +.

  • d
    It prints 4

    4 is (a + b * c) % 5, which applies % to the whole sum and drops the - c / a term. % only applies to b * c here.

The concept

Multiplicative operators (* / %) have higher precedence than additive ones (+ -), and operators of equal precedence associate left to right.

Why that’s the answer

The expression groups as a + ((b * c) % 5) - (c / a). Each wrong value comes from one grouping mistake: % before * (12), + before * (-2), or % applied to the whole sum (4).

How to reason it out
  1. Group the multiplicative operators first: (b * c) % 5 and c / a.
  2. Within that level, go left to right: 12, then 12 % 5 = 2.
  3. c / a = 2.
  4. Finish left to right: 2 + 2 - 2 = 2.

Exam tip: * / % bind tighter than + -, and equal-precedence operators run left to right.

Java Primitives, Wrapper Classes, Operators and Casting (1Z0-831) — the lesson that teaches this.

Question 2Handling Date, Time, Text, Numeric and Boolean Values

Given:public class Main { public static void main(String[] args) { int a = 1, b = 2; char c = 'A'; System.out.println(a + b + "x" + a + b); System.out.println(c + a + "" + c + a); } }What is the result?

Choose one.

  • a
    Line 1 prints 3x3 and line 2 prints 66A1

    Once the expression is a String, the next + concatenates rather than adds, so a + b after "x" appends 1 and then 2, not 3.

  • b
    Line 1 prints 3x12 and line 2 prints 66A1 Correct

    + is left-associative: 1 + 2 = 3, then concatenation gives 3x, 3x1, 3x12. On line 2, 'A' + 1 is int 66 (char is numeric), then + "" makes it a String: 66, 66A, 66A1.

  • c
    Line 1 prints 3x12 and line 2 prints B1A1

    'A' + 1 is evaluated before any String is involved, so it is int addition producing 66, not the char 'B'.

  • d
    Line 1 prints 3x3 and line 2 prints B1A1

    Both lines are misread: after "x" each + appends, so line 1 ends 3x12, and 'A' + 1 is int addition giving 66, not the char 'B'.

The concept

+ evaluates left to right and means numeric addition until one operand is a String; char participates as a number.

Why that’s the answer

Each + is decided by the types of its two operands at that point. Before the first String, numbers (and chars) are added; after it, every + concatenates. 'A' + 1 is therefore 66, and 1 + 2 is 3, but everything after "x" or "" is appended text.

How to reason it out
  1. Group + left to right: ((((a + b) + "x") + a) + b).
  2. Before the String: 1 + 2 = 3; after it: append 1 then 2.
  3. Line 2: 'A' + 1 is int addition, 65 + 1 = 66.
  4. Then + "" turns it into text: 66, then append A and 1.

Exam tip: + adds until a String appears, then concatenates; a char before that point is a number.

Java Primitives, Wrapper Classes, Operators and Casting (1Z0-831) — the lesson that teaches this.

Question 3Handling Date, Time, Text, Numeric and Boolean Values

Given:public class Main { public static void main(String[] args) { int x = 0; boolean r = (x > 0) && (++x > 0); boolean s = (x > 0) | (++x > 0); System.out.println(r + " " + s + " " + x); } }What is the result?

Choose one.

  • a
    It prints false true 2

    That assumes both ++x expressions ran. && short-circuits, so the first ++x never executes.

  • b
    It prints false true 1 Correct

    && skips its right operand because x > 0 is false, so x stays 0 and r is false. | evaluates both sides: x > 0 is false, ++x makes x 1 and is true, so s is true.

  • c
    It prints false false 1

    The boolean | operator is true when either side is true, and ++x > 0 is true, so s is true.

  • d
    It prints true true 2

    r cannot be true: && needs both sides true and x > 0 is false, so && stops and returns false.

The concept

&& and || short-circuit; the boolean & and | operators always evaluate both operands.

Why that’s the answer

The first expression stops after x > 0 is false, so its ++x never runs. The second uses |, which evaluates both sides even when the left is false, so ++x runs once in total and x ends at 1.

How to reason it out
  1. Evaluate r: x > 0 is false, && stops, r = false, x = 0.
  2. Evaluate s: | runs both sides; x > 0 is false.
  3. ++x makes x 1, and 1 > 0 is true, so s = true.
  4. Print r, s and x: false true 1.

Exam tip: && and || may skip the right side; & and | never do.

Java Primitives, Wrapper Classes, Operators and Casting (1Z0-831) — the lesson that teaches this.

Domain 2: Implementing Program Flow Control Using Decision and Looping Constructs

8% of the 1Z0-831 exam, so 2 of the 25 questions on this page.

Question 4Implementing Program Flow Control Using Decision and Looping Constructs

Given:public class Main { public static void main(String[] args) { int level = 2; String label = switch (level) { case 1 -> "low"; case 2 -> "mid"; case 3 -> "high"; }; System.out.println(label); } }What is the result?

Choose one.

  • a
    mid

    level is 2, but the program never runs: the switch expression is not exhaustive.

  • b
    null

    A switch expression never yields an implicit null for an uncovered value; the compiler rejects it.

  • c
    Compilation fails Correct

    An int selector has more values than 1, 2 and 3, so a switch expression on it needs default. javac: the switch expression does not cover all possible input values.

  • d
    A MatchException is thrown

    MatchException is a runtime failure for patterns or enums changed after compilation. Here the gap is visible at compile time, so it never runs.

The concept

A switch expression must produce a value for every possible selector value. For int, char, String and similar selectors that means a default (or an unconditional pattern).

Why that’s the answer

The compiler checks exhaustiveness of every switch expression. Three int constants cover only three of about four billion values, so without default the code does not compile, even though level happens to be 2. The tempting answer mid ignores the compile-time check; null and MatchException imagine a runtime fallback that does not exist for this case.

How to reason it out
  1. Identify the construct as a switch expression (its value is assigned).
  2. Check whether the labels cover every possible int value.
  3. They do not, and there is no default.
  4. So compilation fails before any output.

Exam tip: A switch expression on int or String needs default; exhaustiveness is checked at compile time.

Java Flow Control: if/else, switch and Loops (1Z0-831) — the lesson that teaches this.

Question 5Implementing Program Flow Control Using Decision and Looping Constructs

Given:public class Main { enum Size { S, M, L } static int price(Size s) { return switch (s) { case S -> 5; case M -> 7; case L -> 9; }; } public static void main(String[] args) { for (Size s : new Size[] {Size.L, null}) { System.out.print(price(s)); } } }What is the result?

Choose one.

  • a
    9 is printed, then a MatchException is thrown

    MatchException covers an enum constant added after compilation. A null selector without case null throws NullPointerException instead.

  • b
    Compilation fails: the switch has no default label

    A switch expression over an enum is exhaustive when every constant is listed, so default is not required.

  • c
    9 is printed, then a NullPointerException is thrown Correct

    The enum switch is exhaustive without default, so it compiles. Size.L gives 9; the null element reaches a switch with no case null, which throws NullPointerException.

  • d
    9 is printed, then the loop ends with no exception

    The second element is null; switching on it without case null is not silently skipped.

The concept

A switch expression on an enum is exhaustive when it lists every constant, so default is optional. Exhaustiveness does not cover null: a switch with no case null throws NullPointerException when the selector is null.

Why that’s the answer

Two facts combine here. First, S, M and L are all covered, so the switch expression compiles without default. Second, null is never matched by constant labels or by default; unless the switch has case null, a null selector throws NullPointerException. So the loop prints 9 for Size.L and then fails on null.

How to reason it out
  1. Check exhaustiveness: every Size constant has an arm, so it compiles.
  2. First element Size.L returns 9, which is printed.
  3. Second element is null and the switch has no case null.
  4. A null selector without case null throws NullPointerException.

Exam tip: Listing every enum constant makes a switch expression exhaustive, but only case null handles null.

Java Flow Control: if/else, switch and Loops (1Z0-831) — the lesson that teaches this.

Domain 3: Applying Object-Oriented Principles in Java Programs

18% of the 1Z0-831 exam, so 4 of the 25 questions on this page.

Question 6Applying Object-Oriented Principles in Java Programs

Given:class Config { static String mode; static { mode = "prod"; System.out.print("load "); } } public class Main { public static void main(String[] args) { System.out.print("start "); System.out.print(Config.mode + " "); System.out.print(Config.mode); } }What is the result?

Choose one.

  • a
    load start prod prod

    Config is not initialized when Main starts; class initialization is triggered by the first use of Config.mode.

  • b
    start load prod load prod

    A static initializer runs once per class; the second read of Config.mode uses the already initialized class.

  • c
    start load prod prod Correct

    Config is initialized on the first read of its static field, after start is printed, and its static block runs only once.

  • d
    start prod prod

    Reading a non-constant static field triggers class initialization, so the static block does run and print load.

The concept

A class is initialized lazily on its first active use (such as reading a non-constant static field), and its static initializers run exactly once.

Why that’s the answer

main prints start. The expression Config.mode reads a static field that is not a compile-time constant, so the JVM initializes Config at that point: its static block sets mode and prints load. The value prod is then printed. The second read finds Config already initialized, so only prod is printed again.

How to reason it out
  1. Main prints start before touching Config.
  2. The first read of a non-constant static field initializes Config.
  3. The static block runs once: prints load and sets mode.
  4. Later reads do not re-run it.

Exam tip: Class initialization is lazy and happens once: on first active use, never again.

Java Classes, Records, Constructors and Initializers (1Z0-831) — the lesson that teaches this.

Question 7Applying Object-Oriented Principles in Java Programs

Given:class Lamp { boolean on = false; void Lamp() { on = true; } } public class Main { public static void main(String[] args) { System.out.println(new Lamp().on); } }What is the result?

Choose one.

  • a
    true

    This assumes void Lamp() runs as a constructor; a declaration with a return type is a method, even when named after the class.

  • b
    Compilation fails

    A method may legally have the same name as its class; javac accepts it, though it is confusing style.

  • c
    false Correct

    void Lamp() has a return type, so it is an ordinary method that is never called; the default constructor leaves on as false.

  • d
    An exception is thrown at runtime

    Nothing in the code can throw: the compiler-generated default constructor runs and on is read normally.

The concept

A constructor has no return type. Adding one, even void, turns the declaration into a method with the class's name.

Why that’s the answer

Because void Lamp() declares a return type, it is a method, not a constructor. Lamp then declares no constructor, so the compiler supplies a default one. new Lamp() runs only the field initializer on = false; the method Lamp() is never invoked. The program prints false.

How to reason it out
  1. Check each member named Lamp for a return type.
  2. void Lamp() has one, so it is a method.
  3. With no declared constructor, the default constructor is used.
  4. Only the field initializer runs: on is false.

Exam tip: No return type means constructor; any return type, even void, means method.

Java Classes, Records, Constructors and Initializers (1Z0-831) — the lesson that teaches this.

Question 8Applying Object-Oriented Principles in Java Programs

Given:record Range(int lo, int hi) { Range { if (lo > hi) { int tmp = lo; lo = hi; hi = tmp; } } } public class Main { public static void main(String[] args) { System.out.println(new Range(9, 2)); } }What is the result?

Choose one.

  • a
    Range[lo=9, hi=2]

    This assumes the fields were stored before the body ran; in a compact constructor they are assigned after it, from the swapped parameters.

  • b
    Compilation fails

    Reassigning the implicit parameters inside a compact constructor is legal; only assigning the fields themselves is forbidden.

  • c
    An IllegalArgumentException is thrown

    Nothing throws: the body swaps the values instead of rejecting them.

  • d
    Range[lo=2, hi=9] Correct

    A compact constructor may reassign its parameters; the fields are assigned from the parameters' final values after the body ends.

The concept

A compact canonical constructor omits the parameter list; its implicit parameters can be validated or reassigned, and the fields are assigned from them automatically at the end.

Why that’s the answer

Range { ... } is a compact canonical constructor. Its parameters lo and hi start as 9 and 2. The body swaps them, so lo is 2 and hi is 9. After the body completes, the compiler-inserted assignments this.lo = lo and this.hi = hi store the swapped values. toString prints Range[lo=2, hi=9].

How to reason it out
  1. Recognise the compact canonical constructor (no parameter list).
  2. Trace the body: 9 > 2, so the parameters are swapped.
  3. Fields are assigned from the parameters after the body ends.
  4. Format with the record toString.

Exam tip: In a compact constructor you normalise the parameters; Java assigns the fields afterwards.

Java Classes, Records, Constructors and Initializers (1Z0-831) — the lesson that teaches this.

Question 9Applying Object-Oriented Principles in Java Programs

Given:record Temp(double celsius) { Temp { if (celsius < -273.15) { throw new IllegalArgumentException("too cold"); } this.celsius = celsius; } } public class Main { public static void main(String[] args) { System.out.println(new Temp(20.0)); } }What is the result?

Choose one.

  • a
    Temp[celsius=20.0]

    The value is valid, but the program never runs: the explicit field assignment in the compact constructor does not compile.

  • b
    Compilation fails Correct

    A compact constructor must not assign the record's fields; javac reports "cannot assign a value to final variable celsius".

  • c
    An IllegalArgumentException is thrown

    20.0 passes the check, and in any case the code never compiles, so the guard never executes.

  • d
    Temp[celsius=0.0]

    There is no path where the field keeps its default; the compiler rejects the explicit assignment before anything runs.

The concept

In a compact canonical constructor the fields are assigned implicitly at the end; assigning this.field yourself is a compile-time error.

Why that’s the answer

Inside a compact constructor, the record fields are final and are assigned automatically after the body. The explicit statement this.celsius = celsius would assign a final field the compiler is about to assign, so javac reports "cannot assign a value to final variable celsius". Writing celsius = ... (the parameter) would be legal; writing this.celsius is not. The validation itself is fine.

How to reason it out
  1. Identify the compact canonical constructor.
  2. Find any statement assigning this.component.
  3. Recall that fields are assigned implicitly, so explicit assignment is an error.
  4. The program does not compile.

Exam tip: Compact constructor: touch the parameters, never this.field.

Java Classes, Records, Constructors and Initializers (1Z0-831) — the lesson that teaches this.

Domain 4: Implementing Exception Handling in Java Applications

8% of the 1Z0-831 exam, so 2 of the 25 questions on this page.

Question 10Implementing Exception Handling in Java Applications

Given:public class Main { static int count = 10; static int next() { try { return count; } finally { count += 5; } } public static void main(String[] args) { int first = next(); System.out.println(first + " " + count); } }What is the result?

Choose one.

  • a
    15 15

    The returned value is captured when the return statement executes; changing the field afterwards in finally does not change it.

  • b
    10 10

    finally runs before next() returns, and it does update the static field to 15.

  • c
    15 10

    It swaps the two values: next() returns 10, not 15, and the field ends at 15, not 10.

  • d
    10 15 Correct

    return count evaluates count (10) before finally runs. finally then adds 5 to the static field, so first is 10 and count is 15.

The concept

A return expression is evaluated before finally runs; finally can change variables but not a value that has already been computed for return.

Why that’s the answer

Executing return count evaluates the expression to 10 and keeps that value as the pending result. finally then executes count += 5, so the static field becomes 15, but the pending result is a copy of the int and stays 10. Because finally completes normally, the method returns 10. main prints first (10) and the field (15). 15 15 assumes finally changes what is returned; it would only do so with its own return statement.

How to reason it out
  1. Evaluate the return expression when it executes: 10.
  2. Run finally: count becomes 15.
  3. finally completes normally, so the pending 10 is returned.
  4. Print first and the updated field.

Exam tip: The return value is fixed when return executes; finally changes variables, not that value.

Java Exception Handling: try/catch/finally, try-with-resources and Custom Exceptions (1Z0-831) — the lesson that teaches this.

Question 11Implementing Exception Handling in Java Applications

Given:class Conn implements AutoCloseable { @Override public void close() { throw new IllegalStateException("close"); } } public class Main { public static void main(String[] args) { try (Conn c = new Conn()) { throw new IllegalArgumentException("query"); } catch (RuntimeException e) { System.out.println(e.getMessage() + " " + e.getSuppressed().length); } } }What is the result?

Choose one.

  • a
    close 0

    The body's exception is the one that propagates. The close() failure does not replace it.

  • b
    query 0

    The close() exception is not lost: try-with-resources attaches it to the body's exception as a suppressed exception.

  • c
    query 1 Correct

    The body throws query, close() then throws close, and try-with-resources adds the close exception to the primary one via addSuppressed, so the array has one element.

  • d
    close 1

    This mixes up the roles: the primary exception is the body's, and the close failure is the suppressed one.

The concept

When both the try-with-resources body and close() throw, the body's exception propagates and the close() exception is suppressed onto it.

Why that’s the answer

The body throws IllegalArgumentException("query"). Before the catch can run, the resource is closed, and close() throws IllegalStateException("close"). Try-with-resources keeps the first exception as primary and calls addSuppressed with the second, so the catch sees query with getSuppressed().length equal to 1. A plain try/finally would lose one of the two; try-with-resources keeps both.

How to reason it out
  1. Identify the first exception: query from the body.
  2. Close the resource: close() throws close.
  3. Attach the close exception as suppressed on the primary.
  4. Print the primary message and the suppressed count.

Exam tip: In try-with-resources the body's exception wins; close() failures ride along in getSuppressed().

Java Exception Handling: try/catch/finally, try-with-resources and Custom Exceptions (1Z0-831) — the lesson that teaches this.

Domain 5: Using Arrays and Collections to Store and Retrieve Data

8% of the 1Z0-831 exam, so 2 of the 25 questions on this page.

Question 12Using Arrays and Collections to Store and Retrieve Data

Given:import java.util.*; public class Main { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("C"); list.add(1, "B"); String old = list.set(2, "D"); System.out.println(old + " " + list + " " + list.indexOf("C")); } }What is the result?

Choose one.

  • a
    D [A, B, D] 2

    set returns the replaced element C, not the new D; and C is no longer in the list, so indexOf returns -1, not its old index 2.

  • b
    C [A, B, D] 2

    After set, the list no longer contains C, so indexOf returns -1 rather than the old position.

  • c
    C [A, B, D] -1 Correct

    add(1, "B") inserts and shifts C to index 2; set(2, "D") replaces C and returns it; C is no longer present, so indexOf is -1.

  • d
    An IndexOutOfBoundsException is thrown

    add(int, E) inserts rather than replaces, so the list has three elements and index 2 is valid for set.

The concept

add(index, e) inserts and shifts later elements right; set(index, e) replaces and returns the old element; indexOf returns -1 when the element is absent.

Why that’s the answer

The list is [A, C] and add(1, "B") inserts B at index 1, giving [A, B, C]. set(2, "D") replaces C with D and returns C, which is stored in old. The list is now [A, B, D], and indexOf("C") returns -1 because C is gone.

How to reason it out
  1. Apply each add, remembering that add(index, e) shifts elements.
  2. Apply set and note the value it returns.
  3. Look up indexOf in the final list.
  4. Assemble the output.

Exam tip: set replaces and returns the old element; add(index, e) inserts.

Java Arrays and Collections: List, Set, Map, Deque and Sequenced Collections (1Z0-831) — the lesson that teaches this.

Question 13Using Arrays and Collections to Store and Retrieve Data

Given:List<String> list = new ArrayList<>(List.of("xa", "xb", "c", "xd"));Which two code fragments, each run on a fresh copy of this list, leave it as [c]? (Select TWO.)

Choose TWO.

  • a
    for (String s : list) if (s.startsWith("x")) list.remove(s);

    Removing through the list inside an enhanced for loop changes it behind the iterator's back; the next iteration throws ConcurrentModificationException.

  • b
    list.removeIf(s -> s.startsWith("x")); Correct

    removeIf applies the predicate to every element and removes the matches safely, leaving [c].

  • c
    for (int i = 0; i < list.size(); i++) if (list.get(i).startsWith("x")) list.remove(i);

    After removing index 0 the elements shift left and i moves on to 1, so xb is skipped and the list ends as [xb, c].

  • d
    for (var it = list.iterator(); it.hasNext();) if (it.next().startsWith("x")) it.remove(); Correct

    Iterator.remove deletes the element just returned by next() and keeps the iterator consistent, so every match is removed.

  • e
    list.removeAll(List.of("x"));

    removeAll removes elements equal to an element of the argument; no element equals the string x, so nothing is removed.

The concept

To remove elements while traversing a list, use removeIf or the iterator's own remove. Removing through the list during an enhanced for loop fails fast, and a forward index loop skips the element that shifts into the removed slot.

Why that’s the answer

removeIf and Iterator.remove both remove xa, xb and xd and leave [c]. The enhanced for loop calls list.remove(s) while its hidden iterator is active, so the iterator's next call throws ConcurrentModificationException. The forward index loop removes xa at index 0, xb shifts into index 0, and the loop continues at index 1, so xb survives. removeAll matches by equals, not by prefix, so it removes nothing.

How to reason it out
  1. Ask which fragments use the iterator or a bulk predicate.
  2. Check enhanced for loops for structural changes made through the list.
  3. For index loops, trace what shifts after a removal.
  4. Check whether a bulk method matches by equals or by predicate.

Exam tip: Remove while iterating with removeIf or Iterator.remove, never with list.remove inside for-each.

Java Arrays and Collections: List, Set, Map, Deque and Sequenced Collections (1Z0-831) — the lesson that teaches this.

Domain 6: Processing Data Using Streams and Lambda Expressions

9% of the 1Z0-831 exam, so 2 of the 25 questions on this page.

Question 14Processing Data Using Streams and Lambda Expressions

Given:import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.iterate(1, n -> n < 30, n -> n * 3) .skip(1) .forEach(n -> System.out.print(n + " ")); } }What is the result?

Choose one.

  • a
    1 3 9 27

    skip(1) discards the first element produced by iterate, which is the seed 1.

  • b
    3 9

    27 is less than 30, so the predicate lets it through; only 81 fails the test.

  • c
    3 9 27 Correct

    iterate produces 1, 3, 9, 27 and stops before 81 because the hasNext predicate n < 30 fails; skip(1) drops the seed.

  • d
    3 9 27 81

    The three-argument iterate tests each value before emitting it, so 81 is never produced.

The concept

Stream.iterate(seed, hasNext, next) emits the seed first and stops at the first value that fails hasNext, like a for loop.

Why that’s the answer

The three-argument iterate behaves like for (n = 1; n < 30; n = n * 3). It emits 1, 3, 9 and 27, then computes 81, which fails n < 30, and the stream ends. skip(1) discards the first emitted element, the seed 1, leaving 3, 9 and 27. Answers containing 81 treat the predicate as checked after emitting; the answer ending at 9 applies the bound too early.

How to reason it out
  1. Expand iterate as a loop: 1, 3, 9, 27; 81 fails n < 30.
  2. Remember that the seed itself is the first element.
  3. Apply skip(1): drop 1.
  4. Print 3 9 27 with trailing spaces.

Exam tip: Three-argument iterate is a for loop in stream form: seed first, predicate checked before every element.

Java Streams, Lambdas and Primitive Streams (1Z0-831) — the lesson that teaches this.

Question 15Processing Data Using Streams and Lambda Expressions

Given:import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.generate(() -> "x") .filter(s -> s.length() > 1) .limit(2) .forEach(System.out::print); System.out.print("end"); } }What is the result?

Choose one.

  • a
    It prints end after the filter rejects every element

    This assumes the stream ends when no element passes the filter. Stream.generate is infinite, so limit(2) keeps waiting for two passing elements.

  • b
    It prints nothing and the program never terminates Correct

    generate supplies x forever, filter rejects every one, and limit(2) needs two elements to pass before it can stop the stream, so forEach never returns.

  • c
    It prints xxend because limit(2) stops the stream

    Every generated element is "x", whose length is 1, so none of them passes the filter and nothing reaches limit or forEach.

  • d
    It throws an OutOfMemoryError as elements pile up

    The stream does not store rejected elements; each x is generated, rejected and discarded, so memory use stays flat while the loop runs.

The concept

Stream.generate and two-argument iterate produce infinite streams; limit counts only the elements that reach it, so a filter upstream that never matches makes the pipeline run forever.

Why that’s the answer

Stream.generate(() -> "x") never runs out. limit(2) would end the stream after two elements, but it counts elements that arrive at it, not elements generated. Because "x".length() > 1 is false, filter discards every element, limit never sees one, and forEach keeps pulling forever. Nothing is printed and end is never reached. No elements are buffered, so there is no OutOfMemoryError; the program simply runs until it is killed.

How to reason it out
  1. Recognise generate as an infinite source.
  2. Evaluate the filter for x: length 1, rejected every time.
  3. Note that limit counts only elements that pass the filter.
  4. Conclude that the terminal operation never completes.

Exam tip: limit bounds what reaches it, not what the source produces: put limit before a filter that might never match.

Java Streams, Lambdas and Primitive Streams (1Z0-831) — the lesson that teaches this.

Domain 7: Packaging and Deploying Java Code

9% of the 1Z0-831 exam, so 2 of the 25 questions on this page.

Question 16Packaging and Deploying Java Code

Given:// metrics/module-info.java module metrics { exports metrics; } // metrics/metrics/Counter.java package metrics; public class Counter { public static void inc() { System.out.println("inc"); } } // app/module-info.java module app { requires static metrics; } // app/app/Main.java package app; public class Main { public static void main(String[] args) { System.out.println("start"); metrics.Counter.inc(); } }Both modules are compiled together, and both compiled modules are in directory out. The program is launched with java -p out -m app/app.Main. What is the result?

Choose one.

  • a
    It prints start, then inc.

    metrics is on the module path, but nothing causes it to be resolved: app's only dependency on it is requires static, and metrics is not a root module.

  • b
    It throws NoClassDefFoundError before printing anything.

    Classes are loaded lazily. Counter is needed only when the second statement runs, so start is printed first.

  • c
    The JVM fails to start because metrics is not resolved.

    An unresolved static dependency is legal; that is the purpose of requires static. The boot layer is created with app alone, and main starts normally.

  • d
    It prints start, then throws NoClassDefFoundError. Correct

    requires static is mandatory at compile time and optional at run time. Resolution does not pull in metrics, so it is not in the boot layer even though it is in out; the call to Counter fails with NoClassDefFoundError after start is printed.

The concept

requires static makes a dependency mandatory at compile time but optional at run time, and the module is resolved only if something else requires it or it is added (for example with --add-modules).

Why that’s the answer

Being on the module path is not the same as being resolved. The module system resolves the root module (app) and its non-static requires. metrics is reached only through requires static, so it stays out of the module graph. main prints start, then the reference to Counter cannot be loaded and NoClassDefFoundError is thrown. Launching with --add-modules metrics would print inc.

How to reason it out
  1. Note the directive: requires static metrics.
  2. Recall that static dependencies are not followed during resolution.
  3. metrics is therefore not in the boot layer despite being in out.
  4. Lazy loading means start prints before the failed Counter lookup.

Exam tip: On the module path is not in the graph: a requires static module must be resolved by something else, such as --add-modules.

Java Modules, Services and Module Imports (1Z0-831) — the lesson that teaches this.

Question 17Packaging and Deploying Java Code

Module app marks its classes with annotations from module annotations. The annotations are read only by the compiler and build tools, and the team wants app to start when the annotations module is not deployed. Which directive should app's module declaration contain?

Choose one.

  • a
    requires transitive annotations;

    transitive forwards readability to app's own readers and keeps the dependency mandatory at run time; the launch fails if annotations is missing.

  • b
    requires annotations;

    A plain requires is mandatory at both compile time and run time, so launching without the annotations module fails during resolution.

  • c
    uses annotations.Audited;

    uses declares that the module consumes a service through ServiceLoader. It does not make the annotations package readable to the compiler.

  • d
    requires static annotations; Correct

    A static dependency must be present when the module is compiled but is not required when the application runs, which fits annotations used only by the compiler and tools.

The concept

requires static declares a dependency that is mandatory at compile time and optional at run time.

Why that’s the answer

app needs to read annotations so javac can resolve the annotation types, but the deployed application must not depend on it. requires static does exactly that. A plain or transitive requires makes resolution fail when the module is absent, and uses has nothing to do with readability.

How to reason it out
  1. Separate the two phases: needed to compile, not needed to run.
  2. Match that to requires static.
  3. Reject directives that make the module mandatory at run time.

Exam tip: Compile-time only dependency: requires static.

Java Modules, Services and Module Imports (1Z0-831) — the lesson that teaches this.

Domain 8: Implementing Multithreading for Concurrent Code Execution

11% of the 1Z0-831 exam, so 3 of the 25 questions on this page.

Question 18Implementing Multithreading for Concurrent Code Execution

Given:import java.util.concurrent.*; public class Main { public static void main(String[] args) throws Exception { ExecutorService es = Executors.newSingleThreadExecutor(); Future<?> f = es.submit(() -> System.out.print("1 ")); f.get(); es.shutdown(); System.out.print(es.isShutdown() + " "); es.execute(() -> System.out.print("2 ")); System.out.print("end"); } }What is the result?

Choose one.

  • a
    1 true 2 end

    After shutdown() the executor accepts no new tasks, so the second task is never run.

  • b
    1 true, then a RejectedExecutionException is thrown Correct

    shutdown() stops new submissions; execute() then throws RejectedExecutionException from main.

  • c
    1 true end

    A task submitted after shutdown() is not silently dropped. The default rejection policy throws, so end is never printed.

  • d
    1 true, then an IllegalStateException is thrown

    The executor reports a refused task with RejectedExecutionException, an unchecked exception from java.util.concurrent, not IllegalStateException.

The concept

shutdown() starts an orderly shutdown: queued tasks still run, but new submissions are rejected.

Why that’s the answer

f.get() waits for the first task, so 1 is printed first. After shutdown(), isShutdown() is true. Any later execute() or submit() call is rejected, and the default policy throws RejectedExecutionException to the caller, main, which ends the program before end is printed.

How to reason it out
  1. The first task finishes before shutdown: 1 is printed.
  2. isShutdown() is true right after shutdown().
  3. Recall that a shut-down executor rejects new tasks.
  4. The rejection throws RejectedExecutionException in main.

Exam tip: After shutdown(), submitting a task throws RejectedExecutionException.

Java Threads, Virtual Threads, Executors and Scoped Values (1Z0-831) — the lesson that teaches this.

Question 19Implementing Multithreading for Concurrent Code Execution

Given:import java.util.*; import java.util.concurrent.*; public class Main { public static void main(String[] args) throws Exception { var es = Executors.newSingleThreadExecutor(); var started = new CountDownLatch(1); es.submit(() -> { started.countDown(); try { Thread.sleep(5_000); System.out.print("slept "); } catch (InterruptedException e) { System.out.print("interrupted "); } }); es.submit(() -> System.out.print("second ")); es.submit(() -> System.out.print("third ")); started.await(); List<Runnable> pending = es.shutdownNow(); es.awaitTermination(1, TimeUnit.SECONDS); System.out.print(pending.size()); } }What is the result?

Choose one.

  • a
    slept second third 0

    That is shutdown() behaviour. shutdownNow() interrupts the sleeping task and removes the queued tasks instead of running them.

  • b
    interrupted second third 0

    shutdownNow() drains the queue: the second and third tasks are returned in the list and never run, so the list is not empty.

  • c
    interrupted 2 Correct

    shutdownNow() interrupts the running task, which catches InterruptedException, and returns the two queued tasks that never started.

  • d
    interrupted 3

    The first task had already started, as the latch guarantees, so it is not in the returned list; only the two waiting tasks are.

The concept

shutdownNow() interrupts actively running tasks and returns the tasks that were still waiting in the queue.

Why that’s the answer

The latch ensures the first task is running before shutdownNow() is called. shutdownNow() interrupts the worker, so Thread.sleep throws InterruptedException and interrupted is printed. The second and third tasks were still queued; shutdownNow() removes them and returns them as a List<Runnable> of size 2. awaitTermination lets the first task finish its catch block before main prints the size.

How to reason it out
  1. The latch proves task one has started.
  2. shutdownNow() interrupts the running worker: interrupted is printed.
  3. Queued tasks are drained and returned, not executed.
  4. Two tasks were waiting, so the list size is 2.

Exam tip: shutdown() lets queued tasks finish; shutdownNow() interrupts running ones and hands back the queue.

Java Threads, Virtual Threads, Executors and Scoped Values (1Z0-831) — the lesson that teaches this.

Question 20Implementing Multithreading for Concurrent Code Execution

Given:import java.util.concurrent.*; public class Main { public static void main(String[] args) { try (ExecutorService es = Executors.newSingleThreadExecutor()) { for (int i = 1; i <= 3; i++) { int n = i; es.execute(() -> System.out.print(n)); } } System.out.print("!"); } }What is the result?

Choose one.

  • a
    !123

    ExecutorService.close(), called at the end of the try block, waits for submitted tasks to finish, so ! cannot print first.

  • b
    The digits print in an unpredictable order, then !

    With one worker thread, tasks run sequentially in the order they were submitted, so the order is fixed.

  • c
    123! Correct

    A single-thread executor runs tasks one at a time in submission order, and close() waits for them before ! is printed.

  • d
    Compilation fails

    The lambda captures n, which is effectively final in each iteration, and ExecutorService is AutoCloseable, so try-with-resources compiles.

The concept

newSingleThreadExecutor runs tasks sequentially in submission order, and ExecutorService.close() waits for submitted tasks.

Why that’s the answer

Two facts decide the output. A single-thread executor processes its queue one task at a time, in order, so the digits are 1, 2, 3. ExecutorService has been AutoCloseable since Java 19; close() performs an orderly shutdown and waits for the tasks to complete. Only then does main print !.

How to reason it out
  1. Check compilation: n is effectively final and the executor is AutoCloseable.
  2. One worker thread means tasks run in submission order.
  3. close() at the end of try waits for those tasks.
  4. So ! prints after 123.

Exam tip: try-with-resources on an ExecutorService waits for its tasks; a single-thread executor keeps them in order.

Java Threads, Virtual Threads, Executors and Scoped Values (1Z0-831) — the lesson that teaches this.

Domain 9: Performing Input and Output Operations Using the Java I/O API

11% of the 1Z0-831 exam, so 3 of the 25 questions on this page.

Question 21Performing Input and Output Operations Using the Java I/O API

Given:import java.io.*; public class Main { public static void main(String[] args) throws IOException { var pw = new PrintWriter(new FileWriter("p.txt"), true); pw.print("a"); pw.println("b"); pw.print("c"); try (var r = new BufferedReader( new FileReader("p.txt"))) { String s; while ((s = r.readLine()) != null) { System.out.print("<" + s + ">"); } } pw.close(); } }What is the result?

Choose one.

  • a
    <abc>

    Autoflush is triggered by println, printf and format, not by print, so c is still in the writer when the file is read.

  • b
    <a>

    print("a") is not flushed by itself, but println("b") flushes the whole buffer, including the a written before it.

  • c
    <ab> Correct

    With autoflush on, println flushes everything written so far, so a and b reach the file. The later print("c") does not flush and stays buffered.

  • d
    <ab><c>

    c is still in the PrintWriter's buffer when the file is read. It reaches the file only at pw.close(), after the loop has finished.

The concept

PrintWriter autoflush (the true argument) flushes on println, printf and format only.

Why that’s the answer

PrintWriter(Writer out, boolean autoFlush) flushes its output when println, printf or format is called. print("a") stays buffered, println("b") flushes "ab" and a line separator to the file, and print("c") is buffered again. The reader therefore sees one line, ab, and then end of file. c is written only when pw.close() runs at the end. Choosing <abc> means treating autoflush as flush-after-every-call.

How to reason it out
  1. autoFlush is true, but only println, printf and format trigger it.
  2. print("a") is buffered; println("b") flushes a, b and a newline.
  3. print("c") is buffered and not yet in the file.
  4. The reader finds one line: ab.

Exam tip: PrintWriter autoflush is not per-call: print() never flushes; println/printf/format do.

Java I/O Streams: Console and File Readers, Writers and Streams (1Z0-831) — the lesson that teaches this.

Question 22Performing Input and Output Operations Using the Java I/O API

Given:import java.io.*; public class Main { public static void main(String[] args) { PrintWriter pw = new PrintWriter(System.out); pw.println("ready"); System.out.println("done"); } }What is the result?

Choose one.

  • a
    ready and done are printed on separate lines

    pw was created without autoflush, so println("ready") stays in the PrintWriter's buffer. The program ends without flushing it.

  • b
    Compilation fails because println can throw IOException

    PrintWriter methods never throw IOException; they set an internal error flag that checkError() reports, so main needs no throws clause.

  • c
    Only done is printed Correct

    PrintWriter(OutputStream) buffers its output and has autoflush off. ready is never flushed or closed, so only System.out's own println appears.

  • d
    done and ready are printed on separate lines

    Nothing flushes pw, even at exit: the JVM does not flush PrintWriter buffers on shutdown.

The concept

A PrintWriter around System.out buffers output and must be flushed when autoflush is off.

Why that’s the answer

new PrintWriter(OutputStream) wraps the stream in a BufferedWriter and sets autoFlush to false. println("ready") goes into that buffer. Nothing calls pw.flush() or pw.close(), and the JVM does not flush user-level buffers on exit, so ready is lost. System.out is a PrintStream that the JVM sets up to flush on println, so done appears. The code compiles because PrintWriter methods do not throw IOException.

How to reason it out
  1. PrintWriter(OutputStream) has autoflush off.
  2. println("ready") is held in its buffer.
  3. Nothing flushes or closes pw before main ends.
  4. System.out.println("done") is written normally.

Exam tip: Wrap System.out in a PrintWriter with autoflush (new PrintWriter(System.out, true)) or flush it yourself.

Java I/O Streams: Console and File Readers, Writers and Streams (1Z0-831) — the lesson that teaches this.

Question 23Performing Input and Output Operations Using the Java I/O API

Given:public class Main { public static void main(String[] args) { byte[] b = { 72, 105 }; ________ System.out.println(); } }Which two statements, used in place of the blank, let the program compile? (Select TWO.)

Choose TWO.

  • a
    System.out.write(b);

    PrintStream.write(byte[]) is declared throws IOException (an override added in Java 14 to match OutputStream), so this line needs a try/catch or throws.

  • b
    System.in.read(b, 0, 2);

    System.in is an InputStream, and InputStream.read(byte[], int, int) declares IOException, a checked exception that main does not handle.

  • c
    System.out.write(65); Correct

    PrintStream overrides write(int) without a throws clause; errors only set the flag that checkError() reports.

  • d
    System.in.close();

    InputStream.close() declares IOException, so calling it from a method with no throws clause does not compile.

  • e
    System.out.writeBytes(b); Correct

    writeBytes(byte[]) was added to PrintStream in Java 14 as a write(byte[]) that declares no checked exception.

The concept

PrintStream methods swallow IOException, except write(byte[]), which is declared throws IOException.

Why that’s the answer

PrintStream (the type of System.out) is designed not to throw IOException: print, println, printf, write(int) and write(byte[], int, int) set an internal error flag instead. The exception is write(byte[]): the Java 14 override is declared throws IOException to match OutputStream, which is why writeBytes(byte[]) was added alongside it. System.in is a plain InputStream, so its read methods and close() declare the checked IOException. Running each option: write(65) prints A, writeBytes(b) prints Hi, the other three fail with "unreported exception IOException; must be caught or declared to be thrown".

How to reason it out
  1. System.out is a PrintStream; System.in is an InputStream.
  2. PrintStream's own methods report errors via checkError(), not exceptions.
  3. write(byte[]) is declared throws IOException, like OutputStream.write(byte[]).
  4. InputStream read methods and close() declare IOException too.

Exam tip: System.out methods don't throw checked exceptions, except write(byte[]); use writeBytes(byte[]) instead.

Java I/O Streams: Console and File Readers, Writers and Streams (1Z0-831) — the lesson that teaches this.

Domain 10: Developing Applications with Localization Support

7% of the 1Z0-831 exam, so 2 of the 25 questions on this page.

Question 24Developing Applications with Localization Support

An application packages Shop.properties, Shop_en.properties, Shop_en_US.properties, Shop_fr.properties, Shop_fr_FR.properties and Shop_fr_CA.properties. The default locale is en_US, and the code calls ResourceBundle.getBundle("Shop", Locale.of("fr", "CA")). Apart from Shop_fr_CA.properties, which two files can supply values to the returned bundle? (Select TWO.)

Choose TWO.

  • a
    Shop_en_US.properties

    The default locale is used only when nothing matches the requested locale. Here Shop_fr_CA matches, so no en_US file joins the chain.

  • b
    Shop_en.properties

    Shop_en would be a fallback for the default locale, which is not consulted because a bundle for fr_CA was found.

  • c
    Shop_fr.properties Correct

    It is the first parent of Shop_fr_CA: dropping the country from fr_CA gives fr.

  • d
    Shop.properties Correct

    The base bundle ends every parent chain, so keys missing from the French files are read from it.

  • e
    Shop_fr_FR.properties

    A parent is formed by dropping parts of the requested locale. fr_CA never becomes fr_FR, so this sibling file is never read.

The concept

A loaded bundle's parent chain is built by removing parts of its own locale, ending at the base bundle.

Why that’s the answer

Shop_fr_CA exists, so it is the bundle returned. Its parents are Shop_fr and then Shop, and getString or keySet sees keys from all three. The default locale's files (Shop_en_US and Shop_en) are only tried when no candidate for fr_CA exists, and Shop_fr_FR is a sibling for another country, not an ancestor.

How to reason it out
  1. Find the most specific existing bundle for fr_CA: Shop_fr_CA.
  2. Drop the country to get the parent Shop_fr.
  3. Drop the language to reach the base bundle Shop.
  4. Ignore the default locale: it is a fallback only when the requested locale finds nothing.

Exam tip: Parents come from trimming the found bundle's locale; siblings and the default locale stay out.

Java Localization: Locales, Resource Bundles and Formatting Numbers, Dates and Messages (1Z0-831) — the lesson that teaches this.

Question 25Developing Applications with Localization Support

Labels_en.properties and the compiled Labels_en class are both on the classpath. Labels_en.properties contains:title=Catalogue (file)Labels_en.java:import java.util.ListResourceBundle; public class Labels_en extends ListResourceBundle { @Override protected Object[][] getContents() { return new Object[][] { {"title", "Catalogue (class)"}, {"pageSize", 25} }; } }Main.java:import java.util.*; public class Main { public static void main(String[] args) { ResourceBundle rb = ResourceBundle.getBundle("Labels", Locale.ENGLISH); System.out.println(rb.getString("title") + " / " + rb.getObject("pageSize")); } }What is the result?

Choose one.

  • a
    Catalogue (file) / 25

    When a class and a property file share a bundle name, getBundle loads the class, so the property file's title is never read.

  • b
    Catalogue (file), then a MissingResourceException

    This assumes the property file is loaded. The class is preferred, and it defines pageSize, so no exception occurs.

  • c
    Catalogue (class) / 25 Correct

    The class-based bundle Labels_en wins over Labels_en.properties, and getObject returns the Integer 25 stored in it.

  • d
    Catalogue (class), then a ClassCastException

    getObject returns any value type without casting; a ClassCastException arises only from getString on a non-String value.

The concept

For each candidate bundle name, getBundle looks for a class first and a .properties file second; ListResourceBundle values can be any object.

Why that’s the answer

The candidate name Labels_en matches both a class and a property file. The default ResourceBundle.Control tries the class format before the properties format, so the ListResourceBundle subclass is loaded and the file is ignored. title is "Catalogue (class)" and getObject("pageSize") returns the Integer 25, printed as 25.

How to reason it out
  1. Both candidates have the name Labels_en, so precedence decides.
  2. The default lookup tries java.class before java.properties.
  3. The ListResourceBundle supplies title as Catalogue (class).
  4. getObject returns the stored Integer without a cast, printed as 25.

Exam tip: Same bundle name as a class and a file? The class wins.

Java Localization: Locales, Resource Bundles and Formatting Numbers, Dates and Messages (1Z0-831) — the lesson that teaches this.

Keep going with 1Z0-831

1Z0-831 practice by domain

1Z0-831 sample questions: your questions

No. Real exam questions are confidential. These are original questions written to the skills in the official 1Z0-831 exam guide, so they test the same knowledge in the same style without being copies of the exam.

Source

The domains and weights on this page come from the official 1Z0-831 exam guide.