Quand les variables statiques sont-elles initialisées?

87

Je me demande quand les variables statiques sont initialisées à leurs valeurs par défaut. Est-il correct que lorsqu'une classe est chargée, des variables statiques sont créées (allouées), puis des initialiseurs statiques et des initialisations dans les déclarations sont exécutés? À quel moment les valeurs par défaut sont-elles données? Cela conduit au problème de la référence directe.

Veuillez également si vous pouvez expliquer cela en référence à la question posée sur Pourquoi les champs statiques ne sont pas initialisés à temps? et surtout la réponse donnée par Kevin Brock sur le même site. Je ne comprends pas le 3ème point.

Ankit
la source
2
Veuillez modifier votre question pour inclure le devis auquel vous faites référence.
Oliver Charlesworth
1
Avez-vous lu la spécification du langage Java? C'est un document assez lisible, délibérément. Si vous avez lu cela, vous comprendrez peut-être ce qui se passe. Sinon, vous pouvez poser une question plus précise au minimum ...
Maarten Bodewes
Je pense que ce Q&A est un dup de stackoverflow.com/questions/3499214 .
Stephen C

Réponses:

72

À partir de See Java Static Variable Methods :

  • C'est une variable qui appartient à la classe et non à l'objet (instance)
  • Les variables statiques ne sont initialisées qu'une seule fois, au début de l'exécution. Ces variables seront initialisées en premier, avant l'initialisation de toute variable d'instance
  • Une seule copie à partager par toutes les instances de la classe
  • Une variable statique est accessible directement par le nom de la classe et n'a besoin d'aucun objet.

Les variables d'instance et de classe (statiques) sont automatiquement initialisées aux valeurs standard par défaut si vous ne parvenez pas à les initialiser volontairement. Bien que les variables locales ne soient pas automatiquement initialisées, vous ne pouvez pas compiler un programme qui ne parvient pas à initialiser une variable locale ou à affecter une valeur à cette variable locale avant son utilisation.

Ce que fait réellement le compilateur est de produire en interne une seule routine d'initialisation de classe qui combine tous les initialiseurs de variables statiques et tous les blocs de code d'initialisation statiques, dans l'ordre dans lequel ils apparaissent dans la déclaration de classe. Cette procédure d'initialisation unique est exécutée automatiquement, une seule fois, lors du premier chargement de la classe.

En cas de intérieur classes , elles ne peuvent pas avoir de champs statiques

Une classe interne est une classe imbriquée qui n'est pas explicitement ou implicitement déclaréestatic .

...

Les classes internes ne peuvent pas déclarer d'initialiseurs statiques (§8.7) ou d'interfaces membres ...

Les classes internes ne peuvent pas déclarer de membres statiques, sauf si ce sont des variables constantes ...

Voir JLS 8.1.3 Classes internes et instances englobantes

finalles champs en Java peuvent être initialisés séparément de leur lieu de déclaration, mais cela ne peut pas s'appliquer aux static finalchamps. Voir l'exemple ci-dessous.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

En effet, il n'y a qu'une seule copie des staticvariables associées au type, plutôt qu'une copie associée à chaque instance du type comme avec les variables d'instance et si nous essayons d'initialiser le ztype static finaldans le constructeur, il tentera de réinitialiser le static finalchamp de type zcar le constructeur est exécuté à chaque instanciation de la classe qui ne doit pas se produire dans les finalchamps statiques .

Lion
la source
5
In case of static inner classes, they can not have static fieldssemble être une faute de frappe. Les classes internes ne sont pas statiques.
Daniel Lubarov
Vous devriez utiliser cependant au lieu de Bien
Suraj Jain
Lorsque vous lancez une JVM et chargez une classe pour la première fois (cela est fait par le chargeur de classe lorsque la classe est référencée pour la première fois de quelque manière que ce soit), tous les blocs ou champs statiques sont «chargés» dans la JVM et deviennent accessibles.
nhoxbypass
1
Malheureusement, cette réponse contient des inexactitudes factuelles sur le moment où la statique est initialisée. Veuillez consulter stackoverflow.com/a/3499322/139985 .
Stephen C
15

Voir:

Le dernier en particulier fournit des étapes d'initialisation détaillées qui expliquent quand les variables statiques sont initialisées, et dans quel ordre (avec la mise en garde que finalles variables de classe et les champs d'interface qui sont des constantes au moment de la compilation sont initialisés en premier.)

