CodeGym /Java Blog /ランダム /オブジェクト作成時のアクションのシーケンス
John Squirrels
レベル 41
San Francisco

オブジェクト作成時のアクションのシーケンス

ランダム グループに公開済み
やあ!今日のレッスンは非常に...ええと...多面的になります:)幅広いトピックを取り上げるという意味ですが、それらはすべてオブジェクト作成プロセスに関連します。 オブジェクト作成時のアクションのシーケンス - 1コンストラクターがどのように呼び出されるのか、フィールド (静的フィールドを含む) がどのようにどのような順序で初期化されるのかなど、最初から最後まで分析します。この記事で説明したいくつかの点については以前に触れたので、ざっと目を通すことができます。基本クラスのコンストラクターのマテリアル。まず、オブジェクトがどのように作成されるかを思い出してください。開発者の観点からこのプロセスがどのように見えるかをよく覚えているでしょう。開発者はクラスを作成し、 を書き込みnew、すべての準備が整います:) ここでは、次のような書き込みを行うときにコンピュータと Java マシンの内部で何が起こるかについて説明します。

Cat cat = new Cat();
これについては以前にもお話しましたが、念のため思い出してください。
  • まず、オブジェクトを保存するためのメモリが割り当てられます。
  • 次に、Java マシンはオブジェクトへの参照を作成します (この場合、参照は Cat cat です)。
  • 最後に、変数が初期化され、コンストラクターが呼び出されます (このプロセスについては後で詳しく説明します)。
さらに、オブジェクトのライフ サイクルに関するレッスンで、オブジェクトへの参照が少なくとも 1 つある限り、オブジェクトは存続することを覚えているでしょう。何も残っていない場合、オブジェクトはガベージ コレクターの餌食になります。 オブジェクト作成時のアクションのシーケンス - 2最初の 2 つの点については、特別な疑問は生じません。メモリ割り当ては単純なプロセスであり、考えられる結果は 2 つだけです。メモリがあるかないかのどちらかです :) そして、リンクの作成は珍しいことではありません。しかし、3 番目の点は、厳密な順序で実行される一連の操作全体を表します。私は詰め込みテストは好きではありませんが、このプロセスをよく理解し、この一連の操作を覚える必要があります。。前回のレッスンでオブジェクトの作成プロセスについて説明したとき、継承についてはまだ何も知らなかったため、いくつかのことを説明するのが困難でした。これで、かなり多くのことがわかったので、ようやくこの質問を完全に検討することができます :) 3 番目のポイントは、「最後に、変数が初期化され、コンストラクターが呼び出されます。」とありますが、これはすべてどのような順序で行われるのでしょうか? 理解を深めるために、親と子という 2 つの非常に単純なクラスを作成してみましょう。

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 マシンにとって、プロセスは次のようになります。
  1. 最初に、クラスの静的変数が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
    
  2. 親クラスの静的変数が初期化された後、子クラスの静的変数が初期化されます。私たちの場合、これはクラス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. まだコンストラクターの出番ではありません!変数の初期化が続行されます。 親クラスの非静的変数は 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. いよいよコンストラクターの出番です! より正確には、基底クラスのコンストラクターの出番です。これは、オブジェクト作成プロセスの 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! 
    

    素晴らしい。つまり、私たちは間違っていません:)次に進みましょう。

  5. 次に、子クラス、つまり私たちのクラスの非静的フィールドを初期化します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 に等しくなります。

  6. 子クラスのコンストラクターがTruck呼び出されます。

    そして最後に、この時点で初めて、インスタンス化しているクラスのコンストラクターが呼び出されます。

    6 番目のステップでのみ、トラックに引数として渡す値がフィールドに割り当てられます。

    ご覧のとおり、トラックの「構築」、つまりオブジェクトの作成プロセスは簡単ではありません。しかし、それを最小の部分に分割したようです:)

オブジェクト作成時のアクションのシーケンス - 3 このプロセスをよく理解することがなぜそれほど重要なのでしょうか? 「内部で」何が起こっているのかを正確に知らなかった場合、普通のオブジェクトを作成した結果がどれほど予期せぬものになるかを想像してみてください:) さて、コースに戻っていくつかのタスクを完了する時が来ました。頑張って、また会いましょう! :)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION