OCP Certification – Advanced Java Class Design

[[OCP Java]]
=== 1. Overview

In this tutorial, we’ll discuss the advanced Java class design objective
of the OCP certification.

[[Java certification]]
=== 2. OCP Java Certification

The
OCP
certification
is an upgrade on the
OCA
certification
but follows the same format of multiple-choice questions.
It, however, includes advanced topics such as concurrency, generics, and
NIO.

In this tutorial, we’ll focus on the advanced Java class design
objective of the exam. In reality, some of the topics we’ll discuss are
overlapping with the Java class design objective of the OCA exam. But,
at the same time, OCP also contains questions on advanced topics like
inner classes, enum types, and lambdas
.

Each of the following sections is dedicated to an objective from the
exam.

[[java abstract]]
=== 3. Develop Code that Uses Abstract Classes and Methods

The first exam objective is the use of
abstract classes and
methods. In Java, we use abstract classes to share variables and
methods between the concrete child classes.

[[abstract visibility]]
==== Exam Tip 3.1: Incorrect Access Modifier with abstract Classes

We must always look for an access modifier in questions about
abstract classes and methods.

For example, try and solve the following:

package animal;
public abstract class Animal {

    abstract boolean canFly();
}

package horse;
public class Horse extends Animal {

    @Override
    boolean canFly() {
        return false;
    }

    public static void main(String[] args) {

        System.out.println(new Horse().canFly());
    }
}
Which of the following is true?
A. The output is false
B. Compilation fails on Line 8
C. Compilation fails on Line 11
D. None of the above

Notably, the abstract method has a default access modifier and since
both classes are in different packages, we cannot access it in the
Horse class
. Therefore,  the correct answer is (B).

[[abstract syntax]]
==== Exam Tip 3.2: Syntax Errors in abstract Class or Method

Some questions require us to check for incorrect syntax in the given
code. With abstract classes, we can easily miss such errors.

For example, try to solve the following:

public abstract class Animal {

    protected abstract boolean canFly() {
    }

    public abstract void eat() {
        System.out.println("Eat...");
    }
}

public class Amphibian extends Animal {
    @Override
    protected boolean canFly() {
        return false;
    }

    @Override
    public void eat() {

    }

    public abstract boolean swim();
}

public class Frog extends Amphibian {
}
Which are true? (Choose all that apply.)
A. Compilation error on line 3
B. Compilation error on line 6
C. Compilation error on line 11
D. Compilation error on line 13
E. Compilation error on line 22

It’s important to remember here that abstract methods can’t have a
method body
. Also, an abstract method cannot exist in a
non-abstract class
. Therefore, (A), (B), and ©  are correct
answers.

[[java abstract methods]]
==== Exam Tip 3.3: Missing Implementation for abstract Methods

Look for non-abstract child classes without the concrete
implementation for an abstract method.

For example, try to solve the following:

public abstract class Animal {

    protected abstract boolean canFly();

    public abstract void eat();
}

public abstract class Amphibian extends Animal {

    @Override
    public void eat() {
        System.out.println("Eat...");
    }

    public abstract boolean swim();
}

public class Frog extends Amphibian {

    @Override
    protected boolean swim() {
        return false;
    }

}
Which are true? (Choose all that apply)
A. Compilation error on line 8
B. Compilation error on line 11
C. Compilation error on line 18
D. Compilation error on line 21
E. No compilation error

The Frog class doesn’t implement the canFly() method and also
reduces the visibility of the swim() method. Therefore, © and
(D) are correct.

Even though Amphibian doesn’t implement canFly(), it’s declared
as an abstract class, which is why (A) is incorrect.

[[abstract static]]
==== Exam Tip 3.4: Use of privatefinal, or static with abstract Keyword

The abstract keyword cannot be combined with static, private,
or final keyword
. As a result, any of the following statements are
not allowed:

public final abstract class Animal {
}

public abstract class Animal {

    public final abstract void eat();
}

public abstract class Animal {

    private abstract void eat();
}

Any such declaration will result in a compilation error.

[[java final]]
=== 4. Develop Code That Uses the final Keyword

The final keyword in Java
allows us to declare variables with a constant value. Moreover, it also
allows us to declare classes and methods that we can’t extend or
override.

[[final classes]]
==== Exam Tip 4.1: Overridden final Classes or Methods

Look for methods that are declared as final, and overridden in the
child class.

For example, try to solve the following:

public abstract class Animal {

    public final void eat() {
        System.out.println("Eat...");
    }
}

public class Horse extends Animal {

    public void eat() {
        System.out.println("Eat Grass");
    }

    public static void main(String[] args) {
        Animal animal = new Horse();
        animal.eat();
    }
}
What is the output?
A. Eat...
B. Eat Grass
C. The code will not compile because of line 3
D. The code will not compile because of line 8
E. The code will not compile because of line 10

