Advanced Class Design & Design Patterns practice questions

From OCP Java SE 25 (1Z0-831) · 29 questions on this topic

Advanced Class Design & Design Patterns practice questions from OCP Java SE 25 (1Z0-831). This pack has 29 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

    What does this print? ```java public class Main { interface Counter { int base(); private int step() { return 2; } default int next() { return base() + step(); } } public static void main(String[] args) { Counter c = () -> 10; System.out.print(c.next()); } } ```

    1. A. Compilation fails: an interface cannot declare a private method

      Private interface methods have been legal since Java 9; they exist to share code among default methods without exposing it in the public API.

    2. B. 10

      Ignores step(); next() adds the private helper's return value 2 to base()'s 10, so the result is not just the base value.

    3. C. Compilation fails: a default method cannot call a private method

      A default method may freely call a private interface method — sharing logic with default methods is exactly what private interface helpers are for.

    4. D. 12Correct answer

      base() is the single abstract method (the private and default methods do not count against the functional-interface rule), the lambda supplies base() returning 10, and the default next() returns base() + step() = 10 + 2 = 12.

    Explanation

    An interface may declare abstract, default, static, and private methods, and only the abstract ones count toward the single-abstract-method rule — so this interface stays functional and a lambda can implement its one abstract method. Private interface methods exist precisely so that default methods can call shared helpers without exposing them. The default method here sums the lambda-supplied value and the private helper's value.

  2. Question 2

    What is the result of running this program? ```java public class Main { static class Animal {} static class Dog extends Animal {} static class Cat extends Animal {} public static void main(String[] args) { Animal a = new Cat(); Dog d = (Dog) a; System.out.print(d.getClass().getSimpleName()); } } ```

    1. A. Throws ClassCastExceptionCorrect answer

      a is declared Animal but refers to a Cat; the cast (Dog) a compiles because Dog is a subtype of Animal, but at run time the JVM finds the object is a Cat, not a Dog, and throws ClassCastException before getSimpleName() runs.

    2. B. Prints Cat

      This would require the cast to succeed and leave a Cat, but the cast target is Dog and the object is not a Dog, so the cast fails and nothing prints.

    3. C. Prints Dog

      Assumes the cast changes the object's type; a cast only reinterprets a reference, never converting a Cat into a Dog, and it fails when the runtime type does not match the target.

    4. D. Compilation fails: Animal cannot be cast to Dog

      It compiles: casting between two types on the same inheritance branch (Animal to Dog) is a legal narrowing reference conversion; only direct sibling-to-sibling casts (Cat to Dog) are rejected at compile time.

    Explanation

    A downcast to a subtype compiles whenever the target is on the same inheritance branch as the reference's static type, because the compiler cannot rule out that the object is really that subtype. At run time the JVM checks the object's actual class, and when it is a sibling type rather than the cast target, it throws ClassCastException before the reference is used. Guarding the downcast with an instanceof pattern avoids the exception.

  3. Question 3

    The interface below declares a field, an abstract method, and a default method, and is implemented by a lambda. What is the output? ```java public class Main { interface Meter { int UNIT = 100; int raw(); default int scaled() { return raw() * UNIT; } } public static void main(String[] args) { Meter m = () -> 3; System.out.println(m.scaled()); } } ```

    1. A. 3

      Assumes the default method returns the raw abstract result alone; it multiplies that result by the constant.

    2. B. 100

      Assumes only the constant is returned; the default method multiplies the raw result (3) by the constant (100).

    3. C. 300Correct answer

      An interface field is implicitly public static final, so the constant is 100, and the default method returns raw times constant, which is 3 times 100, or 300.

    4. D. Compilation fails

      Assumes declaring a field and a default method disqualifies the interface as functional; only the count of abstract methods matters, and there is exactly one, so a lambda is valid.

    Explanation

    Fields declared in an interface are implicitly public, static and final constants. A functional interface is defined solely by having one abstract method, so extra constants and default methods do not prevent a lambda from implementing it. The default method simply combines the lambda-supplied abstract result with the constant.

  4. Question 4

    Sub declares a field with the same name as a field in Base and also overrides a method. The object is accessed through a Base reference, then through a cast. What is printed? ```java public class Main { static class Base { String name = "base"; String label() { return "L-base"; } } static class Sub extends Base { String name = "sub"; String label() { return "L-sub"; } } public static void main(String[] args) { Base b = new Sub(); System.out.println(b.name + " " + ((Sub) b).name + " " + b.label()); } } ```

    1. A. sub sub L-sub

      Applies dynamic dispatch to fields; field access is resolved from the static type, so through a superclass-typed reference the superclass field is read.

    2. B. base base L-base

      Assumes both the field and the method resolve to the superclass version; the method is overridden and dispatches to the subclass at run time.

    3. C. base sub L-subCorrect answer

      Fields are resolved from the reference's static type, so the superclass-typed access reads the superclass field and the cast reads the subclass field, while the overridden method dispatches dynamically to the subclass.

    4. D. base sub L-base

      Applies static resolution to the method; an overridden instance method is dispatched on the runtime type even through a superclass-typed reference.

    Explanation

    Fields are not polymorphic: a same-named field in a subclass hides the superclass field, and which one an access sees is fixed at compile time by the reference's declared type. Both fields coexist in the object, so casting the reference selects the other one. Instance methods are the opposite; overriding causes run-time dispatch on the actual class regardless of the reference type.

  5. Question 5

    Does this compile, and if not, why? ```java public class Main { public static void main(String[] args) { int _ = compute(); System.out.print("value=" + _); } static int compute() { return 42; } } ```

    1. A. It compiles and prints value=42

      Printing value=42 would require reading `_`, and that read is exactly the illegal operation that stops the program from compiling.

    2. B. It compiles and prints value=0

      Assumes `_` holds a readable default; the problem is not its value but that an unnamed variable is unreadable, so the read never compiles.

    3. C. Compilation fails: an unnamed variable _ cannot be readCorrect answer

      `int _ = compute();` is a legal unnamed declaration (compute()'s side effect still runs), but the next line tries to READ `_`, which javac rejects with `underscore not allowed here` (JEP 456).

    4. D. Compilation fails: _ is not a legal name for a local variable declaration

      Declaring `int _ = ...` is legal — that is the whole point of unnamed variables; the error is the later use, not the declaration itself.

    Explanation

    An unnamed variable is write-only: you may declare and even initialize it — running any side effects of the initializer — but you may never reference it afterward. The declaration here is fine, so the compile error comes from the subsequent attempt to read the underscore, which javac reports as `underscore not allowed here`. Any code that reads an unnamed variable fails to compile, which is also why two `int _` declarations can coexist in one scope.

  6. Question 6

    What does this print? ```java public class Main { static class Base { Base(int weight) { System.out.print("base=" + weight + " "); } } static class Box extends Base { Box(int side) { if (side <= 0) throw new IllegalArgumentException("bad"); int volume = side * side * side; super(volume); System.out.print("side=" + side); } } public static void main(String[] args) { new Box(3); } } ```

    1. A. base=27 side=3Correct answer

      The prologue validates the argument (side is 3, so no exception) and computes volume = `3*3*3` = 27 into a local; super(volume) then runs Base's constructor and prints `base=27 `, after which the epilogue prints `side=3`. This is exactly the flexible-constructor-body behaviour of JEP 513.

    2. B. base=3 side=3

      Assumes the raw parameter side was passed to super(); the prologue actually forwarded the computed volume (27), not side itself.

    3. C. side=3 base=27

      Reverses the print order; super(volume) executes before the epilogue statement, so Base prints first and the `side=3` line comes second.

    4. D. Compilation fails: no statement may appear before super()

      That was the pre-JDK-25 rule; JEP 513 finalized flexible constructor bodies, so locals, validation, and computation may precede an explicit super()/this() call.

    Explanation

    Flexible constructor bodies let the statements before an explicit super(...) run as a prologue against locals and static state. Those statements can validate the argument and compute the value handed to the superclass constructor, so the superclass runs with the computed argument and prints first, before the constructor's remaining body executes. The key to tracing the output is following what value actually reaches super(), not the raw parameter.

  7. Question 7

    Which TWO statements about methods declared in an interface are true in Java 25? (Choose two.)

    1. A. An interface may declare a private static method, and both static and default methods of that interface may call it.Correct answer

      Correct: private interface methods exist to factor out shared code, and a private static method can be called unqualified from both the interface's static and default methods.

    2. B. A default method may be declared final so that implementing classes cannot override it.

      Incorrect: a default method may not be declared final — the modifier is rejected outright, because a default method is always overridable by an implementing class or subinterface.

    3. C. A class that implements two unrelated interfaces which each supply a default method with the same signature does not compile unless it overrides that method.Correct answer

      Correct: this is the diamond rule — a class inheriting two unrelated default methods with the same signature does not compile until it overrides that method (optionally disambiguating with X.super.tag()).

    4. D. A static interface method is inherited by an implementing class and may be invoked through that class's name.

      Incorrect: static interface methods are not inherited, so they cannot be invoked through an implementing class's name — you must call them through the interface name.

    Explanation

    Why `An interface may declare a private static method, and both static and default methods of that interface may call it.` is true: private interface methods (static and instance) exist to factor out shared code without exporting it. An interface with `private static String secret()`, a `static String viaStatic()` calling it, and a `default String viaDefault()` calling it compiles and runs, printing `s1 s2`. A private *instance* method needs a receiver, so a static method of the interface can call it only on an explicit instance (`s.inst()`), never unqualified; a private *static* method can be called unqualified from both. Why `A class that implements two unrelated interfaces which each supply a default method with the same signature does not compile ...` is true: this is the diamond rule. With `interface X { default String tag() {...} }` and `interface Y { default String tag() {...} }`, `class Impl implements X, Y { }` fails with `types X and Y are incompatible; class Impl inherits unrelated defaults for tag() from types X and Y`. The class must override `tag()`, and inside the override it may disambiguate with `X.super.tag()`. Why the others are wrong: `A static interface method is inherited by an implementing class ...` encodes the belief that interface statics behave like class statics. They do not: static interface methods are not inherited. Calling `Impl.tag()` where `Util` declares `static tag()` fails with `cannot find symbol ... location: class Impl`; you must write `Util.tag()`. `A default method may be declared final ...` assumes default methods are ordinary virtual methods you can seal. `final default String tag()` is rejected outright with `modifier final not allowed here` — a default method is always overridable, which is why a subinterface or class can always replace it. Exam tip: interface method modifiers are a short list — `public`/`abstract` (implicit), `default`, `static`, `private`, `private static`. `final`, `synchronized`, and `protected` are all illegal on an interface method. The reverse trap: interface *fields* are implicitly `public static final`, so the word `final` is fine there and only illegal on methods.

  8. Question 8

    Three overloaded constructors are declared and one is invoked with the int literal 7. Which constructor is selected, and what does the program print? ```java public class Main { static class Signal { static String chosen; Signal(long v) { chosen = "long"; } Signal(Integer v) { chosen = "Integer"; } Signal(int... v) { chosen = "varargs"; } } public static void main(String[] args) { new Signal(7); System.out.println(Signal.chosen); } } ```

    1. A. varargs

      Assumes the variable-arity constructor is a candidate; a varargs match is only considered in the third phase of overload resolution, which is never reached because a fixed-arity constructor is already applicable by widening in the first phase.

    2. B. Integer

      Assumes the int literal boxes to Integer; boxing is only considered in the second phase, but the first phase already succeeds because the int widens to long, so the wrapper constructor is never reached.

    3. C. longCorrect answer

      The first phase of overload resolution allows widening primitive conversion but no boxing or varargs, so int widens to long and the long constructor is applicable, ending the search and printing 'long' (JLS 25 §15.12.2).

    4. D. Compilation fails

      Assumes the three candidates make the call ambiguous; because an applicable constructor is found in the earliest phase, resolution stops there and there is no ambiguity, so the code compiles.

    Explanation

    Overload resolution for constructors runs in three phases. Phase 1 considers only subtyping and widening primitive conversion, with no boxing and no varargs: int widens to long, so Signal(long) is applicable and the search stops there — 'long' is printed. Signal(Integer) would require boxing, which is only considered in phase 2, and Signal(int...) is only considered in phase 3, so neither is ever reached. The call is not ambiguous, because an applicable candidate was found in the earliest phase.

Practise all 29 Advanced Class Design & Design Patterns questions

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

Open OCP Java SE 25

Other topics in this pack