13

インスタンス変数はいつ初期化されますか?コンストラクターブロックが完了した後ですか、それとも前ですか?

この例を考えてみましょう。

public abstract class Parent {

 public Parent(){
   System.out.println("Parent Constructor");
   init();
 }

 public void init(){
   System.out.println("parent Init()");
 }
}

public class Child extends Parent {

private Integer attribute1;
private Integer attribute2 = null;

public Child(){
    super();
    System.out.println("Child Constructor");
}

public void init(){
    System.out.println("Child init()");
    super.init();
    attribute1 = new Integer(100);
    attribute2 = new Integer(200);
}

public void print(){
    System.out.println("attribute 1 : " +attribute1);
    System.out.println("attribute 2 : " +attribute2);
}
}

public class Tester {

public static void main(String[] args) {
    Parent c = new Child();
    ((Child)c).print();
    
}
}

出力:

親コンストラクタ

子init()

親Init()

子コンストラクター

属性1:100

属性2:null


  1. アトリビューション1と2のメモリはいつヒープに割り当てられますか?

  2. 属性2がNULLである理由を知りたいですか?

  3. 設計上の欠陥はありますか?

4

3 に答える 3

13

アトリビュート1と2のメモリがヒープに割り当てられると?

オブジェクト全体のメモリは、コンストラクターが入力さnewれる前に、オペレーターが呼び出されたときに割り当てられます。java.lang.Objectの個々のIntegerインスタンスにinitメモリが割り当てられますが、個々のプロパティにメモリが割り当てられる意味はありません。オブジェクト全体のみです。

属性2がNULLである理由を知りたいですか?

このinitメソッドはスーパーコンストラクターで呼び出されるため、attribute2が割り当てられnew Integer(200)ます。次に、サブクラスコンストラクターが呼び出され、ソースコードに表示される順序でプロパティ初期化子が適用されます。この行

private Integer attribute2 = null;

に割り当てられた値を上書きしinit()ますnull

に通話を追加する場合

 System.out.println("attribute 2 : " +attribute2);

呼び出した直後にsuper();、これが明らかになります。

設計上の欠陥はありますか?

基本クラスの初期化が完了する前にサブクラスメソッドを呼び出すのは危険です。サブクラスは、それ自体の不変条件を保護するためにその基本クラスの不変条件に依存する場合があり、基本クラスのコンストラクターが完了していない場合、その不変条件は保持されない場合があります。

initこれはまた、コンストラクターが入力されるとC ++がvtableポインターを書き換えるため、基本クラスからの呼び出しが基本クラスのバージョンを呼び出すことを期待するC++プログラマーなどを混乱させる可能性があります。

厄介な詳細については、 Java言語仕様を参照してください。

于 2012-09-06T21:35:54.063 に答える
2

ここで提供される回答とリンクを消費した後、私のダイジェスト観察があります:


フローは次のとおりです。

  1. 子クラスコンストラクターを入力します。子(){ ... }

  2. 明示的なsuper()を呼び出します[親クラスコンストラクターを呼び出します]。

  3. Parent(){...}クラスコンストラクターを入力します

  4. 暗黙のsuper()を呼び出す[オブジェクトクラスコンストラクターを呼び出す]

  5. Object(){}を入力します(スーパーコンストラクターの呼び出しはありません)

  6. スーパークラスコンストラクターの再帰呼び出しはここで終了します。

  7. Objectクラスコンストラクターの戻り値

  8. 現在、Parentクラスコンストラクターで...Parentクラスのインスタンス初期化子とインスタンス変数初期化子が実行されます。

  9. 残りのParentクラスコンストラクターが実行され、

  10. 現在、Childクラスコンストラクターにあります。Childクラスのインスタンス初期化子とインスタンス変数初期化子が実行されます。

  11. 次に、残りの子クラスコンストラクターが実行され、オブジェクトの初期化プロセスが終了します。


attribute2がNULLだった理由は

  1. attribute2には、ステップ9で値200が割り当てられます。
  2. ただし、ステップ10でNULLにオーバーライドされます

設計上の欠陥はありますか?

Fabian Barneyが言及しているように:::::サブクラスによってオーバーライドされる可能性のあるコンストラクター内のメソッドを呼び出すことは、一般的に悪い習慣です。

アトリビュート1と2のメモリがヒープに割り当てられると? まだ理解しています。ポインタに感謝します。

マイクとファビアンに感謝します

于 2012-09-06T22:37:14.990 に答える
1

初期化のフローを示す以下のコードとそのコンソール出力を参照してください。'オブジェクトの構築中'親クラスのメソッドとプロパティはまったく使用されない(呼び出されない)ことに注意してください。

コード

class Parent{
   static{ System.out.println("Static Parent initialization"); }

   int i=1234;
   {  System.out.println("Parent's instance initializer running");
      print("executed from Parent's instance initializer");
      update(1);
   }

   Parent() {
      System.out.println("Parent's constructor running");
      print("executed from Parent's constructor");
      update(2);
   }

   void print(String note) { System.out.println("never executed"); }
   void update(int newI){ System.out.println("never executed"); }
}

class Child extends Parent{
   static{ System.out.println("Static Child initialization"); }

   int i = 3;
   {System.out.println("Child's instance initializer; i=" + i); }

   Child(){super(); i=4; System.out.println("Child's constructor running. Setting i to 4");}

   void print(String note) { System.out.println("print(): '"+note.toUpperCase()+"' ; i="+i); }
   void update(int newI){i=newI;System.out.println("update("+newI+"): After update i="+i);}
}

class Context {
   public static void main(String[] args) {
      Parent parent = new Child();
      System.out.println("In main: child's i="+((Child)parent).i);
      System.out.println("In main: parent's i=" +parent.i);
   }
}

出力

静的な親の初期化

静的な子の初期化

実行中の親のインスタンス初期化子

print():'親のインスタンス初期化子から実行されました'; i = 0

update(1):更新後i = 1

実行中の親のコンストラクター

print():'親のコンストラクターから実行されました'; i = 1

update(2):更新後i = 2

子のインスタンス初期化子。i = 3

実行中の子のコンストラクター。iを4に設定

主に:子供のi = 4

主に:親のi = 1234

于 2021-06-17T12:11:16.717 に答える