Since eat() is declared as final in the Animal class, we cannot
override it in the Horse class
. Hence, (E) is the correct answer.

Also, look for final variables in an argument of a method. If a new
value is assigned to such variables, it will result in a compilation
error.

[[java inner classes]]
=== 5. Inner Classes

Questions on inner classes
are usually not as straightforward as other topics. There are many
questions in the exam on topics like generics, collections, and
concurrency that use the inner class syntax,
thereby making it
difficult for us to understand the intent of the question.

[[inner classes java]]
==== Exam Tip 5.1: Incorrect Instantiation of Non-static Inner Classes

The only way to instantiate a non-static inner class is through an
instance of the outer class.

For example, try to solve the following:

public class Animal {

    class EatingHabbits {
    }

    private EatingHabbits eatingHabbits() {
        return new EatingHabbits();
    }
}

public class Zookeeper {

    public static void main(String[] args) {
        Zookeeper zookeeper = new Zookeeper();
        zookeeper.feed();
    }

    private void feed() {
        EatingHabbits habbits = new EatingHabbits();
        Animal animal = new Animal();
        Animal.EatingHabbits habbits1 = animal.eatingHabbits();
    }
}
What is the result? (Choose all that apply.)
A. Compilation error on line 7
B. Compilation error on line 19
C. Compilation error on line 21
D. No compilation error

Since on line 19, we try to instantiate the inner class without the
object of the outer class, (B) is the correct answer.

[[java this]]
==== Exam Tip 5.2: Incorrect Use of this Keyword in Inner Classes

Look for incorrect use of this keyword inside inner classes:

public class Animal {
    private int age = 10;

    public class EatingHabbits {
        private int numOfTimes = 5;

        public void print() {
            System.out.println("The value of numOfTimes " + this.numOfTimes);
            System.out.println("The value of age " + this.age);
            System.out.println("The value of age " + Animal.this.age);
        }
    }

    public static void main(String[] args) {
        Animal.EatingHabbits habbits = new Animal().new EatingHabbits();
        habbits.print();
    }
}

Since this can only be used to access the currently executing
object
, line 9 would result in a compilation error. For this reason, we
must closely observe the use of this inside inner classes.

[[inner class final]]
==== Exam Tip 5.3: Non-final Variables Inside Local Inner Classes

Method local classes cannot access a local variable unless it’s declared
as final or its value remains unchanged inside the inner class.

For example, try to solve the following:

public class Animal {
    private int age = 10;

    public void printAge() {
        String message = "The age is ";
        class PrintUtility {
            void print() {
                System.out.println(message + age);
            }
        }

        PrintUtility utility = new PrintUtility();
        utility.print();
    }

    public static void main(String[] args) {
        new Animal().printAge();
    }
}
What is the result of the following code?

A. The age is 0
B. The age is 10
C. Line 8 generates a compiler error
D. Line 12 generates a compiler error
E. An exception is thrown

Since we never updated the message field, it’s effectively
final
. Hence, (B) is the correct answer.

[[method local classes]]
==== Exam Tip 5.4: Local Inner Class Cannot be Marked as private, public, protected, or *static*

The same rules apply to local inner classes as to local variables.
Therefore, we must look out for any question that violates such
constraints.

Additionally, any local class declared in a static method has access
to only static members of the enclosing class.

[[java static]]
==== Exam Tip 5.5: Non-static Member Variables in a static Inner Class

static nested classes don’t have access to the instance variables
or non-static methods of the outer class.

It’s, therefore, important to look out for questions that involve
static nested classes but behave as non-static nested classes:

public class Animal {

    private int age = 10;

    static class EatingHabits {

        private int numOfTimes = 5;

        public void print() {
            System.out.println("The value of x " + age);
            System.out.println("The value of x " + Animal.this.age);
            System.out.println("The value of numOfTimes " + numOfTimes);
        }
    }
}

Even though line 10 and 11 were valid for non-static nested classes,
it results in a compilation error here.

[[java anonymous]]
==== Exam Tip 5.6: Incorrect Declaration for Anonymous Inner Classes

Anonymous classes are
scattered across the OCP exam in the same way as the nested classes.
There are a lot of questions around collections, threads, and
concurrency that use an anonymous inner class, mostly with a confusing
syntax.

For example, try to solve the following:

public class Animal {

    public void feed() {
        System.out.println("Eating Grass");
    }
}

public class Zookeeper {

    public static void main(String[] args) {
        Animal animal = new Animal(){
            public void feed(){
                System.out.println("Eating Fish");
            }
        }
        animal.feed();
    }
}
What is the result?

