Перейти к основному содержимому

Сертификация OCP — расширенный дизайн классов Java

· 16 мин. чтения

1. Обзор

В этом руководстве мы обсудим цель разработки расширенного класса Java для сертификации OCP.

2. Сертификация OCP Java

Сертификация OCP является обновлением сертификации OCA, но следует тому же формату вопросов с несколькими вариантами ответов. Однако он включает дополнительные темы, такие как параллелизм, дженерики и NIO.

В этом руководстве мы сосредоточимся на задаче экзамена по расширенному дизайну класса Java. В действительности, некоторые из тем, которые мы обсудим, пересекаются с задачей проектирования классов Java экзамена OCA. Но в то же время OCP также содержит вопросы по сложным темам, таким как внутренние классы, типы перечислений и лямбда-выражения .

Каждый из следующих разделов посвящен задаче экзамена.

3. Разрабатывайте код, использующий абстрактные классы и методы

Первая цель экзамена — использование абстрактных классов и методов. В Java мы используем абстрактные классы для совместного использования переменных и методов между конкретными дочерними классами.

Экзаменационный совет 3.1. Неправильный модификатор доступа к абстрактным классам

Мы всегда должны искать модификатор доступа в вопросах об абстрактных классах и методах.

Например, попробуйте решить следующее:

package animal;
public abstract class Animal {

abstract boolean canFly();
}

package horse;
import animal.Animal;

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 10
C. Compilation fails on Line 12
D. None of the above

Примечательно, что абстрактный метод имеет модификатор доступа по умолчанию, и поскольку оба класса находятся в разных пакетах, мы не можем получить к нему доступ в классе Horse . Следовательно, правильный ответ (Б).

Совет к экзамену 3.2: Синтаксические ошибки в абстрактном классе или методе

Некоторые вопросы требуют от нас проверки неправильного синтаксиса в данном коде. С абстрактными классами мы можем легко пропустить такие ошибки.

Например, попробуйте решить следующее:

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

Здесь важно помнить, что у абстрактных методов не может быть тела метода . Кроме того, абстрактный метод не может существовать в неабстрактном классе . Следовательно, (A), (B) и (C) являются правильными ответами.

Экзаменационный совет 3.3: Отсутствует реализация абстрактных методов

Ищите неабстрактные дочерние классы без конкретной реализации абстрактного метода.

Например, попробуйте решить следующее:

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

Класс Frog не реализует метод canFly `` ( ) , а также уменьшает видимость метода swim () . Следовательно, (C) и (D) верны.

Несмотря на то, что Amphibian не реализует canFly(), он объявлен как абстрактный класс, поэтому (A) неверен.

Экзаменационный совет 3.4. Использование private , final или static с ключевым словом abstract

Абстрактное ключевое слово нельзя сочетать с ключевым словом static , private или final . В результате любое из следующих утверждений не допускается:

public final abstract class Animal {
}

public abstract class Animal {

public final abstract void eat();
}

public abstract class Animal {

private abstract void eat();
}

Любое такое объявление приведет к ошибке компиляции.

4. Разработайте код, использующий последнее ключевое слово

Ключевое слово final в Java позволяет нам объявлять переменные с постоянным значением. Более того, это также позволяет нам объявлять классы и методы, которые мы не можем расширять или переопределять.

Экзаменационный совет 4.1: Переопределение окончательных классов или методов

Ищите методы, которые объявлены как final и переопределены в дочернем классе.

Например, попробуйте решить следующее:

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

Поскольку функция eat() объявлена как final в классе Animal , мы не можем переопределить ее в классе Horse . Следовательно, (Е) является правильным ответом.

Кроме того, ищите конечные переменные в аргументе метода. Если таким переменным будет присвоено новое значение, это приведет к ошибке компиляции.

5. Внутренние классы

Вопросы по внутренним классам обычно не такие простые, как по другим темам. В экзамене есть много вопросов по таким темам , как обобщения, коллекции и параллелизм, которые используют синтаксис внутреннего класса, что затрудняет нам понимание цели вопроса.

Совет к экзамену 5.1: неправильное создание нестатических внутренних классов

Единственный способ создать экземпляр нестатического внутреннего класса — через экземпляр внешнего класса.

