Advanced Class Design & Design Patterns practice questions

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

Advanced Class Design & Design Patterns practice questions from OCP Java SE 17 (1Z0-829). This pack has 27 questions tagged Advanced Class Design & Design Patterns, 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 Advanced Class Design & Design Patterns

  1. Question 1

    Main declares one static nested class and one inner class, both reading private members of the enclosing class. What is the result? ```java public class Main { private static int secret = 42; private int instanceVal = 7; static class Vault { int peek() { return secret; } } class Room { int peek() { return instanceVal + secret; } } public static void main(String[] args) { Main m = new Main(); Room r = m.new Room(); System.out.println(new Vault().peek() + " " + r.peek()); } } ```

    1. A. 42 7

      Reads only instanceVal for the inner class and omits secret; Room returns instanceVal + secret = 7 + 42 = 49, not 7.

    2. B. Compilation fails

      Assumes private members are hidden from nested types; a nested class is a member of its enclosing class and may read its private fields, so it compiles.

    3. C. 42 49Correct answer

      Correct — the static nested Vault reads the static secret (42), and the inner Room reads both instanceVal (7) and secret (42) for 49, since nested classes may access the enclosing class's private members (JLS 17 §8.5).

    4. D. 49 42

      Swaps the two results; the static nested class prints 42 and the inner-class instance prints 49, so the order is 42 then 49.

    Explanation

    A nested class is a member of its enclosing class, so it may read the enclosing class's private members: Vault (static nested, no enclosing instance needed) sees the static field secret = 42, and Room (inner) sees both instanceVal = 7 and secret, giving 49. Compilation fails is the trap for thinking private members are hidden from nested types; the qualified creation expression m.new Room() is the correct — and required — way to build an inner-class instance from a static context, since an inner class has no existence without an enclosing instance.

  2. Question 2

    Interface Tool mixes a static method, a private method and a default method. Hammer implements Tool and calls both. What is the result? ```java public class Main { interface Tool { static String brand() { return "Acme"; } private String prefix() { return ">> "; } default String label() { return prefix() + brand(); } } static class Hammer implements Tool {} public static void main(String[] args) { Hammer h = new Hammer(); System.out.println(h.label() + " " + h.brand()); } } ```

    1. A. >> Acme Acme

      This is what the program would print if the static method were called correctly through the interface name; it wrongly assumes an implementing class inherits the interface's static method.

    2. B. >> Acme

      Assumes only the label method is evaluated and that the static call contributes nothing or fails silently; in fact that call is a hard compile error, not a no-op.

    3. C. Compilation failsCorrect answer

      Correct — a class does not inherit static methods from its superinterfaces, so calling the static method through an instance of the implementing class fails to compile with "cannot find symbol" (JLS 17 §8.4.8).

    4. D. Acme Acme

      Assumes the whole thing runs and the label yields just "Acme"; both the missing prefix and the fact that the static call never compiles are wrong.

    Explanation

    Static methods declared in an interface belong to the interface itself and are not inherited by implementing classes, so they can only be invoked through the interface name, never through an implementing type or its instances. Because the program calls the static method through an instance of the implementer, the whole class fails to compile. The private and default instance methods, by contrast, are perfectly legal in Java 17.

  3. Question 3

    Two overloads of feed() differ only in their parameter type. The same object is passed through two references of different declared types. What is printed? ```java public class Main { static class Animal {} static class Dog extends Animal {} static String feed(Animal a) { return "animal"; } static String feed(Dog d) { return "dog"; } public static void main(String[] args) { Animal a = new Dog(); Dog d = new Dog(); System.out.println(feed(a) + " " + feed(d)); } } ```

    1. A. animal dogCorrect answer

      Correct — overload resolution uses the declared type of the argument: the Animal-declared reference selects feed(Animal), and the Dog-declared reference selects the more specific feed(Dog) (JLS 17 §15.12.2).

    2. B. dog dog

      Assumes overloading dispatches on the runtime object; overloads are resolved at compile time from the declared type, so the Animal-typed reference selects feed(Animal) even though it holds a Dog.

    3. C. animal animal

      Ignores that the second reference is declared Dog; for it the more specific feed(Dog) is chosen, giving 'dog' for the second call.

    4. D. Compilation fails

      Assumes the call is ambiguous; each argument has a single most-specific applicable overload, so there is nothing for the compiler to reject.

    Explanation

    Overload resolution is a compile-time decision based on the *declared* (static) type of the argument, not the runtime object: `a` is declared Animal, so feed(Animal) is chosen even though it holds a Dog; `d` is declared Dog, so the more specific feed(Dog) wins. dog dog is the classic trap of assuming overloading is polymorphic — only *overriding* uses dynamic dispatch. There is no ambiguity for the compiler to reject, so it compiles cleanly.

  4. Question 4

    What does this print? ```java public class Main { static class A { static String s() { return "A"; } } static class B extends A { static String s() { return "B"; } } public static void main(String[] args) { A a = new B(); System.out.println(a.s()); } } ```

    1. A. ACorrect answer

      s() is static, so the call binds at compile time to the declared type of the reference, which is the superclass; its s() returns "A" and the runtime type never matters (JLS 17 §15.12.4).

    2. B. Compilation fails

      Assumes it will not compile, but calling a static method through an instance reference is legal (if poor style), so it compiles.

    3. C. Throws ClassCastException

      Expects a cast failure, but no cast occurs anywhere in the code.

    4. D. B

      Would require virtual dispatch, but same-signature static methods hide rather than override, so the runtime type is irrelevant.

    Explanation

    Static method calls are bound at compile time using the declared type of the reference, not the runtime type of the object. A subclass static method with the same signature hides the superclass one rather than overriding it, so no virtual dispatch occurs. Invoking such a method through a superclass-typed reference therefore runs the superclass version (JLS 17 §15.12.4).

  5. Question 5

    An interface declares a field with no modifiers and an abstract method. What does this program print? ```java public class Main { interface Config { int MAX = 10; int limit(); } static class Small implements Config { @Override public int limit() { return MAX / 4; } } public static void main(String[] args) { Config c = new Small(); System.out.println(c.limit() + " " + Config.MAX); } } ```

    1. A. 2 10Correct answer

      Correct — interface fields are implicitly public static final, so MAX is the constant 10, and int division 10 / 4 truncates to 2 (JLS 17 §9.3).

    2. B. 2.5 10

      Assumes floating-point division; both operands are int, so 10 / 4 is integer division that truncates to 2, not 2.5.

    3. C. 2 0

      Assumes MAX is uninitialised or zero; an interface constant must be initialised and here holds 10, so Config.MAX prints 10.

    4. D. Compilation fails

      Assumes the constant needs an explicit `static final` or the method an explicit `public`; interface fields are implicitly public static final and interface methods implicitly public, and the override correctly declares public, so it compiles.

    Explanation

    Every field declared in an interface is implicitly public, static and final (JLS 9.3), so MAX is a constant of value 10, inherited by Small and readable unqualified inside it. MAX / 4 is int division — 10 / 4 truncates to 2, not 2.5 — so the answer is 2 10. Compilation fails is the trap for expecting an explicit `static final` or an explicit `public` on limit(); interface methods are implicitly public abstract, and the implementing method correctly declares public.

  6. Question 6

    What does this print? ```java public class Main { enum Op { ADD { int apply(int a, int b) { return a + b; } }, MUL { int apply(int a, int b) { return a * b; } }; abstract int apply(int a, int b); } public static void main(String[] args) { System.out.println(Op.ADD.apply(2, 3) + Op.MUL.apply(2, 3)); } } ```

    1. A. 56

      Would result from string concatenation of the two results, but both apply() calls return ints and no String operand is present, so + is arithmetic.

    2. B. 11Correct answer

      Each constant supplies a class body implementing the abstract method; ADD.apply(2,3) is 5 and MUL.apply(2,3) is 6, and adding these ints gives 11 (JLS 17 §8.9).

    3. C. 30

      Multiplies the two results (5 * 6) instead of adding them.

    4. D. Compilation fails

      Assumes it will not compile, but an enum with an abstract method compiles as long as every constant provides a body, which both do.

    Explanation

    An abstract method on an enum requires every constant to supply a constant-specific class body implementing it, and the code does so. The two constants' implementations return ints, so combining their results with + performs integer arithmetic rather than string concatenation. The printed value is the arithmetic sum of the two computed ints (JLS 17 §8.9).

  7. Question 7

    Which two statements about reference casting and `instanceof` in Java 17 are correct? (Choose two.)

    1. A. Casting a reference to a class type when neither that class nor the reference's compile-time class is a subtype of the other is rejected by the compilerCorrect answer

      Two unrelated classes can share no instance under single inheritance, so no object could satisfy the cast and javac rejects it outright with incompatible types, with no run-time check reached.

    2. B. Casting a reference whose compile-time type is a non-final class to an interface that the class does not implement compiles; any failure surfaces at run time as a ClassCastExceptionCorrect answer

      A subclass of the non-final class could implement the interface, so an object satisfying the cast is conceivable; the compiler permits it and defers to a run-time check that throws ClassCastException.

    3. C. An `instanceof` test whose right-hand type is a class unrelated to the operand's compile-time type simply evaluates to false at run time

      Assumes instanceof is a purely run-time question that can answer false; when the two types are provably disjoint the test is a compile error, the same incompatible types error the cast gets.

    4. D. Casting a reference whose compile-time type is a final class to an interface that the class does not implement compiles, and fails only at run time

      Misses that final means no subclasses, so the set of objects satisfying the cast to an unimplemented interface is provably empty and the compiler rejects it - a compile error, not a run-time failure.

    Explanation

    Java splits cast checking in two. The compiler first asks whether the conversion is *possible for any object* the reference could hold; only conversions that survive that question get a run-time check. Why `Casting a reference to a class type when neither that class nor the reference's compile-time class is a subtype of the other...` is correct: two unrelated classes can have no common instance (Java is single-inheritance for classes), so no object could ever satisfy the cast. javac rejects it outright with `incompatible types: P cannot be converted to Q` — there is no run-time check to reach. Why `Casting a reference whose compile-time type is a non-final class to an interface that the class does not implement...` is correct: a *subclass* of that non-final class could implement the interface, so an object satisfying the cast is conceivable. The compiler therefore permits it and defers to a run-time check, which throws ClassCastException when the object turns out not to implement it. Why the others are wrong: `An `instanceof` test whose right-hand type is a class unrelated to the operand's compile-time type...` encodes the belief that `instanceof` is a purely run-time question that can always answer false. It is not: when the two types are provably disjoint, the test is a compile error, the same `incompatible types` error the cast gets. `Casting a reference whose compile-time type is a final class to an interface that the class does not implement...` misses the significance of `final`. A final class has no subclasses, so the set of objects that could satisfy the cast is empty and the compiler can prove it — the cast is a compile error, not a run-time failure. That final/non-final split is the whole point of the pair. Exam tip: ask "could any object at all satisfy this cast?" If provably not — unrelated classes, or a final class to an interface it does not implement — it is a compile error. If yes but not guaranteed, it compiles and risks ClassCastException. `instanceof` obeys the same castability rule, so an `instanceof` that "looks obviously false" may not compile at all.

  8. Question 8

    The enum Status overrides toString(). What is written to standard output? ```java public class Main { enum Status { NEW, ACTIVE, CLOSED; @Override public String toString() { return name().toLowerCase(); } } public static void main(String[] args) { Status s = Status.ACTIVE; System.out.println(s + " " + s.name() + " " + Status.valueOf(s.name()).ordinal()); } } ```

    1. A. ACTIVE ACTIVE 1

      Assumes concatenation uses the constant identifier; it actually invokes the overridden toString(), which lower-cases the name, so the first token is "active".

    2. B. active ACTIVE 1Correct answer

      Correct — concatenation calls the overridden toString() ("active"), while name() is final and returns the exact identifier "ACTIVE", and ordinal() is zero-based so the second constant is 1.

    3. C. active active 1

      Assumes overriding toString() also changes name(); name() is final on java.lang.Enum and always returns the exact constant identifier, so it stays "ACTIVE".

    4. D. active ACTIVE 2

      Uses a one-based ordinal; ordinal() is zero-based, so the second constant is 1, not 2.

    Explanation

    String concatenation invokes an object's toString(), so the overridden version — which lower-cases the constant — governs that token, but name() is declared final on the enum superclass and always returns the exact source identifier regardless of any toString() override. valueOf recovers the constant by its exact identifier, and ordinal positions are counted from zero, so the second constant reports 1.

Practise all 27 Advanced Class Design & Design Patterns 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