40

次のコードを実行すると:

public class Test {

  Test(){
    System.out.println("1");
  }

  {
    System.out.println("2");
  }

  static {
    System.out.println("3");
  }

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

次の順序で出力が得られることを期待しています。

1
2
3

しかし、私が得たのは逆の順序です:

3
2
1

逆順で出力される理由を誰か説明できますか?

================

また、複数のインスタンスを作成すると、次のようになりますTest

new Test();
new Test();
new Test();
new Test();

static ブロックは初回のみ実行されます。

4

9 に答える 9

62

それはすべて、初期化ステートメントの実行順序に依存します。あなたのテストは、この順序が次のとおりであることを示しています。

  1. 静的初期化ブロック
  2. インスタンス初期化ブロック
  3. コンストラクター

編集

コメントありがとうございます。これで、JVM 仕様の適切な部分を引用できます。これが、詳細な初期化手順です。

于 2011-11-18T16:44:39.607 に答える
31

3 - 静的初期化子です。クラスがロードされると、最初に実行されます。

2 - イニシャライザ ブロックです。Java コンパイラは実際にこれを各コンストラクタにコピーするため、必要に応じてコンストラクタ間で初期化を共有できます。ほとんど使われません。

1 - (3) と (2) の後、オブジェクトを構築するときに実行されます。

詳細はこちら

于 2011-11-18T16:45:53.293 に答える
18

静的ブロックが最初に実行されます。

そして、インスタンスインスタンスの初期化ブロック

インスタンス初期化子については JLS を参照してください

{

// sop ステートメント

}

コンストラクターと同様に、インスタンスの初期化ブロック内に return ステートメントを含めることはできません。

于 2011-11-18T16:42:33.263 に答える
5
Test(){System.out.println("1");}

    {System.out.println("2");}

    static{System.out.println("3");}

静的なものが最初に実行され {System.out.println("2");}、関数の一部ではありません。そのスコープのために最初に Test(){System.out.println("1");}呼び出され、他の2つが最初に呼び出されるため最後に呼び出されます

于 2011-11-18T16:45:41.140 に答える
5

まず、クラスが JVM にロードされ、クラスの初期化が行われます。このステップでは、静的ブロックが実行されます。"{...}" は "static{...}" と同等の構文です。コードにはすでに「static{...}」ブロックがあるため、「{...}」が追加されます。そのため、2 の前に 3 が印刷されています。

次に、クラスがロードされると、java.exe (コマンド ラインから実行したものと想定) がメイン メソッドを見つけて実行します。メインの静的メソッドは、コンストラクターが呼び出されるインスタンスを初期化するため、最後に「1」が出力されます。

于 2011-11-18T16:50:03.757 に答える
4

ここで ASM によってバイトコードのようなコードを取得しました。

これは、この機会にオブジェクトが作成されたときに何が起こったのかを説明して、あなたの質問に答えることができると思います.

public class Test {
static <clinit>() : void
GETSTATIC System.out : PrintStream
LDC "3"
INVOKEVIRTUAL PrintStream.println(String) : void
RETURN


<init>() : void
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
GETSTATIC System.out : PrintStream
LDC "2"
INVOKEVIRTUAL PrintStream.println(String) : void
GETSTATIC System.out : PrintStream
LDC "1"
INVOKEVIRTUAL PrintStream.println(String) : void
RETURN

public static main(String[]) : void
NEW Test
INVOKESPECIAL Test.<init>() : void
RETURN
}

LDC "3" 「clinit」にあることがわかり ます。これはクラス初期化子です。

通常、オブジェクトの存続期間は、クラスのロード -> クラスのリンク -> クラスの初期化 -> オブジェクトのインスタンス化 -> 使用 -> GC です。そのため、最初に 3 が表示されます。また、これはオブジェクト レベルではなくクラス レベルであるため、クラス タイプが 1 回ロードされると 1 回表示されます。詳細については、Java2 仮想マシンの内部 : タイプのライフタイムを参照してください。

LDC "2"コンストラクター`LDC "1"の "init" にあります。

この順序になっている理由: コンストラクターは、最初にクラスの {} でスーパー コンストラクターやコードなどの暗黙の命令を実行し、次にコンストラクターの明示的なコードを実行します。

これは、コンパイラが Java ファイルに対して行うことです。

于 2011-11-18T18:02:25.840 に答える
4

Complete Explanation

The order of execution is like,

  1. static block
  2. instance block
  3. constructor

Explanation

Static block will always be called only once in the very beginning whenever the class is accessed by any means, in your case which is when you run the program. (That is what static block is meant for). It does not depend on instances therefore not called again when new instances are created.

Then the Instance initialization block will be called for each instance created and after that the constructor for each instance created. Because both of them can be used to instantiate the instance.

Is instance initialization block actually called before constructor?

After compilation the code will become,

public class Test {

  Test(){
    super();
    System.out.println("2");
    System.out.println("1");
  }


  static {
    System.out.println("3");
  }

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

So you can see, the statement written in instance block itself becomes part of the constructor. Therefore it is executed before the statements already written in the constructor.

From this documentation

The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.

于 2011-11-21T13:19:05.000 に答える
4

JVM 内でクラスが最初に初期化されるときに (つまり、 が呼び出されるstatic{}前であっても) コードが実行されるため、インスタンスは、インスタンスが構築される前に最初に初期化されるときに呼び出され、すべての処理が完了した後にコンストラクターが呼び出されます。main(){}

于 2011-11-18T16:45:16.440 に答える
4

3が明示的に一度だけ印刷される理由を誰かが述べたようには見えません。したがって、これは最初に印刷される理由に関連していると付け加えておきます。

静的に定義されたコードは、クラスの特定のインスタンスから分離されているというフラグが立てられます。一般に、静的に定義されたコードは、まったくクラスではないと見なすことができます (もちろん、スコープを考慮すると、そのステートメントにはいくつかの無効性があります)。したがって、上記のように、インスタンスが構築されたときに呼び出されないTest()ため、クラスがロードされるとそのコードが実行されるため、コンストラクターを複数回呼び出しても、静的コードが実行されることはありません。

2を含む角かっこで囲まれたコードは、クラス内のすべてのコンストラクターに対する前提条件のようなものであるため、上記で開始したように、コンストラクトの先頭に追加されます。Test のコンストラクターで何が起こるかはわかりませんが、すべてが2を出力することで開始されることが保証されています。したがって、これは特定のコンストラクターの前に発生し、(ny) コンストラクターが呼び出されるたびに呼び出されます。

于 2011-11-22T17:51:46.750 に答える