【JavaSE】オブジェクト指向プログラミングの考え方の継承

 【本プログラムの目的】

1. 継承

2. 組み合わせ

目次

1. 継承が必要な理由

2. 継承の概念

3. 継承の構文

4. 親クラスのメンバーへのアクセス

4.1 サブクラス内の親クラスのメンバー変数にアクセスする

4.2 サブクラス内の親クラスのメンバー メソッドにアクセスする

5.スーパーキーワード

6. サブクラスコンストラクター

7.スーパーとこれ

8. 再び初期化について話します

9. 保護されたキーワード

10. 継承方法

11.最後のキーワード

12 継承と構成


1. 継承が必要な理由

Javaではクラスは現実世界のエンティティを記述するために使用され、クラスのインスタンス化後の製品オブジェクトを使用して現実の実体を表現できますが、現実世界は複雑であり、物事の間に何らかの関係がある場合があるため、プログラムを設計する際にはい、それは考慮する必要があります。

:犬と猫、どちらも動物です。

Java 言語を使用して記述すると、次のように設計されます。

class Dog {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "正在吃饭!");
    }
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
    public void bark() {
        System.out.println(this.name + "正在汪汪叫!");
    }
}
class Cat {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "正在吃饭!");
    }
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
    public void mew() {
        System.out.println(this.name + "正在喵喵叫!");
    }
}

上記のコードを見ると、以下に示すように、cat クラスと Dog クラスに多くの繰り返しがあることがわかります。

 これらの共通点を抽出できるでしょうか? 継承の概念はオブジェクト指向の考え方で提案されており、特に共通点の抽出とコードの再利用に使用されます。

2. 継承の概念

継承(継承)機構:オブジェクト指向プログラミングにおいて、コードを再利用可能にするための最も重要な手段であり、プログラマが元のクラスの特性を維持した上で新しい機能を拡張・追加することで、派生と呼ばれる新しいクラスを生成することができます。クラス継承はオブジェクト指向プログラミングの階層構造を表し、単純なものから複雑なものまでの認知プロセスを反映します。継承によって解決される主な問題は、共通性の抽出とコードの再利用の実現です。

たとえば、犬と猫はどちらも動物であるため、共通の内容を抽出し、継承のアイデアを使用して共有を実現できます。

 コードマップ:

上の図では、Dog と Cat の両方が Animal クラスを継承していますが、そのうち、Animal クラスは親クラス/基本クラスまたはスーパークラスと呼ばれ、Dog と Cat は Animal のサブクラス/派生クラスと呼ばれます。サブクラスは繰り返すことができます。親クラスのメンバーを使用します。サブクラスは、実装時に新しく追加されたメンバーのみを考慮する必要があります。

 継承の概念から、継承の最大の役割はコードの再利用を実現し、ポリモーフィズム(続編)を実現することであることがわかります。

3. 継承の構文

Java では、クラス間の継承関係を表現したい場合は、次のようにextendsキーワードを使用する必要があります。

 継承を使用して 1.2 のシーンを再設計します。

class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "正在吃饭!");
    }
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}
class Dog extends Animal {
    public void bark() {
        System.out.println(this.name + "正在汪汪叫!");
    }
}
class Cat extends Animal {
    public void mew() {
        System.out.println(this.name + "正在喵喵叫!");
    }
}

 概要:継承は、コードの再利用の効果を達成するために共通性の抽出を実現する一種の思考です。

知らせ:

1. サブクラスは、親クラスのメンバ変数またはメンバメソッドをサブクラスに継承します。

2. サブクラスは親クラスを継承した後、基本クラスとの違いを反映するために独自の一意のメンバーを追加する必要があります。それ以外の場合は継承する必要はありません。

4. 親クラスのメンバーへのアクセス

継承システムでは、サブクラスは親クラスのメソッドとフィールドを継承します。そのため、サブクラスは親クラスの継承されたメンバーに直接アクセスできますか?

4.1 サブクラス内の親クラスのメンバー変数にアクセスする

1. サブクラスと親クラスに同名のメンバ変数が存在しない

public class Base {
    int a;
    int b;
}
public class Derived extends Base{
    int c;
    public void method(){
        a = 10; // 访问从父类中继承下来的a
        b = 20; // 访问从父类中继承下来的b
        c = 30; // 访问子类自己的c
    }
}

2. サブクラスと親クラスのメンバー変数は同じ名前です

class Base {
    public int a = 9;
    public int b = 99;
}
class Derived extends Base {
    public int a = 88;
    public void method() {
        a = 1;
        b = 2;
        System.out.println("a: " + a);
        System.out.println("b: " + b);
    }
}
public class Test2 {
    public static void main(String[] args) {
        Derived derived = new Derived();
        derived.method();
    }
}

 サブクラス メソッドまたはサブクラス オブジェクトを通じてメンバーにアクセスする場合:

  1. アクセス対象のメンバ変数がサブクラスに存在する場合は、自身のメンバ変数へのアクセスが優先されます。
  2. アクセスするメンバ変数がサブクラスに存在しない場合は、親クラスから継承したメンバ変数にアクセスします。
  3. 親クラスが定義されていない場合、コンパイル時にエラーが報告されます。アクセスするメンバ変数が親クラスのメンバ変数と同じ名前の場合、自身のメンバ変数へのアクセスが優先されます。

メンバ変数へのアクセスは近接性の原則に従います。独自の変数がある場合は優先されます。メンバ変数がない場合は、親クラスで見つけることができます。

4.2 サブクラス内の親クラスのメンバー メソッドにアクセスする

1. メンバーのメソッド名が異なる

class Base {
    public void method1() {
        System.out.println("Base:method()");
    }
}
class Derived extends Base {
    public void method2() {
        System.out.println("Derived:method()");
    }
    
    public void test() {
        method1();
        method2();
    }
}
public class Test2 {
    public static void main(String[] args) {
        Derived derived = new Derived();
        derived.test();
    }
}

2. メンバーメソッドが同じ名前を持つ

class Base {
    public void method1() {
        System.out.println("Base:method()");
    }
}
class Derived extends Base {
    public void method1() {
        System.out.println("Derived:method()");
    }
    public void method2() {
        System.out.println("Derived:method()");
    }
    public void test() {
        method1();
        method2();
    }
}
public class Test2 {
    public static void main(String[] args) {
        Derived derived = new Derived();
        derived.test();
    }
}

 【イラスト】

  • サブクラスオブジェクトを介して親クラスとサブクラスの異なる名前のメソッドにアクセスする場合は、まずサブクラスでそのメソッドを探し、見つかった場合はアクセスします。それ以外の場合は親クラスで探し、見つかった場合はアクセスします。見つけられない場合は、コンパイルしてエラーを報告します。
  • 派生クラスオブジェクト経由で親クラスとサブクラスの同名のメソッドにアクセスする際、親クラスとサブクラスの同名のメソッドのパラメータリストが異なる場合(オーバーロード)、適切なメソッドアクセスを選択する呼び出しメソッドによって渡されたパラメータに従って、何もない場合はエラーを報告します。

5.スーパーキーワード

設計が悪いため、またはシナリオの必要性により、サブクラスと親クラスに同じ名前のメンバーが存在する可能性があります。親クラスのメソッド内で親クラスの同じ名前のメンバーにアクセスしたい場合はどうすればよいですか?サブクラス?直接アクセスは不可能です。Java にはsuper キーワードが用意されています。このキーワードの主な機能は、サブクラス メソッド内の親クラスのメンバーにアクセスすることです。

サブクラスのメンバー メソッドの前に super キーワードを追加します。

 サブクラスのメンバー変数の前に super キーワードを追加します。

サブクラス メソッドで、親クラスのメンバーに明示的にアクセスする場合は、super キーワードを使用できます。

同時に、 super() は、以下で紹介する親クラスのコンストラクターを呼び出すこともできます。

【予防】

1.非静的メソッドでのみ使用可能

2. サブクラスのメソッドで、親クラスのメンバー変数およびメソッドにアクセスします。

6. サブクラスコンストラクター

サブクラスの構築方法を学ぶ前に、次の質問を見てみましょう。

コンストラクターにパラメーターを親クラスに提供すると、コンパイラーによってエラーが表示されます。

 パラメーターを持たないコンストラクターを親クラスに提供すると、コンパイラーはエラー メッセージを表示しません。

現時点で大きな疑問をお持ちですか?この問題を解決しましょう:

サブクラスが親クラスを継承する場合、サブクラスは構築が完了する前に親クラスの初期化を支援する必要があります。

 コンストラクターを作成しない場合、コンパイラーはデフォルトでパラメーターなしのコンストラクターを提供します。

コンストラクターにはデフォルトでサブクラスのパラメーターを持たない super( ) があり、 super( ) はパラメーターを受け取りません。パラメーターを含むコンストラクターを親クラスに記述すると、コンパイラーはパラメーターのないコンストラクターを親クラスに提供しなくなり、コンパイル時にエラーが報告されます。

