Pourquoi les champs statiques ne sont-ils pas initialisés dans le temps?

Le code suivant imprime null une fois.

 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(Ssortingng[] args) {} } 

Pourquoi les objects statiques ne sont-ils pas initialisés avant l’exécution du constructeur?

Mettre à jour

Je viens de copier cet exemple de programme sans attention, je pensais que nous parlions de 2 champs d’object, maintenant j’ai vu que le premier est un champ MyClass ..: /

Parce que les statiques sont initialisées dans l’ordre dans lequel elles sont données dans le code source.

Regarde ça:

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

Cela va imprimer:

 null null myClassObject null 

MODIFIER

Ok, tirons ceci pour être un peu plus clair.

  1. Les statiques sont initialisées une par une dans l’ordre indiqué dans le code source.
  2. Le premier statique étant initialisé avant le rest, lors de son initialisation, les autres champs statiques sont des valeurs nulles ou par défaut.
  3. Pendant l’initialisation de la seconde statique, la première statique est correcte mais les autres sont toujours nulles ou par défaut.

Est-ce clair?

EDIT 2

Comme Varman l’a fait remarquer, la référence à lui-même sera nulle lors de son initialisation. Ce qui a du sens si vous y réfléchissez.

Essayons une autre façon d’expliquer cela …

C’est la séquence traversée par la JVM lorsque vous faites référence pour la première fois à la classe MyClass .

  1. Chargez le code d’octet en mémoire.
  2. La mémoire pour le stockage statique est effacée (zéro binary).
  3. Initialiser la classe:
    1. Exécutez chaque initialiseur statique dans l’ordre où il apparaît, cela inclut les variables static { ... } et static { ... } blocs static { ... } .
    2. La machine myClass initialise ensuite votre variable statique myClass sur une nouvelle instance de MyClass .
    3. Lorsque cela se produit, la machine MyClass remarque que MyClass est déjà chargé (code d’octet) et en cours d’initialisation , il ignore donc l’initialisation.
    4. Allouer de la mémoire en tas pour un object.
    5. Exécutez le constructeur.
    6. Affiche la valeur de obj qui est toujours null (car elle ne fait pas partie des variables initialisées par le tas et le constructeur).
    7. Lorsque le constructeur a terminé, exécutez le prochain initialiseur statique qui définit obj sur une nouvelle instance de Object .
  4. Initialisation de classe effectuée. A partir de là, tous les appels de constructeur se comporteront comme vous le supposez / l’attendez – c’est-à-dire que obj ne serait pas null mais une référence à une instance d’ Object .

Rappelez-vous que Java spécifie qu’une variable final se voit atsortingbuer une valeur une fois. Il n’est pas garanti qu’une valeur lui soit assignée lorsque le code le référence, sauf si vous vous assurez que le code le référence après son affectation.

Ce n’est pas un bug. C’est le moyen défini pour gérer l’utilisation de la classe lors de sa propre initialisation. Si ce n’était pas le cas, la JVM entrerait dans une boucle infinie. Voir l’étape 3.3 (si la JVM ne saute pas l’initialisation pour une classe en cours d’initialisation, elle se contente de l’initialiser – boucle infinie).

Notez également que tout se passe sur le même thread qui référence la classe en premier. Deuxièmement, la machine virtuelle Java garantit que l’initialisation se terminera avant qu’un autre thread puisse utiliser cette classe.

C’est parce que Java exécute la section statique dans l’ordre où il est déclaré. Dans votre cas, la séquence est

  1. nouveau MyClass
  2. nouvel object

Lorsque # 1 est exécuté, obj n’est toujours pas initialisé et affiche donc null. Essayez ce qui suit et vous verrez la différence:

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

De manière générale, il vaut mieux éviter une telle construction tous ensemble. Si vous essayez de créer un singleton, voici comment ce fragment de code devrait ressembler à:

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

c’est-à-dire que les champs statiques initialisés dans le même ordre qu’ils ont défini.

@Pyrolistique

puisque l’initiale du premier champ statique myclass n’est pas entièrement construite … le résultat est

null null testInitialize.MyObject@70f9f9d8 null