76 votes

Étrange comportement Java avec des qualificatifs statiques et finaux

Dans notre équipe, nous avons constaté un comportement étrange dans lequel nous utilisions les 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 String S = "abc";

    public Test() {
        System.out.println(I);
        System.out.println(S);
    }

    public static Test getInstance() { return me; }

    public static void main(String[] 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 à la fois, car le code des membres statiques de la classe est exécuté de haut en bas.

Quelqu'un peut-il expliquer pourquoi ce comportement se produit?

109voto

Marko Topolnik Points 77257

Ce sont les mesures prises lorsque vous exécutez votre programme:

  1. Avant d' main peut être exécuté, l' Test classe doit être initialisé par l'exécution des initialiseurs statiques dans l'ordre d'apparition.
  2. Pour initialiser l' me domaine, de lancer l'exécution d' new Test().
  3. Imprimer la valeur de I. Étant donné que le champ est de type 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 exécuté, l'impression de la valeur initiale null.
  4. Imprimer la valeur de S. Depuis qu'il est initialisé avec une constante de compilation, cette valeur est cuit dans le référencement de site, l'impression d' abc.
  5. new Test() terminé, maintenant l'initialiseur, pour I s'exécute.

Leçon: si vous comptez sur initialisé avec impatience statique singletons, placez le singleton déclaration que le dernier champ statique de la déclaration, ou de recourir à un initialiseur statique bloc qui se produit une fois tous les autres statique déclarations. Que va faire la classe apparaissent entièrement initialisé pour le singleton du code de la construction.

71voto

Jon Skeet Points 692016

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

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

22voto

siebenschlaefer Points 598

Vous avez un comportement étrange en raison du type de données Integer . En ce qui concerne JLS 12.4.2, les champs statiques sont initialisés dans l'ordre d'écriture, MAIS les constantes de compilation sont d'abord initialisées.

Si vous n'utilisez pas le type d'enveloppe Integer mais le type int , vous obtenez le comportement souhaité.

14voto

Martin Tuskevicius Points 1340

Votre Test compile:

public class Test {

    public static final Test me;
    public static final Integer I;
    public static final String 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(String[] args) {
        Test.getInstance();
    }
}

Comme vous pouvez le voir, le constructeur Test est appelée avant I est initialisé. C'est pourquoi il imprime "null" pour I. Si vous étiez à inverser l'ordre de déclaration, pour me et I, vous obtiendrez le résultat escompté, car I serait initialisé avant que le constructeur est appelé. Vous pouvez également modifier le type I de Integer de int.

Parce qu' 4 des besoins pour obtenir autoboxed (c'est à dire, enveloppé dans un Integer objet), il n'est pas une constante de compilation et fait partie de l'initialiseur statique bloc. Toutefois, si le type ont été int, le nombre 4 serait une constante de compilation, de sorte qu'il ne serait pas besoin d'être explicitement initialisée. Parce qu' "abc" est une constante de compilation, la valeur de S est imprimé comme prévu.

Si vous devez le remplacer,

public static final String S = "abc";

avec,

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

Ensuite, vous verrez la sortie de l' S est "null" . Pourquoi est-ce arrivé? Pour la même raison pourquoi I également des sorties "null". Des domaines comme ceux qui ont littérale, des valeurs constantes (qui n'a pas besoin de l'autoboxing, comme String) sont attribués à l' "ConstantValue" de l'attribut lors de la compilation, ce qui signifie que leur valeur peut être résolu simplement en regardant dans la classe' constante de la piscine, sans avoir besoin d'exécuter n'importe quel code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X