web-dev-qa-db-fra.com

La valeur de la variable statique n'a pas changé même après l'initialisation de la classe enfant en Java

Lorsque j'appelle la variable statique y en utilisant Checks.y (Checks étant une sous-classe), le bloc statique n'est pas exécuté et la valeur de y n'est pas mise à jour.

class Par {
    static int y = 4;
}

class Checks extends Par {
    static {
        y = 5;
    }
}

public class Check {
    public static void main(String args[]) {
        System.out.println(Checks.y); // here printing 4
    }
}

Comme statique est partagé entre toutes les sous-classes, la valeur doit être mise à jour.

Quelle pourrait être la raison derrière cela?

32
rawat

Le champ y n'est pas déclaré par la classe Checks.

La lecture de champs statiques ne déclenche pas l'initialisation de la classe référencée (Checks), sauf si cette classe est celle dans laquelle le champ est déclaré (voir la citation JLS ci-dessous). Dans cet exemple, même si y est accessible via Checks, cela ne fera que déclencher l'initialisation de Par car Par est la classe déclarant y.

En d'autres termes, la classe Checks est dans un sens non utilisée à l'exécution.

C'est peut-être une illustration de la raison pour laquelle il est erroné d'accéder aux membres static à travers des sous-classes, ce qui provoque un avertissement lors de la compilation.


Il y a une explication simple dans la spécification :

12.4.1. Quand l'initialisation se produit

Une classe ou une interface de type T sera initialisé immédiatement avant la première occurrence de l’un des fichiers Suivant:

  • T est une classe et une instance de T est créée.

  • Une méthode statique déclarée par T est invoquée.

  • Un champ statique déclaré par T est affecté.

  • Un champ statique déclaré par T est utilisé et le champ n'est pas une constante variable (§4.12.4).

  • T est une classe de niveau supérieur (§7.6) et une assertion (§14.10) Lexicalement imbriqué dans T (§8.1.3) est exécuté . 
    ... 
    Une référence à un champ statique (§8.3.1.1) provoque l’initialisation de la classe ou de l’interface qui le déclare réellement, même si le nom d’une sous-classe, d’une sous-interface ou d’une classe implémentant une interface est utilisé.

La dernière note explique pourquoi votre sous-classe n'est pas en cours d'initialisation.

29
ernest_k

De JLS 12.4.1 :

Une classe ou une interface de type T sera initialisée immédiatement avant le première apparition de l’un des cas suivants:

  • T est une classe et une instance de T est créée.
  • T est une classe et une méthode statique déclarée par T est appelée.
  • Un champ statique déclaré par T est affecté.
  • Un champ statique déclaré par T est utilisé et le champ n'est pas une variable constante (§4.12.4).
  • T est une classe de niveau supérieur (§7.6) et une instruction d'assertion (§14.10) imbriquée lexicalement dans T (§8.1.3) est exécutée.

Puisque y n'est pas déclaré dans les chèques, aucun des critères ci-dessus n'est satisfait.

Une autre façon d’illustrer ce comportement:

class par {
    static int y = 4;
    static {
        System.out.println("static constructor of par");
    }
}

class checks extends par {
    static int x = 6;
    static {
        System.out.println("checks static constructor");
        y = 5;
    }
}

public class check{
    public static void main(String args[]){
        System.out.println(checks.y);
        System.out.println(checks.x);
        System.out.println(checks.y);
    }
}

Sortie

static constructor of par
4
checks static constructor
6
5

Ainsi, après avoir appelé checks.x qui satisfait à la deuxième règle, le constructeur statique est appelé.

9
Diadistis

Ici:

System.out.println(checks.y); // Here printing 4

y fait référence à un champ de la classe par. Cet accès au champ entraîne le chargement de la classe par (la classe parente) en fonction du JLS (l'emphase est à moi):

12.4. Initialisation de classes et d'interfaces

....

12.4.1. Quand l'initialisation se produit

Une classe ou une interface de type T sera initialisée immédiatement avant le première occurrence d’un des phénomènes suivants: T est une classe et une instance de T est créée. Une méthode statique déclaré par T est invoqué.

Un champ statique déclaré par T est affecté.

Un champ statique déclaré par T est utilisé et le champ n'est pas une constante variable (§4.12.4).

T est une classe de niveau supérieur (§7.6) et une assertion (§14.10) imbriqué lexicalement dans T (§8.1.3) est exécuté.

Mais cela ne charge pas la classe checks parce que (c'est moi qui souligne):

Une référence à un champ statique (§8.3.1.1) provoque l'initialisation de seulement la classe ou l'interface qui le déclare réellement, même s'il est possible que être désigné par le nom d'une sous-classe, d'une sous-interface ou d'un classe qui implémente une interface.

4
davidxxx

En effet, le bloc static de la classe checks n'est pas exécuté. Bien que vous mentionniez la classe checks, la JVM ne la charge pas du tout.

Vous pouvez le confirmer en ajoutant un autre System.out.println à l'intérieur du bloc statique.

class checks extends par {

    static {
        System.out.println("Test");
        y = 5;
    }
}

Le mot Test ne sera jamais imprimé.


Lisez la section 12.4.1. Lorsque l'initialisation se produit de la spécification du langage Java pour en savoir plus.

4
Roshana Pitigala

Le seul aspect qui n’a pas été mentionné jusqu’à présent et qui puisse prêter à confusion pour les nouveaux programmeurs Java: le fait d’organiser votre code source n’a aucune importance! 

Vous avez vos deux classes (qui doivent s'appeler Parent et Child btw pour suivre les conventions de nommage Java) dans un fichier ou un exemple. Donc, vous supposez probablement que ces choses sont réunies automatiquement au moment de l'exécution. 

Mais au moment de l'exécution, il existe des fichiers de classe individuels par classe. Et comme l'ont dit les autres: rien dans le code ne fait référence à la classe enfant. Ainsi, cette classe n'est pas chargée et l'assignation ne se produit pas! 

1
GhostCat

Comme d'autres l'ont mentionné, le bloc statique n'est pas exécuté car la classe n'est pas initialisée en tant que répondu avant .

Notez que les noms de convention de classe commencent par une lettre majuscule.

Un exemple plus clair montrant que la classe de substitution n'est pas utilisée:

class Par {
    static int y = 4;
    public static void main(String args[]) {
        System.out.println(Checks.y);    // Here printing 4
        System.out.println(new Checks().y);    // Here printing 5
    }
}

class Checks extends Par {
   static {
        y = 5;
    }
}
0
user7294900