Je ne sais pas quelle est votre question spécifique sur le point 3 (en supposant que vous parlez de celui imbriqué?). La séquence détaillée indique qu'il s'agirait d'une demande d'initialisation récursive afin qu'elle continue l'initialisation.

Dave Newton
la source
11

Les champs statiques sont initialisés lorsque la classe est chargée par le chargeur de classe. Les valeurs par défaut sont attribuées à ce moment. Cela se fait dans l'ordre dans lequel ils apparaissent dans le code source.

Dave
la source
10

L'ordre d'initialisation est:

  1. Blocs d'initialisation statiques
  2. Blocs d'initialisation d'instance
  3. Constructeurs

Les détails du processus sont expliqués dans le document de spécification JVM .

Óscar López
la source
6

variable statique

  • C'est une variable qui appartient à la classe et non à l'objet (instance)
  • Les variables statiques ne sont initialisées qu'une seule fois, au début de l'exécution (lorsque le Classloader charge la classe pour la première fois).
  • Ces variables seront initialisées en premier, avant l'initialisation de toute variable d'instance
  • Une seule copie à partager par toutes les instances de la classe
  • Une variable statique est accessible directement par le nom de la classe et ne nécessite aucun objet
aleroot
la source
4

En commençant par le code de l'autre question:

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

Une référence à cette classe lancera l'initialisation. Tout d'abord, la classe sera marquée comme initialisée. Ensuite, le premier champ statique sera initialisé avec une nouvelle instance de MyClass (). Notez que myClass reçoit immédiatement une référence à une instance MyClass vierge . L'espace est là, mais toutes les valeurs sont nulles. Le constructeur est maintenant exécuté et imprimeobj , ce qui est nul.

Revenons maintenant à l'initialisation de la classe: il objest fait référence à un nouvel objet réel, et nous avons terminé.

Si cela a été déclenché par une instruction comme: l' MyClass mc = new MyClass();espace pour une nouvelle instance MyClass est à nouveau alloué (et la référence placée mc). Le constructeur est à nouveau exécuté et imprime à nouveau obj, ce qui n'est plus nul.

Le vrai truc ici est que lorsque vous utilisez new, comme dans WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiiest immédiatement donné une référence à un peu de mémoire annulée. La machine virtuelle Java va ensuite initialiser les valeurs et exécuter le constructeur. Mais si vous faites référence d'une manière ou d'une autre weii avant de le faire - en le référençant à partir d'un autre thread ou en faisant référence à l'initialisation de la classe, par exemple - vous regardez une instance de classe remplie de valeurs nulles.

RalphChapin
la source
1
Une classe n'est pas marquée comme initialisée tant que l'initialisation n'est pas terminée - faire autrement n'aurait aucun sens. Le marquage comme initialisé est presque la dernière étape franchie. Voir JLS 12.4.2 .
Dave Newton le
@DaveNewton: Une fois que quelque chose fait référence à la classe et commence à l'initialiser, toutes les autres références traiteront la classe comme initialisée. Ils n'essaieront pas de l'initialiser et ils n'attendront pas qu'il soit initialisé. Par conséquent, les champs qui semblent être non nuls au début d'un programme peuvent en fait être nuls pendant un certain temps. Ne pas comprendre cela est ce qui cause toute la confusion. Je pense qu'il est plus simple de dire qu'une classe non initialisée est "marquée" comme initialisée sur la première référence, toutes les autres références la traitent comme initialisée, et c'est pourquoi cela se produit.
RalphChapin
Une correction au commentaire précédent: comme décrit le JLS 12.4.2 de Dave Newton, la classe est verrouillée lors de son initialisation. D' autres discussions vont attendre la classe à initialiser. Cela n'affecte pas ce cas, cependant, qui se passe dans un seul thread.
RalphChapin
4

La variable statique peut être initialisée des trois manières suivantes comme suit, choisissez celle que vous aimez

  1. vous pouvez l'initialiser au moment de la déclaration
  2. ou vous pouvez le faire en créant un bloc statique, par exemple:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. Il existe une alternative aux blocs statiques - vous pouvez écrire une méthode statique privée

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
ajay verma
la source