Например, попробуйте решить следующее:

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

Поскольку в строке 19 мы пытаемся создать экземпляр внутреннего класса без объекта внешнего класса, (B) является правильным ответом.

Экзаменационный совет 5.2. Неправильное использование этого ключевого слова во внутренних классах

Ищите неправильное использование этого ключевого слова внутри внутренних классов:

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();
}
}

Поскольку это можно использовать только для доступа к текущему исполняемому объекту , строка 9 приведет к ошибке компиляции. По этой причине мы должны внимательно следить за использованием this внутри внутренних классов.

Экзаменационный совет 5.3. Нефинальные переменные внутри локальных внутренних классов

Локальные классы методов не могут получить доступ к локальной переменной, если она не объявлена как final или ее значение не остается неизменным внутри внутреннего класса.

Например, попробуйте решить следующее:

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

Поскольку мы никогда не обновляли поле сообщения , оно фактически final . Следовательно, (В) является правильным ответом.

Совет к экзамену 5.4. Локальный внутренний класс нельзя помечать как частный, общедоступный, защищенный или статический

К локальным внутренним классам применяются те же правила, что и к локальным переменным. Поэтому мы должны следить за любым вопросом, который нарушает такие ограничения.

Кроме того, любой локальный класс, объявленный в статическом методе, имеет доступ только к статическим членам окружающего класса.

Совет к экзамену 5.5. Нестатические переменные -члены в статическом внутреннем классе ``

статические вложенные классы не имеют доступа к переменным экземпляра или нестатическим методам внешнего класса. ``

Поэтому важно обращать внимание на вопросы, которые связаны со статическими вложенными классами, но ведут себя как нестатические вложенные классы:

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);
}
}
}

Несмотря на то, что строки 10 и 11 допустимы для нестатических вложенных классов, здесь это приводит к ошибке компиляции.

Совет по экзамену 5.6: Неверное объявление анонимных внутренних классов

Анонимные классы разбросаны по экзамену OCP так же, как и вложенные классы. Есть много вопросов о коллекциях, потоках и параллелизме, которые используют анонимный внутренний класс, в основном с запутанным синтаксисом.

Например, попробуйте решить следующее:

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

Поскольку анонимный класс Animal не закрывается точкой с запятой , в строке 15 возникает ошибка компиляции, поэтому правильный ответ (F).

Совет к экзамену 5.7. Создание экземпляра интерфейса

Обратите внимание на вопросы, пытающиеся создать экземпляр интерфейса, а не реализовать его:

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

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

}
};

6. Перечисления

Перечисления — это способ представления пронумерованного списка констант в Java. Они ведут себя как обычные классы Java и поэтому могут содержать переменные, методы и конструкторы .

Несмотря на схожесть, перечисления имеют довольно сложный синтаксис, чем обычные классы. Экзамены OCP сосредоточены на таких синтаксических неопределенностях с вопросами, содержащими перечисления.

Совет к экзамену 6.1: Синтаксические ошибки в объявлении enum

Обращайте внимание на объявления enum с некорректными синтаксическими ошибками.

Например, попробуйте решить следующее:

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

В этом вопросе есть две проблемы:

  • В строке 3 отсутствует точка с запятой (;). Помните, что если перечисление содержит переменные или методы, точка с запятой обязательна.
  • В этом перечислении есть публичный конструктор

Следовательно, (B) и (C) являются правильными ответами.

Экзаменационный совет 6.2: enum с абстрактными методами

Обратите внимание на вопросы перечисления , которые реализуют интерфейс или содержат абстрактный метод.

Например, попробуйте решить следующее:

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

Поскольку существует абстрактный метод, мы должны обеспечить его реализацию для каждой константы перечисления . И поскольку приведенный выше код реализует это только для FISH , мы получим ошибку компиляции. Следовательно, (А) является правильным ответом.

Точно так же, если перечисление реализует интерфейс , каждая константа должна предоставлять реализации для всех методов этого интерфейса .

Совет к экзамену 6.3. Перебор значений перечисления

Java предоставляет статические методы для перебора значений перечисления . Мы должны ожидать вопросов, которые просят нас рассчитать результат одной такой итерации.

Например, попробуйте решить следующее:

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

