Animalクラスがあると想像してください。
public class Animal {
String name;
int age;
}
Catと の 2 つの子クラスを宣言できますDog。これは、キーワードextends を使用して行われます。
public class Cat extends Animal {
}
public class Dog extends Animal {
}
これは将来役立つかもしれません。たとえば、ネズミを捕まえるタスクがある場合、 Cat プログラム内でオブジェクトを作成します。タスクが棒を追いかけることである場合は、オブジェクトを使用します Dog 。そして、動物病院をシミュレートするプログラムを作成すると、クラスと連携して動作しますAnimal (したがって、猫と犬の両方を治療できるようになります)。オブジェクトが作成されるとき、その基本クラスのコンストラクターが最初に呼び出されるという ことを覚えておくことが非常に重要です。コンストラクターが終了した後でのみ、プログラムは作成中のオブジェクトに対応するクラスのコンストラクターを実行します。つまり、Catオブジェクトを作成するときは、コンストラクAnimalターが最初に実行され、その後でのみコンストラクターが実行されます。Catコンストラクターが実行されました。これを確認するには、コンソール出力をCatおよびAnimalコンストラクターに追加します。
public class Animal {
public Animal() {
System.out.println("Animal constructor executed");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat constructor executed!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
コンソール出力: Animal コンストラクターが実行されました Cat コンストラクターが実行されました! 確かに、そのように機能します!なぜ?理由の 1 つは、2 つのクラス間で共有されるフィールドの重複を避けるためです。たとえば、すべての動物には心臓と脳がありますが、すべての動物に尻尾があるわけではありません。すべての動物に共通する脳フィールドと心臓Animalフィールドを親クラスで宣言し、尾フィールドをサブクラスで宣言できますCat。。次に、3 つのフィールドすべての引数を取るクラス コンストラクターを宣言しますCat。
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
注:クラスCatに Brain フィールドと Heart フィールドがない場合でも、コンストラクターは正しく動作します。これらのフィールドは基本クラスから「継承」されますAnimal。継承クラスは基本クラスのフィールドにアクセスできるため、これらのフィールドはクラス内で表示されますCat。結果として、Catクラス内でこれらのフィールドを複製する必要はありません。クラスから持ち帰ることができますAnimal。さらに、子クラス コンストラクターで基本クラス コンストラクターを明示的に呼び出すことができます。基本クラスは「スーパークラス」とも呼ばれます。Java が基底クラスを示すためにキーワードsuperを使用するのはこのためです。前の例では
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
親クラスの各フィールドを個別に割り当てました。実際にはこれを行う必要はありません。親クラスのコンストラクターを呼び出して、必要な引数を渡すだけで十分です。
public class Animal {
String brain;
String heart;
public Animal(String brain, String heart) {
this.brain = brain;
this.heart = heart;
}
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
this.tail = tail;
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
コンストラクターではCat、コンストラクターを呼び出してAnimal2 つのフィールドを渡しました。明示的に初期化するフィールドは 1 つだけでした。tailは にありませんAnimal。オブジェクトの作成時に最初に親クラスのコンストラクターが呼び出されることを説明したことを覚えていますか? そのため、super() は常にコンストラクターの最初に置く必要があります。 そうしないと、コンストラクターのロジックに違反し、プログラムでエラーが生成されます。
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.tail = tail;
super(brain, heart);// Error!
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
コンパイラは、子クラスのオブジェクトが作成されるときに、基本クラスのコンストラクターが最初に呼び出されることを認識しています。この動作を手動で変更しようとしても、コンパイラはそれを許可しません。
オブジェクトの作成方法
Animal以前に、基本クラスと親クラス、およびを使用した例を見てきましたCat。これら 2 つのクラスを例として使用して、オブジェクトの作成と変数の初期化のプロセスを見ていきます。静的変数とインスタンス (非静的) 変数があることがわかっています。また、Animal基本クラスには変数があり、Cat子クラスには独自の変数があることもわかります。Animalわかりやすくするために、と のクラスに静的変数をそれぞれ 1 つ追加しますCat。クラスのanimalCount変数は地球上の動物種の総数を表し、catCountはAnimal変数は猫の種類の数を表します。さらに、両方のクラスのすべての非静的変数に開始値を割り当てます (その後、コンストラクターで変更されます)。
public class Animal {
String brain = "Initial value of brain in the Animal class";
String heart = "Initial value of heart in the Animal class";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("Animal base class constructor is running");
System.out.println("Have the variables of the Animal class already been initialized?");
System.out.println("Current value of static variable animalCount = " + animalCount);
System.out.println("Current value of brain in the Animal class = " + this.brain);
System.out.println("Current value of heart in the Animal class = " + this.heart);
System.out.println("Have the variables of the Cat class already been initialized?");
System.out.println("Current value of static variable catCount = " + Cat.catCount);
this.brain = brain;
this.heart = heart;
System.out.println("Animal base class constructor is done!");
System.out.println("Current value of brain = " + this.brain);
System.out.println("Current value of heart = " + this.heart);
}
}
public class Cat extends Animal {
String tail = "Initial value of tail in the Cat class";
static int catCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("The cat class constructor has started (The Animal constructor already finished)");
System.out.println("Current value of static variable catCount = " + catCount);
System.out.println("Current value of tail = " + this.tail);
this.tail = tail;
System.out.println("Current value of tail = " + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
Catそこで、 を継承するクラス の新しいインスタンスを作成しますAnimal。何がどのような順序で起こっているかを確認するために、詳細なコンソール出力を追加しました。Catオブジェクトの作成 時に表示される内容は次のとおりです。Animal 基本クラスのコンストラクターが実行されています。 Animal クラスの変数はすでに初期化されていますか? 静的変数animalCountの現在値 = 7700000 Animalクラスのbrainの現在値 = Animalクラスのbrainの初期値 Animalクラスのheartの現在値 = Animalクラスのheartの初期値 Catクラスの変数がすでにある初期化されてる?静的変数 catCount の現在の値 = 37 Animal 基本クラスのコンストラクターが完成しました。Brain の現在値 = Brain 現在値 heart = Heart cat クラスのコンストラクタが開始されました (Animal コンストラクタは既に終了しています) 静的変数 catCount の現在値 = 37 tail の現在値 = Cat クラスの tail の初期値 tail の現在値 =しっぽ これで、新しいオブジェクトが作成されるときの変数の初期化とコンストラクターの呼び出しの順序が明確にわかります。
- 基本クラス( )の静的
Animal変数が初期化されます。この例では、Animalクラスの変数animalCountが7700000に設定されています。 -
子クラス( )の静的
Cat変数が初期化されます。注:私たちはまだ
Animalコンストラクター内にいますが、すでに以下が表示されています。Animal 基本クラスのコンストラクターが実行されています。
Animal クラスの変数はすでに初期化されていますか?
静的変数animalCountの現在値 = 7700000
Animalクラスのbrainの現在値 = Animalクラスのbrainの初期値 Animalクラスのheartの
現在値 = Animalクラスのheartの初期値 Catクラス
の変数がすでにある初期化されてる?
静的変数の現在の値 catCount = 37 -
次に、基本クラスの非静的変数が初期化されます。具体的には初期値を割り当て、コンストラクターで置き換えます。Animal コンストラクターはまだ完了していませんが、brain と heart の初期値はすでに割り当てられています。
Animal 基本クラスのコンストラクターが実行されています。
Animal クラスの変数はすでに初期化されていますか?
静的変数animalCountの現在値 = 7700000
Animalクラスのbrainの現在値 = Animalクラスのbrainの初期値
Animalクラスのheartの現在値 = Animalクラスのheartの初期値 -
基本クラスのコンストラクターが開始されます。
このステップが 4 番目であることはすでに確信しています。コンストラクターの最初の最初の 3 つのステップではAnimal、多くの変数にすでに値が割り当てられています。 -
子クラス( ) の非静的フィールド
Catが初期化されます。
これはCatコンストラクターが実行を開始する前に発生します。
実行を開始すると、tail 変数にはすでに値が設定されています。cat クラスのコンストラクターが開始されました (Animal コンストラクターはすでに終了しています) 静的変数 catCount の現在値 = 37 tail の現在値 = Cat クラスの tail の初期値
-
子クラスのコンストラクター
Catが呼び出されます。これが Java でオブジェクトを作成する様子です。
私たちは暗記学習があまり好きではないと言わざるを得ませんが、変数の初期化とコンストラクター呼び出しの順序を覚えておくのが最善です。
これにより、プログラムの流れと、特定の瞬間におけるオブジェクトの状態についての理解が大幅に深まります。
さらに、多くのクラスは継承を使用しません。この場合、基本クラスに関連する手順は適用されません。
|
さらに読む: |
|---|
GO TO FULL VERSION