OCP認定–高度なJavaクラス設計

[[OCP Java]]
=== 1. 概要

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

[[Java certification]]
=== 2. OCP Java認定

https://education.oracle.com/oracle-certified-professional-java-se-8-programmer/trackp_357[OCP認証]は、https://education.oracle.com/oracle-certified-associateのアップグレードです-java-se-8-programmer / trackp_333 [OCA certification]が、複数選択問題の同じ形式に従います。 ただし、並行性、ジェネリック、NIOなどの高度なトピックが含まれています。
このチュートリアルでは、試験の高度なJavaクラス設計目標に焦点を当てます。 実際には、これから説明するトピックの一部は、OCA試験のJavaクラス設計目標と重複しています。 ただし、同時に* OCPには、内部クラス、列挙型、ラムダなどの高度なトピックに関する質問も含まれています。
以下の各セクションは、試験の目的に専念しています。

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

最初の試験の目的は、__ https://www.baeldung.com/java-abstract-class [abstract] __classesおよびメソッドの使用です。 Javaでは、__ abstract __classesを使用して、具体的な子クラス間で変数とメソッドを共有します。

[[abstract visibility]]
==== 試験のヒント3.1:abstract Classesを使用した不正なアクセス修飾子

__abstract __classesとメソッドに関する質問では、常にアクセス修飾子を探す必要があります。
たとえば、次のことを試して解決してください。
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
特に、__abstract __methodにはデフォルトのアクセス修飾子があり、両方のクラスが*異なるパッケージにあるため、__ Horse __class *ではアクセスできません。 したがって、正解は(B)です。

[[abstract syntax]]
==== 試験のヒント3.2:abstract _Classまたはメソッドの構文エラー

いくつかの質問では、指定されたコードの誤った構文をチェックする必要があります。 __abstract __classesを使用すると、このようなエラーを簡単に見逃してしまいます。
たとえば、次の解決を試みます。
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 ___methodsにメソッドボディ*を含めることはできないことを覚えておくことが重要です。 また、* __ abstract __methodは、_abstract_以外のクラスには存在できません*。 したがって、(A)、(B)、および(C)は正解です。

[[java abstract methods]]
==== 試験のヒント3.3:abstract _Methodsの実装の欠落

__abstract __methodの具体的な実装なしで、_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()__method *の可視性を低下させます。 したがって、(C)と(D)は正しいです。
__Amphibian __doesn __canFly()を実装していない場合でも、__itは__abstract __classとして宣言されているため、(A)が正しくありません。

[[abstract static]]
==== 試験のヒント3.4:abstract Keywordで_private_、final、または_static_を使用する

  • abstract keywordは、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();
}
このような宣言は、コンパイルエラーになります。

[[java final]]
=== 4. _final_キーワードを使用するコードを開発する

Javaの__https://www.baeldung.com/java-final [final] __keywordを使用すると、定数値を持つ変数を宣言できます。 さらに、拡張またはオーバーライドできないクラスとメソッドを宣言することもできます。

[[final classes]]
==== 試験のヒント4.1:オーバーライドされた_final_クラスまたはメソッド

__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 __classで* _final_として宣言されているため、__Horse __class *でオーバーライドできません。 したがって、(E)は正解です。
また、メソッドの引数で_final_変数を探します。 そのような変数に新しい値が割り当てられると、コンパイルエラーが発生します。

[[java inner classes]]
=== 5. 内部クラス

link:/java-nested-classes[inner classes]に関する質問は、通常、他のトピックほど簡単ではありません。 試験には、ジェネリック、コレクション、並行性など、内部クラス構文を使用するトピックに関する多くの質問があります。そのため、質問の意図を理解することは困難です。

[[inner classes java]]
==== 試験のヒント5.1:非_static_内部クラスの誤ったインスタンス化

non_static_ innerクラスをインスタンス化する唯一の方法は、outerクラスのインスタンスを使用することです。
たとえば、次の解決を試みます。
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)が正しい答えです。

[[java this]]
==== 試験のヒント5.2:内部クラスでの_this_キーワードの誤った使用

内部クラス内での__this __keywordの誤った使用を探します。
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();
    }
}
  • this _は現在実行中のオブジェクトにアクセスするためにのみ使用できるため、9行目ではコンパイルエラーが発生します。 このため、 this _inside内部クラスの使用を注意深く観察する必要があります。

[[inner class final]]
==== 試験のヒント5.3:ローカル内部クラス内の非final _変数

メソッドローカルクラスは、__ 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
*私たちは__message ___fieldを更新したことがないため、事実上_final_ *です。 したがって、(B)は正解です。

[[method local classes]]
==== 試験のヒント5.4:L ocal内部クラスを_private、public、protected、_または _ * static * _としてマークすることはできません

ローカル変数と同じルールがローカル内部クラスに適用されます。 したがって、このような制約に違反する質問に注意する必要があります。
さらに、_static_メソッドで宣言されたローカルクラスは、囲んでいるクラスの__static ___membersのみにアクセスできます。

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

[[java anonymous]]
==== 試験のヒント5.6:匿名の内部クラスの誤った宣言

link:/java-anonymous-classes [匿名クラス]は、ネストされたクラスと同じ方法で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
an_Animal_の* anonymousクラスはセミコロン*で閉じられていないため、15行目にコンパイルエラーがあり、これが(F)が正しい答えである理由です。

[[java interfaces]]
==== 試験のヒント5.7:インターフェイスのインスタンス化

インターフェイスを実装するのではなく、インスタンス化することを試みる質問を探してください:*
Runnable r = new Runnable(); // compilation error

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

    }
};

[[java enums]]
=== 6. 列挙型

link:/a-guide-to-java-enums[Enums]は、Javaで列挙された定数のリストを表す方法です。 これらは通常のJavaクラスのように動作するため、https://www.baeldung.com/java-enum-values [変数、メソッド、およびコンストラクター]を含めることができます。
同様ですが、enumは通常のクラスよりもかなり複雑な構文を持っています。 OCP試験は、enumを含む質問でのこのような構文の不確実性に焦点を当てています。

[[java enum]]
==== 試験のヒント6.1:enum Declarationの構文エラー

不正な構文エラーのある__enum __declarationsを探してください。
たとえば、次の解決を試みます。
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には、セミコロン(;)がありません。 覚えておいてください
    enum contains 変数またはメソッド、セミコロンが必須

  • この_enum_にはパブリックコンストラクターがあります

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

[[enum abstract]]
==== 試験のヒント6.2:enum with _abstract_メソッド

インターフェイスを実装する、または__abstract __methodを含む_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
__abstract ___methodがあるため、_enum_定数ごとにその実装を提供する必要があります。 また、上記のコードは_FISH_に対してのみ実装しているため、コンパイルエラーが発生します。 したがって、(A)は正解です。
同様に、__ enum __implementsがinterface __、__を実装する場合、*すべての定数はそのinterface __.__のすべてのメソッドの実装を提供する必要があります*

[[enum iteration]]
==== 試験のヒント6.3:enum Valuesの繰り返し

Javaはlink:/java-enum-iteration [__enum __valuesを反復する]の静的メソッドを提供します。 このような反復の出力を計算するように求める質問を期待する必要があります。
たとえば、次の解決を試みます。
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)は正しいです。

[[java interfaces]]
=== 7. インターフェイスとJavaの @ Override _

Javaでは、https://www.baeldung.com/java-interfaces [interfaces]はクラスのコントラクトを定義する抽象型です。 OCP試験には、受験者の継承、メソッドのオーバーライド、多重継承の問題をテストするさまざまな質問があります。

[[java interfaces]]
==== 試験のヒント7.1:Non_abstract Classesでのabstract Methodの実装

インターフェイスのすべての__abstract ___methodsを実装していない具体的な実装を探してください。
たとえば、次の解決を試みます。
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)が正解です。
継承のレベルでは、そのような質問は時々難しい場合があります。 したがって、*オーバーライドされたメソッドのトレースに従って出力を計算しようとする前に、コンパイルエラーを調べる必要があります。*
別のこのようなコンパイルエラーは、__implements __and _extends:_の使用から発生します。
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_メソッドの具体的な実装を提供する必要があります。

[[java8 default]]
==== 試験のヒント7.2:同一のメソッドシグネチャを持つdefault Methods

JDK 8以降、インターフェイスにlink:/java-static-default-methods[_static_および__default __methods]を追加できるようになりました。 これにより、複数のインターフェイスに同じシグネチャを持つa__default __methodが含まれる場合があります。 このようなインターフェースを備えた試験で質問が見つかります。
たとえば、次の解決を試みます。
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
この質問はlink:/java-inheritance [複数の継承]に関連しています。 特に、ルールは、複数のインターフェイスからオーバーライドされる場合は、__ default __methodsの実装を提供する必要があると述べています*。
現在、このコードは__eat()__methodの実装を提供しているため、最初は有効なコードに見えるかもしれません。 ただし、よく見ると、オーバーライドされた__eat()__methodはnot__publicであることがわかります。したがって、正しい答えは(E)です。

[[java override]]
==== 試験のヒント7.3:_ @ Override _の使用

__https://www.baeldung.com/java-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 ___classにはそのような_abstract_メソッドがないため、これはオーバーライドされたメソッドではありません。 したがって、(C)は正解です。

[[java8 lambdas]]
=== 8. Lambda式を作成して使用する

高度なJavaクラス設計の最後の試験目標は、ラムダに関するものです。 ラムダ式は、https://www.baeldung.com/java-8-functional-interfaces [functional interface]を実装する匿名内部クラスの代わりとして使用できることを覚えておく必要があります。 その結果、試験では多くの質問が交互に使用されます。
ラムダ式の構文は少し注意が必要です。 試験の構文エラーを見つけるには、https://www.baeldung.com/java-8-lambda-expressions-tips [ラムダに関する規則]を理解することが重要です。

[[lambda final variables]]
==== 試験のヒント8.1:Lambda宣言内の非final _変数

メソッドのローカルクラスと同様に、ラムダ関数内では__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)は正解です。

[[java certification]]
=== 9. 結論

一般的に、試験の問題の構文を読んで理解することが重要です。 *ほとんどのコーディングの質問は、候補とコンパイルエラーを混同しようとします*。 したがって、出力を計算する前に、そのようなエラーを除外することが重要です。
この記事では、サンプルの質問とともに、試験で頻繁に表示されるいくつかのヒントについて説明しました。 これらは、試験で期待できることを示すためのサンプルの質問です。
そしてもちろん、試験に合格する最善の方法は、そのような模擬質問を事前に練習することです!