web-dev-qa-db-ja.com

Javaでコンストラクタコードを実行する前にフィールドが初期化されていますか?

誰でも次のプログラムの出力を説明できますか?コンストラクタはインスタンス変数の前に初期化されると思いました。そのため、出力が「XZYY」になると予想していました。

class X {
    Y b = new Y();

    X() {
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {
    Y y = new Y();

    Z() {
        System.out.print("Z");
    }

    public static void main(String[] args) {
        new Z();
    }
}
64
Praveen Kumar

初期化の正しい順序は次のとおりです。

  1. クラスが以前に初期化されていない場合は、静的変数初期化子および静的初期化ブロック(テキスト順)。
  2. 明示的または暗黙的に、コンストラクター内のsuper()呼び出し。
  3. インスタンス変数初期化子とインスタンス初期化ブロック、テキスト順。
  4. Super()の後のコンストラクタの残りの本体。

セクション= Java Virtual Machine Specification の2.17.5-6を参照してください。

91
user207421

クラスファイルの逆コンパイルバージョンを見ると

_class X {
    Y b;

    X() {
        b = new Y();
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {

    Y y;

    Z() {
        y = new Y();
        System.out.print("Z");
    }

    public static void main(String args[]) {
        new Z();
    }
}
_

インスタンス変数yがコンストラクター内で移動されていることがわかります。そのため、実行シーケンスは次のようになります。

  1. Zのコンストラクターを呼び出します
  2. Xのデフォルトコンストラクターをトリガーします
  3. Xコンストラクターの最初の行new Y()が呼び出されます。
  4. Yを印刷
  5. プリントX
  6. コンストラクターZ new Y()の最初の行を呼び出します
  7. 印刷Y
  8. プリントZ

すべてのインスタンス変数は、コンストラクターステートメントを使用して初期化されます。

56
Arun P Johny

コンストラクタを呼び出すと、インスタンス変数初期化子はコンストラクタの本体の前で実行されます。以下のプログラムの出力はどうだと思いますか?

public class Tester {
    private Tester internalInstance = new Tester();
    public Tester() throws Exception {
        throw new Exception("Boom");
    }
    public static void main(String[] args) {
        try {
            Tester b = new Tester();
            System.out.println("Eye-Opener!");
        } catch (Exception ex) {
            System.out.println("Exception catched");
        }
    }
}

Mainメソッドは、例外をスローするTesterコンストラクターを呼び出します。 catch節がこの例外をキャッチし、Exception catchedを出力すると予想される場合があります。しかし、実行しようとすると、そのようなことは何も行わず、StackOverflowErrorがスローされることがわかりました。

1

静的に関する誤解を明確にするために、次の小さなコードを参照します。

public class Foo {
  { System.out.println("Instance Block 1"); }
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  { System.out.println("Instance Block 2"); }
  static { System.out.println("Static Block 2 (Weird!!)"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

驚いたことに、出力は次のとおりです。

Static Block 1
Instance Block 1
Instance Block 2
Constructor
Static Block 2 (Weird!!)
In Main
Instance Block 1
Instance Block 2
Constructor

after2つのインスタンスstatic {}と呼ばれる{}があることに注意してください。これは、コンストラクターが中間で呼び出され、コンストラクターが最初に呼び出されたときに実行順序を挿入するためです。

この答えに取り組んでいたときにこれを発見しました- https://stackoverflow.com/a/30837385/7441

基本的にこれが起こるのを観察します:

  1. オブジェクトが初めて初期化されるとき、発生順序に基づいて混在する静的およびインスタンスの両方の初期化のために現在のオブジェクトを初期化します

  2. 次のすべての初期化では、静的な初期化がすでに行われているため、インスタンスの初期化は発生順にのみ実行してください。

継承の組み合わせ、およびsuperへの明示的呼び出しと暗黙的呼び出しの両方がどのようにこれに影響するかを調査する必要があり、結果を更新します。静的な初期化で問題が発生したことを除けば、提供された他の回答と似ている可能性があります。

1
YoYo