Dlaczego statyczne pola nie są inicjowane w czasie?

Poniższy kod drukuje null raz.

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) {}
}

Dlaczego obiekty statyczne nie są inicjowane przed uruchomieniem konstruktora?

Update

Skopiowałem ten przykładowy program bez uwagi, myślałem, że mówimy o 2 polach obiektowych, teraz zobaczyłem, że pierwsze jest polem MyClass.. :/

Author: Tom Brito, 2010-03-30

5 answers

Ponieważ statyki są inicjowane w kolejności podanej w kodzie źródłowym.

Zobacz też:

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

Które wydrukują:

null
null
myClassObject
null

EDIT

Ok narysujmy to, żeby było bardziej jasne.

  1. statyki są inicjowane jeden po drugim w kolejności zadeklarowanej w kodzie źródłowym.
  2. ponieważ pierwsza statyczna jest inicjalizowana przed resztą, podczas jej inicjalizacji pozostałe pola statyczne są null lub domyślne wartości.
  3. podczas inicjacji drugiej statyki pierwsza statyczna jest poprawna, ale pozostałe są nadal null lub domyślne.
Czy to jasne?

EDIT 2

Jak zauważył Varman, odniesienie do siebie będzie null podczas inicjalizacji. Co ma sens, jeśli się nad tym zastanowić.

 33
Author: Pyrolistical,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-05 23:46:00

Spróbujmy inaczej to wyjaśnić...

Jest to sekwencja, przez którą przechodzi JVM, gdy po raz pierwszy odwołujesz się do klasy MyClass.

  1. załaduj kod bajtowy do pamięci.
  2. pamięć dla statycznej pamięci jest wyczyszczona (binary zero).
  3. Zainicjalizuj klasę:
    1. uruchamia każdy statyczny inicjalizator w kolejności, w jakiej się pojawia, obejmuje to statyczne zmienne i bloki static { ... }.
    2. JVM następnie inicjalizuje zmienną statyczną myClass na nową instancja MyClass.
    3. JVM zwraca uwagę na to, że MyClass jest już załadowany (kod bajtowy) i jest w trakcie inicjalizacji, więc pomija inicjalizację.
  4. Przydziel pamięć na stercie dla obiektu.
  5. wykonaj konstruktor.
  6. wypisuje wartość obj, która jest nadal null (ponieważ nie jest częścią sterty i konstruktora zainicjalizowanych zmiennych).
  7. po zakończeniu konstruktora, wykonaj następny statyczny inicjalizator, który ustawi obj na nowy instancja Object.
  • inicjalizacja klasy zakończona. Od tego momentu wszystkie wywołania konstruktora będą zachowywać się tak, jak przypuszczasz/oczekujesz-to znaczy obj nie będzie null, ale odniesienie do instancji Object.
  • Pamiętaj, że Java określa, że zmiennej final przypisana jest wartość raz. Nie jest tak, że jest gwarantowane, że zostanie przypisana wartość, gdy kod odwołuje się do niego, chyba że zapewnisz, że kod odwołuje się do niego po jego przypisaniu.

    To nie jest błąd. Jest to zdefiniowany sposób obsługi użycia klasy podczas jej własnej inicjalizacji. Gdyby tak nie było, to JVM wchodziłby w nieskończoną pętlę. Patrz krok # 3.3 (jeśli JVM nie pominie inicjalizacji dla klasy, która jest w procesie inicjalizacji, to po prostu będzie ją inicjalizować-nieskończona pętla).

    Zauważ również, że to wszystko dzieje się w tym samym wątku, który jako pierwszy odwołuje się do klasy. Po drugie, JVM gwarantuje, że inicjalizacja zakończy się, zanim jakikolwiek inny wątek zostanie dozwolone do korzystania z tej klasy.

     21
    Author: Kevin Brock,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2010-04-01 01:24:16

    Dzieje się tak dlatego, że Java wykonuje sekcję statyczną w kolejności, w jakiej została zadeklarowana. W Twoim przypadku sekwencja

    1. new MyClass
    2. nowy obiekt

    Gdy wykonywany jest #1, obj nadal nie jest inicjowany, więc wypisuje null. Spróbuj wykonać następujące czynności, a zobaczysz różnicę:

    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
      }
    }
    

    Ogólnie rzecz biorąc, lepiej unikać takiej konstrukcji razem. Jeśli próbujesz utworzyć singleton, tak powinien wyglądać ten fragment kodu:

    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
    Author: Slava Imeshev,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2010-03-30 19:02:20

    Dzieje się tak dlatego, że statyczne pola inicjowane są w tej samej kolejności, w jakiej zostały zdefiniowane.

     0
    Author: antony,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2010-03-30 18:44:08

    @ Pirolistic

    Ponieważ inicjał pierwszego pola statycznego nie jest w pełni skonstruowany ...wynik jaki otrzymuję to

    Null null testInitialize.MyObject @ 70f9f9d8 null

     0
    Author: iamx7777777,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2012-04-02 02:27:15