Comportement Java étrange avec qualificateurs statiques et finaux

Dans notre équipe, nous avons trouvé un comportement étrange où nous avons utilisé static qualificatifs static et final . Ceci est notre classe de test:

 public class Test { public static final Test me = new Test(); public static final Integer I = 4; public static final Ssortingng S = "abc"; public Test() { System.out.println(I); System.out.println(S); } public static Test getInstance() { return me; } public static void main(Ssortingng[] args) { Test.getInstance(); } } 

Lorsque nous exécutons la méthode main , nous obtenons un résultat de:

 null abc 

Je comprendrais si elle écrivait null valeurs null deux fois, car le code des membres de la classe statique est exécuté de haut en bas.

Est-ce que quelqu’un peut expliquer pourquoi ce comportement se produit?

Voici les étapes à suivre lorsque vous exécutez votre programme:

  1. Avant que main puisse être exécuté, la classe Test doit être initialisée en exécutant des initialiseurs statiques dans l’ordre d’apparition.
  2. Pour initialiser le champ me , commencez à exécuter le new Test() .
  3. Imprimer la valeur de I Puisque le type de champ est Integer , ce qui semble être une constante de compilation 4 devient une valeur calculée ( Integer.valueOf(4) ). L’initialiseur de ce champ n’a pas encore été exécuté, imprimant la valeur initiale null .
  4. Imprime la valeur de S Comme il est initialisé avec une constante de compilation, cette valeur est intégrée au site de référence, en imprimant abc .
  5. new Test() termine, maintenant l’initialiseur pour I s’exécute.

Leçon: si vous utilisez des singletons statiques initialisés avec impatience, placez la déclaration singleton comme dernière déclaration de champ statique ou utilisez un bloc d’initialisation statique qui apparaît après toutes les autres déclarations statiques. Cela fera apparaître la classe complètement initialisée au code de construction du singleton.

S est une constante à la compilation, suivant les règles de JLS 15.28 . Ainsi, toute occurrence de S dans le code est remplacée par la valeur connue à la compilation.

Si vous changez le type de I en int , vous verrez la même chose pour cela.

Vous avez un comportement étrange dû au type de données Integer . En ce qui concerne les champs statiques JLS 12.4.2 sont initialisés dans l’ordre dans lequel vous les écrivez, MAIS les constantes de compilation sont initialisées en premier.

Si vous n’utilisez pas le type wrapper Integer mais le type int , vous obtenez le comportement souhaité.

Votre Test compilé en:

 public class Test { public static final Test me; public static final Integer I; public static final Ssortingng S = "abc"; static { me = new Test(); I = Integer.valueOf(4); } public Test() { System.out.println(I); System.out.println("abc"); } public static Test getInstance() { return me; } public static void main(Ssortingng[] args) { Test.getInstance(); } } 

Comme vous pouvez le voir, le constructeur de Test est appelé avant que I initialisé. C’est pourquoi il imprime "null" pour I . Si vous changiez l’ordre de déclaration pour me et I , vous obtiendriez le résultat attendu car I serais initialisé avant l’appel du constructeur. Vous pouvez également changer le type pour I de Integer à int .

Étant donné que 4 doit obtenir une firebase database automatique (c’est-à-dire enveloppé dans un object Integer ), il ne s’agit pas d’une constante à la compilation et fait partie du bloc d’initialisation statique. Cependant, si le type était int , le nombre 4 serait une constante à la compilation, il ne serait donc pas nécessaire de l’initialiser explicitement. Comme "abc" est une constante à la compilation, la valeur de S est imprimée comme prévu.

Si vous voulez remplacer,

 public static final Ssortingng S = "abc"; 

avec,

 public static final Ssortingng S = new Ssortingng("abc"); 

Vous remarquerez alors que la sortie de S est également "null" . Pourquoi ça arrive? Pour la même raison, I produis aussi "null" . Les champs comme ceux qui ont des valeurs littérales et constantes ( ne nécessitant pas de génération automatique, comme Ssortingng ) sont atsortingbués avec l’atsortingbut "ConstantValue" lorsqu’ils sont compilés, ce qui signifie que leur valeur peut être résolue simplement en examinant le pool constant de la classe sans avoir besoin pour exécuter n’importe quel code.