1. Overview
In this tutorial, we'll discuss the advanced Java class design objective of the OCP 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.
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.
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).
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 (C) are correct answers.
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, (C) and (D) are correct.
Even though Amphibian doesn't implement canFly(), it's declared as an abstract class, which is why (A) is incorrect.
Exam Tip 3.4: Use of private, final, 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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() { } };
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.
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 (C) are correct answers.
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.
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.
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.
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.
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).
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, (C) is the correct answer.
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.
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, (C) is the correct answer.
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!