サブクラス構築メソッドでは、基本クラスの構築に関するコードは記述されませんが、サブクラス オブジェクトを構築する際には、最初に基本クラスの構築メソッドが実行され、次にサブクラスの構築メソッドが実行されます。サブクラス オブジェクトのメンバーは、基本クラスから継承される部分と、サブクラスによって新たに追加される部分の 2 つの部分があります。父と息子には、最初に父、次に息子が必要であるため、サブクラス オブジェクトを構築するときは、まず基本クラスの構築メソッドを呼び出して、基本クラスから継承されたメンバーの構築を完了してから、サブクラス自体の構築メソッドを呼び出します。これにより、サブクラスの新しく追加されたメンバーが完全に初期化されます。

知らせ:

1. 親クラスが引数なしまたはデフォルトの構築メソッドを明示的に定義している場合、デフォルトでサブクラス構築メソッドの最初の行に暗黙的な super() 呼び出しが存在します。つまり、基本クラス構築メソッドが呼び出されます。

2. 親クラス構築メソッドにパラメータがある場合、ユーザーはサブクラスの構築メソッドを明示的に定義し、サブクラス構築メソッドで適切な親クラス構築メソッド呼び出しを選択する必要があります。そうしないとコンパイルが失敗します。

3.サブクラス構築メソッドで、 super(...) が親クラス構築を呼び出す場合、それがサブクラス コンストラクターの最初のステートメントである必要があります

4. super(...) はサブクラス コンストラクター内で 1 回のみ出現でき、これと同時に出現することはできません。

7.スーパーとこれ

super と this は両方ともメンバー メソッドで使用できます。メンバー変数と他のメンバー関数の呼び出しは、構築メソッドの最初のステートメントとして使用できます。では、それらの違いは何でしょうか?

【同点】

1. どちらも Java のキーワードです

2.クラスの非静的メソッドでのみ、非静的メンバーのメソッドおよびフィールドにアクセスするために使用できます。

3.構築メソッドで呼び出される場合、構築メソッドの最初のステートメントである必要があり、同時に存在することはできません。

【違い】

1. これは現在のオブジェクトの参照です。現在のオブジェクトはインスタンス メソッドを呼び出すオブジェクトです。super はサブクラス オブジェクト内の親クラスから継承された一部のメンバーの参照に相当します。

2. 非静的メンバー メソッドでは、this はこのクラスのメソッドとプロパティにアクセスするために使用され、super は親クラスから継承されたメソッドとプロパティにアクセスするために使用されます。

3. 構築メソッド内: this(...) はこのクラスの構築メソッドの呼び出しに使用され、super(...) は親クラスの構築メソッドの呼び出しに使用されます。この 2 つの呼び出しは構築メソッド内に出現できません。同時に

4. 構築メソッド内に super(...) 呼び出しが必要で、ユーザーが書かないとコンパイラが増えるが、this(...) はユーザーが書かないと存在しない

8. 再び初期化について話します

先ほど話したコード ブロックを覚えていますか? いくつかの重要なコード ブロック (サンプル コード ブロックと静的コード ブロック) を簡単に確認してみましょう。継承関係がない場合の実行順序。

コードの実行結果を分析します。

class Base {
    static {
        System.out.println("父类静态代码块");
    }
    {
        System.out.println("父类实例代码块");
    }
    public Base() {
        System.out.println("父类构造函数");
    }
}
class Derived extends Base {
    static {
        System.out.println("子类静态代码块");
    }
    {
        System.out.println("子类实例代码块");
    }
    public Derived() {
        super();
        System.out.println("子类构造函数");
    }
}
public class Test {
    public static void main(String[] args) {
        Derived derived = new Derived();
        System.out.println("======================");
        Derived derived2 = new Derived();
    }
}

操作結果: 

 実行結果を分析すると、次の結論が導き出されます。

  1. 親クラスの静的コード ブロックは、子クラスの静的コード ブロックよりも前に実行され、最も早い実行になります。
  2. 親クラスのインスタンスのコード ブロックと親クラスの構築メソッドはすぐに実行されます。
  3. サブクラスのインスタンス コード ブロックとサブクラスの構築メソッドは即座に実行されます。
  4. サブクラス オブジェクトが 2 回目にインスタンス化されると、親クラスとサブクラスの静的コード ブロックは実行されなくなります。

