53

次のコードはnull1回出力されます。

class MyClass {
   private static MyClass myClass = new MyClass();
   private static final Object obj = new Object();
   public MyClass() {
      System.out.println(obj);
   }
   public static void main(String[] args) {}
}

コンストラクターが実行される前に静的オブジェクトが初期化されないのはなぜですか?

アップデート

このサンプルプログラムを注意せずにコピーしたところ、2つのオブジェクトフィールドについて話していると思っていましたが、最初のフィールドはMyClassフィールドであることがわかりました。:/

4

5 に答える 5

45

統計はソースコードで指定された順序で初期化されるためです。

これをチェックしてください:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static MyClass myClass2 = new MyClass();
  public MyClass() {
    System.out.println(myClass);
    System.out.println(myClass2);
  }
}

それは印刷されます:

null
null
myClassObject
null

編集

わかりました。もう少し明確にするために、これを引き出しましょう。

  1. スタティックは、ソースコードで宣言されている順序で1つずつ初期化されます。
  2. 最初の静的フィールドは残りの静的フィールドの前に初期化されるため、初期化中、残りの静的フィールドはnullまたはデフォルト値になります。
  3. 2番目のスタティックの開始中、最初のスタティックは正しいですが、残りはnullまたはデフォルトのままです。

それは明らかですか?

編集2

Varmanが指摘したように、初期化中はそれ自体への参照はnullになります。あなたがそれについて考えるならば、それは理にかなっています。

于 2010-03-30T18:43:23.047 に答える
27

これを別の方法で説明してみましょう...

これは、クラスを最初に参照するときに JVM が実行するシーケンスですMyClass

  1. バイトコードをメモリにロードします。
  2. 静的ストレージのメモリがクリアされます (バイナリ ゼロ)。
  3. クラスを初期化します。
    1. 各静的初期化子を表示順に実行します。これには、静的変数とstatic { ... }ブロックが含まれます。
    2. 次に、JVM はmyClass静的変数を の新しいインスタンスに初期化しますMyClass
    3. これが発生すると、JVMMyClassは がすでにロードされており (バイトコード) 、初期化の処理中であることに気付くため、初期化をスキップします。
    4. オブジェクトのヒープにメモリを割り当てます。
    5. コンストラクタを実行します。
    6. の値を出力しますobj(nullヒープおよびコンストラクターで初期化された変数の一部ではないため)。
    7. objコンストラクターが終了したら、 の新しいインスタンスに設定する次の静的イニシャライザーを実行しますObject
  4. クラスの初期化が完了しました。この時点から、すべてのコンストラクター呼び出しは、想定/期待どおりに動作します。つまり、インスタンスへの参照でobjはありません。nullObject

finalJava では、変数に値が 1 回割り当てられることを指定していることを思い出してください。値が割り当てられた後にコードが参照することを保証しない限り、コードが値を参照するときに値が割り当てられることが保証されているわけではありません。

これはバグではありません。これは、独自の初期化中にクラスの使用を処理する定義済みの方法です。そうでない場合、JVM は無限ループに陥ります。手順 3.3 を参照してください (JVM が初期化の過程にあるクラスの初期化をスキップしない場合、初期化を続けるだけです - 無限ループ)。

同様に、これはすべて、最初にクラスを参照する同じスレッドで発生することに注意してください。次に、JVM は、他のスレッドがこのクラスの使用を許可される前に、初期化が完了することを保証します。

于 2010-04-01T01:24:16.607 に答える
19

これは、Java が static セクションを宣言された順序で実行するためです。あなたの場合、シーケンスは

  1. 新しい MyClass
  2. 新しいオブジェクト

#1 を実行すると、obj はまだ初期化されていないため、null が出力されます。次のことを試してみてください。違いがわかります。

class MyClass {
  private static final Object obj = new Object();
  private static MyClass myClass = new MyClass();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

一般的に言えば、このような構造は避けた方がよいでしょう。シングルトンを作成しようとしている場合、そのコード フラグメントは次のようになります。

class MyClass {

  private static final MyClass myClass = new MyClass();

  private Object obj = new Object();

  private MyClass() {
    System.out.println(obj); // will print null once
  }
}
于 2010-03-30T18:44:20.313 に答える
0

これは、静的フィールドが定義したのと同じ順序で初期化されたためです。

于 2010-03-30T18:44:08.057 に答える
0

@Pyrolistical

最初の静的フィールド myclass のイニシャルが完全に構築されていないため...得られる結果は

null null testInitialize.MyObject@70f9f9d8 null

于 2012-04-02T02:27:15.623 に答える