Java I/O and NIO.2 practice questions

From OCP Java SE 17 (1Z0-829) · 18 questions on this topic

Java I/O and NIO.2 practice questions from OCP Java SE 17 (1Z0-829). This pack has 18 questions tagged Java I/O and NIO.2, drawn from its timed mock exams. 8 of them are worked through in full below — the question, every option, why each is right or wrong, and the explanation.

Worked examples for Java I/O and NIO.2

  1. Question 1

    A BufferedReader wraps an in-memory character source. The loop drains it, then readLine() is called once more. What is the output? ```java import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new StringReader("a\nb\n")); int count = 0; while (br.readLine() != null) { count++; } System.out.println(count + " " + br.readLine()); } } ```

    1. A. 2 nullCorrect answer

      Correct — 'a\nb\n' has exactly two lines, so the loop counts 2, and the extra readLine() after end of stream returns null, which concatenation renders as the text 'null'.

    2. B. 3 null

      Counts an empty third line; the trailing \n terminates the line 'b' rather than starting a new empty line, so there are only two lines.

    3. C. Throws IOException

      Assumes reading past end throws; readLine() returns null at end of stream instead of throwing.

    4. D. Throws NullPointerException

      Assumes concatenating the null result throws; string concatenation renders a null reference as the text 'null' rather than throwing.

    Explanation

    BufferedReader.readLine() strips the line terminator and returns null — it does not throw — once the end of the stream is reached, so the classic while ((line = readLine()) != null) idiom is what terminates the loop. The source "a\nb\n" contains exactly two lines: the trailing \n terminates the line "b" rather than starting an empty third line, which is why 'B' (3) is wrong. The extra readLine() after EOF simply returns null again (the reader was never closed), and string concatenation renders a null reference as the text "null" instead of throwing NullPointerException.

  2. Question 2

    What does this print? ```java import java.nio.file.*; public class Main { public static void main(String[] args) { Path p = Path.of("/usr/local/../bin/./java").normalize(); System.out.println(p.getNameCount() + " " + p.getFileName()); } } ```

    1. A. 6 java

      6 counts the UN-normalized path's elements; . and .. are name elements too, which is exactly why the normalize call matters before counting.

    2. B. 3 javaCorrect answer

      normalize drops the . and cancels local/.., leaving /usr/bin/java — a root plus three name elements, so getNameCount() is 3 and getFileName() is the last element, java.

    3. C. 3 /usr/bin/java

      getFileName() returns only the last element as a one-element Path, never the whole path.

    4. D. 2 bin

      bin is getName(1), the second-to-last element; neither the count nor the file name points at it.

    Explanation

    After normalization the path is a root plus its surviving name elements, and the root is never counted, so getNameCount reflects only the segments below it. getFileName returns the single element farthest from the root. The usual traps are counting the pre-normalization ./.. segments or including the root in the count.

  3. Question 3

    Three characters are written to an in-memory byte stream, which is then queried for its size and its contents. What is printed? ```java import java.io.ByteArrayOutputStream; public class Main { public static void main(String[] args) { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write('O'); out.write('C'); out.write('P'); System.out.println(out.size() + ":" + out.toString()); } } ```

    1. A. 32:OCP

      Confuses the byte count with the internal buffer's initial capacity (32); size() returns the number of bytes actually written, which is 3.

    2. B. Compilation fails

      Assumes a checked exception must be handled; this stream overrides write(int) without declaring IOException, so nothing needs a try/catch and it compiles.

    3. C. 3:OCPCorrect answer

      Correct — size() reports the three bytes written, and toString() decodes those bytes with the default charset to give "OCP".

    4. D. Throws IOException

      Assumes write throws a checked exception; the in-memory stream's write(int) does not declare or throw IOException.

    Explanation

    The stream's size() reports how many bytes have actually been written, not the capacity of its internal buffer. Each character argument is stored as a byte, and toString() decodes the accumulated bytes with the default charset. Because this in-memory stream overrides its write method without declaring a checked exception, no exception handling is required.

  4. Question 4

    What does Path.of("/a/./b/../c").normalize() return? ```java import java.nio.file.*; public class Main { public static void main(String[] args) { System.out.println(Path.of("/a/./b/../c").normalize().toString().replace('\\', '/')); } } ```

    1. A. /a/./b/../c

      normalize always eliminates removable . and .. elements; the input would come back unchanged only if there were none.

    2. B. /a/cCorrect answer

      normalize is purely syntactic: the . element drops out and b/.. cancels, leaving /a/c with no file-system access.

    3. C. /a/b/c

      /a/b/c keeps b while dropping the .. that cancels it, but a .. always consumes the preceding name element.

    4. D. /c

      /c would require BOTH a and b to be cancelled, but only one .. appears.

    Explanation

    normalize is a purely syntactic operation that never touches the disk: it deletes every . element, then cancels each preceding name element against a following .., working left to right. Only leading .. elements of a relative path survive the process. The input paths need not exist for it to work.

  5. Question 5

    What does this print? ```java import java.nio.file.*; public class Main { public static void main(String[] args) { Path a = Path.of("projects/app"); Path b = Path.of("projects/app/src/Main.java"); System.out.println(a.relativize(b).toString().replace('\\', '/')); } } ```

    1. A. ../src/Main.java

      A leading .. appears only when the source keeps elements NOT shared with the target; here the source is a pure prefix of the target.

    2. B. src/Main.javaCorrect answer

      Both paths are relative and the target begins with every element of the source, so relativize returns just the leftover suffix: src/Main.java.

    3. C. projects/app/src/Main.java

      The full target path is resolve-style thinking; relativize answers 'how do I get from the source to the target', not 'what is the target'.

    4. D. Throws IllegalArgumentException

      IllegalArgumentException requires exactly one of the two paths to have a root component; both here are relative, so the operation is legal.

    Explanation

    relativize is legal for two relative paths or two absolute paths — only a mixed rooted/unrooted pair throws. When the source is a prefix of the target, the answer is simply the target's remaining suffix; the .. steps appear only once the two paths diverge. A separator replace only masks the platform separator and leaves the element sequence unchanged.

  6. Question 6

    What does Path.of("/a/b").resolve("/c/d") return? ```java import java.nio.file.*; public class Main { public static void main(String[] args) { System.out.println(Path.of("/a/b").resolve("/c/d").toString().replace('\\', '/')); } } ```

    1. A. /a/b//c/d

      resolve never produces doubled separators; joining only happens when the argument is relative, and then exactly one separator is inserted.

    2. B. /c/dCorrect answer

      The argument is absolute (it has a root), so resolve returns that argument itself and discards the receiver /a/b entirely — its documented behavior. Output: /c/d.

    3. C. /a/b/c/d

      /a/b/c/d is the result of resolving the RELATIVE path c/d; the leading slash on the argument changes the behavior completely.

    4. D. Throws IllegalArgumentException

      resolve has no failure mode for absolute arguments; returning the argument unchanged is its documented behavior (relativize is the method that throws on mismatched inputs).

    Explanation

    Path.resolve first checks whether its argument is absolute. When the argument has a root component, resolve ignores the receiver entirely and returns that absolute argument unchanged; joining (with exactly one separator) happens only for a relative argument. So resolving an absolute path against any base yields the absolute path itself. Before tracing any resolve stem, inspect the argument's first character.

  7. Question 7

    Which statement about object serialization is correct?

    1. A. A class needs no special marker to be serializable

      Only classes implementing the java.io.Serializable marker interface can be serialized; writing anything else throws NotSerializableException.

    2. B. static fields are serialized as part of each instance

      static fields belong to the class, not to any instance, so they are never part of an object's serialized form.

    3. C. All fields are serialized, including transient ones

      Excluding transient fields is the entire point of the keyword, so they are not written.

    4. D. Fields marked transient are skipped during serialization and restored to defaults on deserializationCorrect answer

      Serialization writes an object's non-transient, non-static instance fields; transient fields are skipped, and on deserialization they are restored to their type's default value (0, false, null).

    Explanation

    Serialization persists an object's non-static, non-transient instance fields; static state belongs to the class and transient fields are deliberately excluded. On deserialization the class's constructors and field initializers do not run, so transient fields come back as their type defaults (0, false, null) rather than any initializer value. Only the no-arg constructor of the first non-serializable superclass executes.

  8. Question 8

    An object is serialized to a byte array and immediately read back. Item is Serializable; its superclass Base is not. What is printed? ```java import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Main { static class Base { int base = 1; Base() { base = 5; } } static class Item extends Base implements Serializable { private static final long serialVersionUID = 1L; int id = 2; transient String name = "x"; Item() { id = 9; name = "set"; } } public static void main(String[] args) throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); try (ObjectOutputStream out = new ObjectOutputStream(bytes)) { out.writeObject(new Item()); } Item back; try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { back = (Item) in.readObject(); } System.out.println(back.base + " " + back.id + " " + back.name); } } ```

    1. A. 0 9 null

      Assumes a non-serializable superclass contributes nothing, leaving base at the JVM default 0; Base's no-arg constructor actually runs during deserialization and sets base to 5.

    2. B. 5 9 nullCorrect answer

      Deserialization runs Base's no-arg constructor (base=5) but skips Item's constructor, restoring id=9 from the stream; the transient name was never written so it comes back null.

    3. C. 5 2 null

      Assumes the serializable class's field initializers re-run (id=2); Item's state is restored from the stream (id=9), not re-initialized.

    4. D. 5 9 set

      Assumes transient is a hint ignored for in-memory streams; transient is enforced by the serialization mechanism itself, so name is skipped on write and comes back null, not "set".

    Explanation

    Trace: deserialization does not call the constructor of the class being deserialized, but it *does* call the accessible no-argument constructor of the first non-serializable superclass. `Base` is that superclass, so `Base()` runs and sets `base` to 5 — the field initializer `base = 1` runs first and is then overwritten by the constructor body, exactly as in a normal `new`. `Item`'s own constructor and field initializers are skipped entirely; its non-transient state is instead restored from the stream. `id` was 9 when the object was written, so 9 comes back. `name` is `transient`, so it was never written; the restored object's `name` field keeps its default value, `null`, because nothing re-runs the `= "x"` initializer either. The output is `5 9 null`. Why the others are wrong: `0 9 null` encodes the belief that a non-serializable superclass contributes nothing at all, leaving its fields at their JVM defaults. The state of a non-serializable superclass is not serialized, but it is *re-created* by running that superclass's no-arg constructor — which is why the constructor must exist and be accessible, or deserialization fails with `InvalidClassException`. `5 9 set` assumes `transient` merely hints at something or is ignored for in-memory streams. `transient` is enforced by the serialization mechanism itself, not by the destination: the field is skipped on write and left at its default on read, whether the bytes go to disk or to a byte array. `5 2 null` assumes the serializable class's field initializers re-run during deserialization. They do not — that is the whole point of restoring state from the stream. Only the non-serializable superclass's initialization runs. Exam tip: the rule is "initialization stops at the serializable boundary". Everything at or below the first `Serializable` class in the hierarchy is restored from bytes; everything above it is constructed. The reverse trap: give `Base` only a constructor that takes arguments and the identical code compiles but throws `InvalidClassException: no valid constructor` at `readObject`.

Practise all 18 Java I/O and NIO.2 questions

OCP Java SE 17 has the full set, inside timed mock exams that mirror real exam conditions — every question with a worked explanation.

Open OCP Java SE 17

Other topics in this pack