1. 概要

このチュートリアルでは、OCP認定の高度なJavaクラスの設計目標について説明します。

2. OCPJava認定

OCP認定は、 OCA認定のアップグレードですが、同じ形式の多肢選択式の質問に従います。 ただし、並行性、ジェネリック、NIOなどの高度なトピックが含まれています。

このチュートリアルでは、試験の高度なJavaクラスの設計目標に焦点を当てます。 実際には、ここで説明するトピックのいくつかは、OCA試験のJavaクラスの設計目標と重複しています。 ただし、同時に、 OCPには、内部クラス、列挙型、ラムダなどの高度なトピックに関する質問も含まれています。

次の各セクションは、試験の目的に専念しています。

3. 抽象クラスとメソッドを使用するコードを開発する

最初の試験の目的は、抽象クラスとメソッドの使用です。 Javaでは、 abstract クラスを使用して、具象子クラス間で変数とメソッドを共有します。

試験のヒント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

特に、 abstract メソッドにはデフォルトのアクセス修飾子があり、両方のクラスが異なるパッケージに含まれているため、Horseクラスではアクセスできません。 したがって、正解は(B)です。

試験のヒント3.2: abstractClassまたはMethodの構文エラー

一部の質問では、指定されたコードの構文が正しくないかどうかを確認する必要があります。 抽象クラスを使用すると、このようなエラーを簡単に見逃す可能性があります。

たとえば、次のことを解決してみてください。

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

ここで重要なのは、抽象メソッドはメソッド本体を持つことができないということです。 また、abstractメソッドは非抽象クラスに存在できません。 したがって、(A)、(B)、および(C)が正解です。

試験のヒント3.3:抽象メソッドの実装がありません

abstract メソッドの具体的な実装なしで、非abstract子クラスを探します。

たとえば、次のことを解決してみてください。

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)は正しいです。

両生類canFly()を実装していませんが、抽象クラスとして宣言されているため、(A)は正しくありません。

試験のヒント3.4: private final、、または static with abstractKeywordの使用

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のfinalキーワードを使用すると、定数値で変数を宣言できます。 さらに、拡張またはオーバーライドできないクラスとメソッドを宣言することもできます。

試験のヒント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()はAnimalクラスで finalとして宣言されているため、Horseクラスでオーバーライドすることはできません。 したがって、(E)が正解です。

また、メソッドの引数で最終的な変数を探します。 このような変数に新しい値が割り当てられると、コンパイルエラーが発生します。

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:内部クラスでのthisキーワードの誤った使用

内部クラス内でのこのキーワードの誤った使用を探します。

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

メッセージフィールドを更新したことがないため、事実上最終的なです。 したがって、(B)が正解です。

試験のヒント5.4:L ocal内部クラスをプライベート、パブリック、保護、または staticとしてマークすることはできません

ローカル変数と同じルールがローカル内部クラスに適用されます。 したがって、そのような制約に違反する質問には注意する必要があります。

さらに、 static メソッドで宣言されたローカルクラスは、それを囲むクラスのstaticメンバーにのみアクセスできます。

試験のヒント5.5:静的内部クラスの非静的メンバー変数

s tatic ネストされたクラスは、外部クラスのインスタンス変数または非静的メソッドにアクセスできません。

したがって、 static ネストされたクラスを含むが、非staticネストされたクラスとして動作する質問に注意することが重要です。

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行目は、 static 以外のネストされたクラスに対して有効でしたが、ここではコンパイルエラーが発生します。

試験のヒント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. 列挙

Enums は、Javaで列挙された定数のリストを表す方法です。 これらは通常のJavaクラスのように動作するため、変数、メソッド、およびコンストラクターを含めることができます。

同様ですが、enumは通常のクラスよりもかなり複雑な構文を持っています。 OCP試験は、列挙を含む質問によるそのような構文の不確実性に焦点を当てています。

試験のヒント6.1:列挙型宣言の構文エラー

構文エラーが正しくない列挙型宣言に注意してください。

たとえば、次のことを解決してみてください。

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

この質問には2つの問題があります。

  • 3行目には、セミコロン(;)がありません。 列挙型変数またはメソッドが含まれている場合は、セミコロンが必須であることに注意してください
  • この列挙型にはパブリックコンストラクターがあります

したがって、(B)と(C)が正解です。

試験のヒント6.2:列挙型抽象メソッド

インターフェイスを実装する、または抽象メソッドを含む列挙型の質問に注意してください。

たとえば、次のことを解決してみてください。

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

abstract メソッドがあるため、すべてのenum定数にその実装を提供する必要があります。 また、上記のコードは FISH に対してのみ実装されているため、コンパイルエラーが発生します。 したがって、(A)が正解です。

同様に、列挙型がインターフェースを実装する場合、 すべての定数は、そのインターフェースのすべてのメソッドの実装を提供する必要があります。

試験のヒント6.3:列挙型値の反復

Javaは、列挙値を反復処理するための静的メソッドを提供します。 そのような反復の1つの出力を計算するように求める質問を期待する必要があります。

たとえば、次のことを解決してみてください。

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 in Java

Javaでは、 interfaces は、クラスのコントラクトを定義する抽象型です。 OCP試験には、継承、メソッドのオーバーライド、および多重継承の問題について候補者をテストするさまざまな質問があります。

試験のヒント7.1:非抽象クラスでの抽象メソッドの実装

インターフェイスのすべてのabstractメソッドを実装していない具体的な実装に注意してください。

たとえば、次のことを解決してみてください。

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では許可されていません。 3行目のGreaterFlamingoクラスは、すべてのabstractメソッドの具体的な実装を提供する必要があります。

試験のヒント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()メソッドの実装を提供するので、最初は有効なコードのように見えるかもしれません。 ただし、よく見ると、オーバーライドされていることがわかります食べる() メソッドはありません公衆。 したがって、正解は(E)です。

試験のヒント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

eat()メソッドで@Overrideを使用したことに注意してください。 ただし、 Flamingoクラスにはそのようなabstract メソッドがないため、これはオーバーライドされたメソッドではありません。 したがって、(C)が正解です。

8. ラムダ式を作成して使用する

高度なJavaクラス設計の最後の試験の目的はラムダについてです。 ラムダ式は、関数インターフェイスを実装する匿名内部クラスの代わりに使用できることを覚えておく必要があります。 その結果、両方を交互に使用して、試験で多くの質問が表示されます。

ラムダ式の構文は少し注意が必要です。 試験で構文エラーを見つけるには、ラムダに関するいくつかのルールを理解することが重要です。

試験のヒント8.1:ラムダ宣言内の非最終変数

メソッドローカルクラスと同様に、ラムダ関数内で使用できるのはfinalまたは効果的にfinal変数のみです。 試験問題はそのような制約を尊重しない場合があります。

たとえば、次のことを解決してみてください。

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

ラムダ式内の変数に値を割り当てようとしたため、コンパイルエラーが発生します。 したがって、(C)が正解です。

9. 結論

一般的に言って、試験の質問の構文を読んで理解することが重要です。ほとんどのコーディングの質問は、候補者をコンパイルエラーと混同しようとします。 したがって、出力を計算する前に、このようなエラーを除外することが重要です。

この記事では、試験に頻繁に出題されるいくつかのヒントといくつかのサンプルの質問について説明しました。 これらは、試験で何が期待できるかを示すためのサンプルの質問です。

そしてもちろん、試験をクラックするための最良の方法は、事前にそのような模擬問題を練習することです!