new
、すべての準備が整います:) ここでは、次のような書き込みを行うときにコンピュータと Java マシンの内部で何が起こるかについて説明します。
Cat cat = new Cat();
これについては以前にもお話しましたが、念のため思い出してください。
- まず、オブジェクトを保存するためのメモリが割り当てられます。
- 次に、Java マシンはオブジェクトへの参照を作成します (この場合、参照は Cat cat です)。
- 最後に、変数が初期化され、コンストラクターが呼び出されます (このプロセスについては後で詳しく説明します)。
public class Vehicle {
public static int vehicleCounter = 0;
private String description = "Vehicle";
public Vehicle() {
}
public String getDescription() {
return description;
}
}
public class Truck extends Vehicle {
private static int truckCounter = 0;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
}
このTruck
クラスは、年式、モデル、最高速度を表すフィールドを持つトラックの実装です。ここで、そのようなオブジェクトを 1 つ作成したいと思います。
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
}
Java マシンにとって、プロセスは次のようになります。
-
最初に、クラスの静的変数が
Vehicle
初期化されます。はい、私が言ったのはVehicle
クラスであり、 ではありませんTruck
。静的変数はコンストラクターが呼び出される前に初期化され、これは親クラスで開始されます。これを検証してみましょう。vehicleCounter
クラスのフィールドVehicle
を 10 に設定し、 とVehicle
の両方のTruck
コンストラクターで表示しようとします。public class Vehicle { public static int vehicleCounter = 10; private String description = "Vehicle"; public Vehicle() { System.out.println(vehicleCounter); } public String getDescription() { return description; } } public class Truck extends Vehicle { private static int truckCount = 0; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(vehicleCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCount++; } }
が表示される
Truck
ときにトラックのフィールドがまだ初期化されていないことを確認するために、コンストラクターの先頭に println ステートメントを意図的に配置しています。vehicleCounter
そして結果は次のとおりです。
10 10
-
親クラスの静的変数が初期化された後、子クラスの静的変数が初期化されます。私たちの場合、これはクラス
truckCounter
のフィールドですTruck
。他のフィールドが初期化される前に、コンストラクター
truckCounter
内のの値を表示する別の実験を行ってみましょう。Truck
public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(truckCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }
ご覧のとおり、コンストラクターの開始時に、値 10 がすでに静的変数に割り当てられています
Truck
。 -
まだコンストラクターの出番ではありません!変数の初期化が続行されます。 親クラスの非静的変数は 3 番目に初期化されます。 ご覧のとおり、継承によりオブジェクトの作成プロセスが大幅に複雑になりますが、それに関してできることは何もありません。プログラミングでいくつかのことを覚えておく必要があるだけです :)
実験として、クラス
description
内の変数に初期値を代入しVehicle
、コンストラクター内でそれを変更できます。public class Vehicle { public static int vehicleCounter = 10; private String description = "Initial value of the description field"; public Vehicle() { System.out.println(description); description = "Vehicle"; System.out.println(description); } public String getDescription() { return description; } }
main()
トラックを作成するメソッドを実行してみましょう。public class Main { public static void main(String[] args) throws IOException { Truck truck = new Truck(2017, "Scania S 500 4x2", 220); } }
次の結果が得られます。
Initial value of the description field Vehicle
これは、
Vehicle
コンストラクターの開始時にdescription
フィールドにすでに値が割り当てられていることを証明します。 -
いよいよコンストラクターの出番です! より正確には、基底クラスのコンストラクターの出番です。これは、オブジェクト作成プロセスの 4 番目のステップで呼び出されます。
これもかなり簡単に検証できます。2 行をコンソールに出力してみましょう。1 行目は
Vehicle
基本クラスのコンストラクター内で、2 行目はTruck
コンストラクター内です。内側の行がVehicle
最初に表示されていることを確認する必要があります。public Vehicle() { System.out.println("Hello from the Vehicle constructor!"); } public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Hello from the Truck constructor!"); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; }
main()
メソッドを実行して結果を見てみましょう。Hello from the Vehicle constructor! Hello from the Truck constructor!
素晴らしい。つまり、私たちは間違っていません:)次に進みましょう。
-
次に、子クラス、つまり私たちのクラスの非静的フィールドを初期化します
Truck
。インスタンス化されるクラス内のフィールドは、5 番目のステップまで初期化されません。驚くべきことですが、本当です :) もう一度、親クラスの場合と同じように簡単なチェックを行います。変数に初期値を設定し、コンストラクターmaxSpeed
内で、Truck
コンストラクターの開始前に値が割り当てられていることを確認します。public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed = 150; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Initial value of maxSpeed = " + this.maxSpeed); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }
コンソール出力:
Initial value of maxSpeed = 150
ご覧のとおり、 コンストラクターが開始されると
Truck
、maxSpeed
はすでに 150 に等しくなります。 -
子クラスのコンストラクターが
Truck
呼び出されます。そして最後に、この時点で初めて、インスタンス化しているクラスのコンストラクターが呼び出されます。
6 番目のステップでのみ、トラックに引数として渡す値がフィールドに割り当てられます。
ご覧のとおり、トラックの「構築」、つまりオブジェクトの作成プロセスは簡単ではありません。しかし、それを最小の部分に分割したようです:)
GO TO FULL VERSION