Результатом является BIRD , поэтому (B) верно.

7. Интерфейсы и @Override в Java

В Java интерфейсы — это абстрактные типы, которые определяют контракт для класса. На экзамене OCP есть различные вопросы, которые проверяют кандидата на предмет наследования, переопределения методов и проблем с множественным наследованием.

Экзаменационный совет 7.1: Реализация абстрактного метода в неабстрактных классах

Ищите конкретные реализации, которые не реализуют все абстрактные методы интерфейса.

Например, попробуйте решить следующее:

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

Поскольку все эти утверждения верны, (A) является правильным ответом.

С уровнем наследования такие вопросы могут быть временами каверзными. Поэтому мы должны следить за любыми ошибками компиляции, прежде чем пытаться вычислить вывод, следуя по следу переопределенных методов.

Другая такая ошибка компиляции возникает из-за использования реализаций и расширений:

interface Bird extends Flyable, Wings {}

public class GreaterFlamingo extends Flamingos implements Bird, Vegetarian {}

public class GreaterFlamingo extends Flamingos, Bird {}

Здесь строки 1 и 3 являются допустимыми операторами, а 5 не разрешены в Java. Класс GreaterFlamingo в строке 3 теперь должен предоставлять конкретные реализации всех абстрактных методов.

Экзаменационный совет 7.2: методы по умолчанию с идентичными сигнатурами методов

Начиная с JDK 8 интерфейсы теперь могут иметь статические методы и методы по умолчанию . Это может привести к ситуации, когда несколько интерфейсов содержат метод по умолчанию с одной и той же сигнатурой. На экзамене мы найдем вопросы с такими интерфейсами.

Например, попробуйте решить следующее:

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

Этот вопрос связан с множественным наследованием . Примечательно, что в правиле говорится, что мы должны обеспечить реализацию методов по умолчанию , если они переопределяются из нескольких интерфейсов .

Теперь, поскольку этот код обеспечивает реализацию метода eat() , поначалу он может показаться корректным. Однако, если мы внимательно посмотрим, то увидим, что переопределенный метод eat() не является общедоступным. Следовательно, правильный ответ (Е).

Экзаменационный совет 7.3: Использование @Override

@Override используется для обозначения переопределенного метода в Java. Хотя это и необязательно, оно улучшает читабельность и помогает компилятору сообщать о неправильном синтаксисе. Ищите неправильное использование этой аннотации на экзамене.

Например, попробуйте решить следующее:

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

Обратите внимание, что мы использовали @Override в методе eat() . Однако, поскольку в классе Flamingo нет такого абстрактного метода , это не переопределенный метод. Следовательно, (С) является правильным ответом. ``

8. Создание и использование лямбда-выражений

Последняя цель экзамена по продвинутому дизайну классов Java касается лямбда-выражений. Следует помнить, что лямбда-выражения можно использовать вместо анонимных внутренних классов, реализующих функциональный интерфейс . В результате мы увидим много вопросов на экзамене, использующих оба варианта попеременно.

Синтаксис лямбда-выражения немного сложен. Чтобы обнаружить синтаксические ошибки на экзамене, важно понимать некоторые правила, касающиеся лямбда -выражений .

Совет к экзамену 8.1. Неокончательные переменные внутри лямбда-объявлений

Подобно локальным классам методов, мы можем использовать только конечные или фактически конечные переменные внутри лямбда-функции. Экзаменационные вопросы могут не учитывать такие ограничения.

Например, попробуйте решить следующее:

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

Это приведет к ошибке компиляции, потому что мы попытались присвоить значение переменной внутри лямбда-выражения . Следовательно, (С) является правильным ответом.

9. Заключение

Вообще говоря, важно прочитать и понять синтаксис вопросов на экзамене. Большинство вопросов по кодированию пытаются запутать кандидатов ошибками компиляции . Поэтому важно исключить любые такие ошибки перед расчетом выходных данных.

В этой статье мы обсудили несколько советов, которые часто встречаются на экзамене, а также несколько примеров вопросов. Это всего лишь примерные вопросы, чтобы продемонстрировать, что мы можем ожидать на экзамене.

И, конечно же, лучший способ успешно сдать экзамен — заранее попрактиковаться в таких фиктивных вопросах!