web-dev-qa-db-ja.com

静的フィールドが時間内に初期化されないのはなぜですか?

次のコードは、nullを1回出力します。

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フィールドであることがわかりました。

45
Tom Brito

スタティックはソースコードで指定された順序で初期化されるためです。

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

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. 最初のstaticは残りの前に初期化されるため、その初期化中、残りのstaticフィールドはnullまたはデフォルト値です。
  3. 2番目のstaticの開始中、最初のstaticは正しいですが、残りはnullまたはデフォルトのままです。

それは明らかですか?

編集2

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

39
Pyrolistical

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

これは、クラスMyClassを最初に参照したときにJVMが通過するシーケンスです。

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

Javaは、final変数に値が一度割り当てられることを指定します。コードが参照するときに値が割り当てられることが保証されているわけではありません。コードは、割り当て後に参照します。

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

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

25
Kevin Brock

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

  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
  }
}
19
Slava Imeshev

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

0
antony

ゆうたろう

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

null null testInitialize.MyObject@70f9f9d8 null

0
iamx7777777