1.静的コード ブロックは、クラスの読み込みフェーズ中に最初に 1 回だけ実行されます。

2. オブジェクトが作成されると、インスタンス コード ブロックが実行され、インスタンス コード ブロックの実行後、最終的な構築メソッドが実行されます。

9. 保護されたキーワード

クラスとオブジェクトの章では、カプセル化機能を実現するために、Java にアクセス修飾子が導入されています。これは主に、クラスまたはクラスのメンバーがクラス外または他のパッケージ内でアクセスできるかどうかを制限します。

いいえ 範囲 プライベート 保護された デフォルト 公共
1 同じパッケージ内の同じクラス
2 同じパッケージ内の異なるクラス
3 異なるパッケージ内のサブクラス
4 異なるパッケージ内の非サブクラス

保護されている場合、異なるパッケージのサブクラスにアクセスできますが、親クラスの保護されたメンバーには、異なるパッケージ内の他のクラスで直接アクセスできないため、次のプログラムは正しく実行されません。

 親クラス内の保護された変更されたメンバーには、別のパッケージ サブクラスから直接アクセスできます。正しいアクセス方法は次のとおりです。

 注: 親クラスのプライベート メンバー変数にサブクラスで直接アクセスすることはできませんが、サブクラスにも継承されます。

 いつどれを使うべきですか?

  1. 私たちは、クラスが可能な限り「カプセル化」されること、つまり、内部実装の詳細を隠し、必要な情報のみをクラスの呼び出し元に公開することを望んでいます。
  2. したがって、使用する際には、例えばプライベートを使用できるメソッドであればパブリックを使用しないなど、可能な限り厳しいアクセス権限を使用する必要があります。
  3. また、すべてのフィールドをプライベートに設定し、すべてのメソッドをパブリックに設定するという単純かつ大雑把な方法もありますが、これはアクセス権の乱用ですので、学生がコードを記述する際にはよく考えて、 が提供するフィールド メソッドを使用してください。このクラスは「誰」が使用するためのものです (クラス自体が使用するのか、クラスの呼び出し元が使用するのか、それともサブクラスが使用するのか)

10. 継承方法

実生活では、物事間の関係は次のように非常に複雑、柔軟、そして多様です。

ただし、Java では次の継承メソッドのみがサポートされています。

 注: 多重継承は Java ではサポートされていません。

  1.  私たちが作成するクラスは現実のものの抽象化であることを常に念頭に置いてください。私たちが実際に会社で遭遇するプロジェクトは、ビジネスにおいてはより複雑であることが多く、一連の複雑な概念が含まれる場合があり、コードを使用して表現する必要があるため、実際、プロジェクトには多くのクラスが記述され、クラス間の関係はさらに複雑になります。
  2. ただし、そうであっても、クラス間の継承レベルが複雑になりすぎることは望ましくありません。一般に、継承レベルが 3 つを超えることは望ましくありません。継承レベルが多すぎる場合は、コードのリファクタリングを検討する必要があります。
  3. 構文的に継承を制限したい場合は、final キーワードを使用できます。

11.最後のキーワード

最後のキーは、変数、メンバー メソッド、クラスを変更するために使用できます。

1. 定数を表す変更された変数またはフィールド (つまり、変更できません)

2. 変更されたクラス: このクラスが継承できないことを示します

3. 変更されたメソッド: メソッドをオーバーライドできないことを示します (後で説明します)。

12 継承と構成

継承と同様に、合成もクラス間の関係を表現する方法であり、コードの再利用という効果も得られます。合成には特別な構文 ( extends などのキーワード) は必要なく、あるクラスのインスタンスを別のクラスのフィールドとして使用するだけです。

継承とは、オブジェクト間に is-a 関係があることを意味します。たとえば、犬は動物であり、猫は動物です。

組み合わせとは、オブジェクト間の関係を意味します。例: 車

class Student {

}
class Teacher {

}
class School {
    public Student[] students; //默认是null
    public Teacher[] teachers;
    public int a;

    public School() {
        this.students = new Student[10];
        this.teachers = new Teacher[10];
        this.a = 10;
    }
}

合成と継承の両方でコードの再利用を実現できます。継承と合成のどちらを使用するかは、アプリケーションのシナリオによって異なります。一般的には、可能な限り合成を使用することをお勧めします。

注: 合成はオブジェクト指向の機能ではありません。

おすすめ

転載: blog.csdn.net/m0_73648729/article/details/132079605