web-dev-qa-db-ja.com

javaのマルチレベル継承におけるコンストラクター呼び出しの順序

//: c07:Sandwich.Java
// Order of constructor calls.
// package c07;
// import com.bruceeckel.simpletest.*;

import Java.util.*;

class Meal {
  Meal() { System.out.println("Meal()"); }
}

class Bread {
  Bread() { System.out.println("Bread()"); }
}

class Cheese {
  Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
  Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { System.out.println("Lunch()"); }
}

class PortableLunch extends Lunch {
  PortableLunch() { System.out.println("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
//  private static Test monitor = new Test();
  private Bread b = new Bread();
  private Cheese c = new Cheese();
  private Lettuce l = new Lettuce();
  public Sandwich() {
    System.out.println("Sandwich()");
  }
  public static void main(String[] args) {
    new Sandwich();
   /*
   monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });
    // */
  }
} ///:~

このコードの出力は

Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()  

クラスのフィールドは宣言された順序で作成されるので、なぜ

Bread()
Cheese()
Lettuce()

上記のリストの一番上に来ますか?

また、このコードで何をしようとしているのですか?

   monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });  

最初は無名のクラスだと思っていましたが、見た目は似ていません。文字列配列を初期化していますか? String変数の名前がないのはなぜですか?ここで使用されているプログラミング構造の名前を教えてください。

20
user13267

コンストラクタ:

_public Sandwich() {
    System.out.println("Sandwich()");
}
_

コンパイラによって次のように変換されます。

_public Sandwich() {
    super();   // Compiler adds it if it is not explicitly added by programmer
    // All the instance variable initialization is moved here by the compiler.
    b = new Bread();
    c = new Cheese();
    l = new Lettuce();

    System.out.println("Sandwich()");
}
_

したがって、コンストラクターの最初のステートメントは、スーパークラスコンストラクターのチェーンです。実際には、コンストラクターの最初のステートメントは、スーパークラスコンストラクターにつながっています。 super()により、最初にスーパークラスコンストラクターPortableLunchが呼び出され、そのスーパークラスコンストラクターへの呼び出しが再度チェーンされるのはそのためです。コンパイラによって追加されました(覚えていますか?)。

このコンストラクタ呼び出しの連鎖は、継承階層の最上位のクラスまで行われるため、最後にObjectクラスコンストラクタが呼び出されます。

これで、各スーパークラスコンストラクターが実行され、すべてのスーパークラスフィールドが初期化された後、直接のサブクラスコンストラクターがsuper()呼び出しの後で実行を開始します。そして最後に、Sandwitch()コンストラクターに戻り、_3_フィールドを初期化します。

したがって、基本的にフィールドは最後に初期化されるため、Sandwitch()が出力される直前の最後に出力されます。

インスタンスの詳細な説明については、JLS-§12.5-新しいクラスインスタンスの作成を参照してください。作成プロセス。


質問の2番目の部分については:

_monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });  
_

このコードは、無名配列を作成し、同時にいくつかの文字列リテラルを初期化しています。これは、名前付き配列を作成する方法と似ています。

_String[] arr = new String[] { "rohit", "jain" };
_
23
Rohit Jain

この例のオブジェクトは継承を使用しているため、コンストラクタのチェーンが呼び出されます。継承を使用する場合、別のクラスを継承するクラス(subtype)は、それが拡張するクラスのコンストラクタ(super type)。型階層が存在する場合、つまりチェーン内で複数のクラスが相互に拡張している場合、スーパーコンストラクターの呼び出しは、別のクラスから継承しない(オブジェクトを無視して)チェーン内の最初のクラスに伝播します。

サブタイプのスーパークラスコンストラクターは、スーパータイプのフィールドまたはメソッドに依存する可能性があるため、サブタイプのコンストラクターを実行する前に呼び出す必要があります。コンストラクターは、型階層を連鎖的に呼び出し、各コンストラクターが初期化されると、サブタイプがインスタンス化を開始します。

クラス型階層のスーパータイプコンストラクターが呼び出されると、サブタイプのコンストラクターでも必要になる場合があるため、サブタイプのフィールドが宣言されます。サブタイプのフィールドが宣言された後、サブタイプコンストラクターが実行されます。

サブタイプはスーパータイプで確立されたフィールドまたはメソッドに依存する可能性があるため、この順序が必要です。

Meal() (Top of Class Hierarchy, not including Object)
Lunch() (Extends Meal)
PortableLunch() (Extends Lunch)
Bread() (Field of lunch declared before constructor call)
Cheese() (Field of lunch declared before constructor call)
Lettuce() (Field of lunch declared before constructor call)
Sandwich() (Extends Portable Lunch)

これは、Javaでのオブジェクト作成の非常に優れた概要です。

5
Kevin Bowersox
new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    }

\上記は 無名配列宣言 です。この場合、サイズを指定する必要はありません。

インスタンス変数を初期化する前に、すべてのスーパークラスコンストラクターが最初に呼び出されます。つまり、

注文--->

  Object(), 
  all your superclass constructors,
  instance variables of this class in that order
1
PermGenError

最初にコンストラクタが呼び出され、次にそのインスタンス変数が Java言語仕様 で指定された順序に従って評価されます。それがあなたが得る理由です

Bread()
Cheese()
Lettuce()

Meal()
Lunch()
PortableLunch()

string []の初期化については、匿名クラスではないと考えると、単なるStringオブジェクトの作成であり、必ずしも変数に割り当てる必要はありません。

1
morgano