A. An exception occurs at runtime
B. Eating Fish
C. Eating Grass
D. Compilation fails because of an error on line 11
E. Compilation fails because of an error on line 12
F. Compilation fails because of an error on line 15

Since the anonymous class of Animal is not closed with a semicolon,
there is a compilation error on line 15, which is why (F) is the correct
answer.

[[java interfaces]]
==== Exam Tip 5.7: Instantiating an Interface

Look out for questions attempting to instantiate an interface rather
than implementing it:

Runnable r = new Runnable(); // compilation error

Runnable r = new Runnable() { // legal statement
    @Override
    public void run() {

    }
};

[[java enums]]
=== 6. Enums

Enums are a way to
represent an enumerated list of constants in Java. They behave like
regular Java classes and can, therefore, contain
variables, methods, and
constructors
.

Albeit similar, enums do have a rather complex syntax than regular
classes. The OCP exams focus on such syntax uncertainties with questions
containing enums.

[[java enum]]
==== Exam Tip 6.1: Syntax Errors in enum Declaration

Look out for enum declarations with incorrect syntax errors.

For example, try to solve the following:

public enum AnimalSpecies {
    MAMMAL(false), FISH(true), BIRD(false),
    REPTILE(false), AMPHIBIAN(true)

    boolean hasFins;

    public AnimalSpecies(boolean hasFins) {
        this.hasFins = hasFins;
    }

    public boolean hasFins() {
        return hasFins;
    }
}
What is the result of the following code? (Choose all that apply.)

A. Compiler error on line 2
B. Compiler error on line 3
C. Compiler error on line 7
D. Compiler error on line 11
E. The code compiles successfully

There are two problems with this question:

  • On line 3, there is a missing semicolon (;). Remember that if an
    enum contains variables or methods, a semicolon is mandatory

  • There is a public constructor in this enum

Therefore, (B) and © are correct answers.

[[enum abstract]]
==== Exam Tip 6.2: enum with abstract Methods

Look out for enum questions that implement an interface or contain an
abstract method.

For example, try to solve the following:

public enum AnimalSpecies {
    MAMMAL(false), FISH(true){
        @Override
        boolean canFly() {
            return false;
        }
    }, BIRD(false),
    REPTILE(false), AMPHIBIAN(true);

    boolean hasFins;

    AnimalSpecies(boolean hasFins) {
        this.hasFins = hasFins;
    }

    public boolean hasFins() {
        return hasFins;
    }

    abstract boolean canFly();
}

public class Zookeeper {

    public static void main(String[] args) {
        AnimalSpecies.MAMMAL.canFly();
    }
}
What is the result of the following code? (Choose all that apply.)

A. Compilation error on line 2
B. Compilation error on line 4
C. Compilation error on line 20
D. Compilation error on line 26
E. No compilation error

Since there is an abstract method, we must provide its
implementation for every enum constant. And because the above code
only implements it for FISH, we’ll get a compilation error. Hence, (A)
is the correct answer.

Likewise, if the enum implements an interface, every constant
must provide implementations for all methods
of that interface.

[[enum iteration]]
==== Exam Tip 6.3: Iterating over enum Values

Java provides static methods for
iterating over
the enum values
. We must expect questions that ask us to calculate
the output of one such iteration.

For example, try to solve the following:

public enum AnimalSpecies {
    MAMMAL, FISH, BIRD, REPTILE, AMPHIBIAN
}

public class Zookeeper {

    public static void main(String[] args) {
        AnimalSpecies[] animals = AnimalSpecies.values();
        System.out.println(animals[2]);
    }
}
What is the result? (Choose all that apply.)

A. FISH
B. BIRD
C. Compilation fails due to an error on line 2
D. Compilation fails due to an error on line 8
E. Compilation fails due to an error on line 10

The output is BIRD, therefore, (B) is correct.

[[java interfaces]]
=== 7. Interfaces and @Override in Java

In Java, interfaces are
abstract types that define a contract for a class. OCP exam has various
questions that test a candidate on inheritance, method overriding, and
multiple inheritance problems.

[[java interfaces]]
==== Exam Tip 7.1: abstract Method Implementation in Non-abstract Classes

Look out for concrete implementations that don’t implement all
abstract methods of an interface.

For example, try to solve the following:

class Bird implements Flyable {
    public void fly() {
    }
}

abstract class Catbirds extends Bird {

}

abstract class Flamingos extends Bird {
    public abstract String color();
}

class GreaterFlamingo extends Flamingos {
    public String color() {
        System.out.println("The color is pink");
    }
}

interface Flyable {
    void fly();
}
What is the result? (Choose all that apply.)

A. Compilation succeeds
B. Compilation fails with an error on line 6
C. Compilation fails with an error on line 10
D. Compilation fails with an error on line 11
E. Compilation fails with an error on line 14

Since all these are valid statements, (A) is the correct answer.

With the level of inheritance, such questions can be tricky at times.
Therefore, we must look out for any compilation errors before trying to
calculate the output by following a trace of overridden methods.

Another such compilation error arises from the use
of implements and extends:

interface Bird extends Flyable, Wings {}

public class GreaterFlamingo extends Flamingos implements Bird, Vegetarian {}

public class GreaterFlamingo extends Flamingos, Bird {}

Here, line 1 & 3 are valid statements while 5 is not allowed in Java.
The GreaterFlamingo class on line 3 must now provide concrete
implementations of all abstract methods.

[[java8 default]]
==== Exam tip 7.2: default Methods with Identical Method Signatures

Starting with JDK 8, interfaces can now have
static and
default methods
. This might lead to a situation where multiple
interfaces contain a default method with the same signature. We’ll
find questions in the exam with such interfaces.

For example, try to solve the following:

public interface Vegetarian {

    default void eat() {
        System.out.println("Eat Veg");
    }
}

public interface NonVegetarian {

    default void eat() {
        System.out.println("Eat NonVeg");
    }
}

public class Racoon implements Vegetarian, NonVegetarian {

    @Override
    void eat() {
        System.out.println("Eat Something")
    }

    public static void main(String[] args) {
        Racoon racoon = new Racoon();
        racoon.eat();
    }
}
What is the result?

A. Eat Veg
B. Eat NonVeg
C. Eat Something
D. The output is unpredictable
E. Compilation fails
F. An exception is thrown at runtime

This question is related to
multiple inheritance.
Notably, the rule says that we must provide the implementation of
default methods if it’s overridden from multiple interfaces
.

Now, since this code does provide an implementation of eat() method,
it may seem a valid code at first. However, if we look closely, we’ll
see that the overridden eat() method is not public. Therefore, the
correct answer is (E).

[[java override]]
==== Exam Tip 7.3: The Use of @Override 

@Override is used to denote
an overridden method in Java. Although optional, it improves readability
and helps the compiler in reporting incorrect syntaxes. Look for misuse
of this annotation in the exam.

For example, try to solve the following:

public abstract class Flamingo {

    public abstract String color();

    public abstract void fly();
}

public class GreaterFlamingo extends Flamingo {
    @Override
    public String color() {
        return "Pink";
    }

    @Override
    public void fly() {
        System.out.println("Flying");
    }

    @Override
    public void eat() {
        System.out.println("Eating");
    }

    public static void main(String[] args) {
        GreaterFlamingo flamingo = new GreaterFlamingo();
        System.out.println(flamingo.color());
    }
}
What is the result? (Choose all that apply.)

A. Pink
B. Compilation error on line 8
C. Compilation error on line 19
D. Compilation error on line 20

Please note that we used the @Override on the eat() method.
However, since there is no such abstract method in
the Flamingo class, this is not an overridden method. Hence, © is
the correct answer.

[[java8 lambdas]]
=== 8. Create and Use Lambda Expressions

The last exam objective in advanced Java class design is about lambdas.
It must be remembered that lambda expressions can be used as a
substitute for anonymous inner classes implementing a
functional
interface
. As a result, we’ll see a lot of questions in the exam using
both of them alternatively.

The syntax for lambda expression is a bit tricky. To spot syntax errors
in the exam, it’s important to understand some
rules around
lambdas
.

[[lambda final variables]]
==== Exam tip 8.1: Non-final Variables Inside Lambda Declarations

Similar to method local classes, we can only use final or
effectively final variables inside a lambda function. Exam questions
may not honor such constraints.

For example, try to solve the following:

List<String> birds = Arrays.asList("eagle", "seagull", "albatross", "buzzard", "goose");
int longest = 0;
birds.forEach(b -> {
    if (b.length() > longest){
        longest = b.length();
    }
});

System.out.println("Longest bird name is length: " + longest);
What is the result?

A. "Longest bird name is length: 9"
B. Compilation fails because of an error on line 3
C. Compilation fails because of an error on line 5
D. A runtime exception occurs on line 5

This will result in a compilation error because we tried to assign a
value to a variable inside the lambda expression
. Hence, © is the
correct answer.

[[java certification]]
=== 9. Conclusion

Generally speaking, it’s important to read and understand the syntax of
questions in the exam. Most coding questions try and confuse the
candidates with compilation errors
. It’s therefore important to rule
out any such errors before calculating the output.

In this article, we discussed a few tips that appear frequently in the
exam along with some sample questions. These are just sample questions
to demonstrate what we can expect in the exam.

And of course, the best way to crack the exam is by practicing such mock
questions beforehand!

Leave a Reply

Your email address will not be published.