Comment le modificateur statique affecte-t-il ce code?

109

Voici mon code:

class A {
    static A obj = new A();
    static int num1;
    static int num2=0;

    private A() {
        num1++;
        num2++;
    }
    public static A getInstance() {
        return obj;
    }
}

public class Main{
    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

Le résultat est 1 0, mais je ne peux pas comprendre.

Quelqu'un peut-il me l'expliquer?

lirui
la source
10
Bonne question! Que devrions-nous apprendre de cela: ne le faites pas! ;)
isnot2bad

Réponses:

116

En Java, deux phases ont lieu: 1. Identification, 2. Exécution

  1. En phase d' identification , toutes les variables statiques sont détectées et initialisées avec des valeurs par défaut.

    Alors maintenant, les valeurs sont:
    A obj=null
    num1=0
    num2=0

  2. La deuxième phase, l' exécution , commence de haut en bas. En Java, l'exécution commence à partir des premiers membres statiques.
    Ici, votre première variable statique est static A obj = new A();, donc d'abord elle créera l'objet de cette variable et appellera le constructeur, d'où la valeur de num1et num2devient 1.
    Et puis, à nouveau, static int num2=0;sera exécuté, ce qui fait num2 = 0;.

Maintenant, supposons que votre constructeur ressemble à ceci:

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }

Cela lancera un NullPointerExceptioncar objn'a toujours pas de référence class A.

Shoaib Chikate
la source
11
Je vais prolonger: déplacez la ligne static A obj = new A();ci static int num2=0;- dessous et vous devriez obtenir 1 et 1.
Thomas
2
Ce qui me trouble encore, c'est le fait que même si num1 n'a pas d'initialisation explicite, il est (implicitement) initialisé avec 0. Il ne devrait vraiment y avoir aucune différence entre l'initialisation explicite et implicite ...
isnot2bad
@ isnot2bad "l'initialisation implicite" se produit dans le cadre de la déclaration. Déclaration se produit avant la cession , peu importe ce que vous pouvez les commander présents dans. A obj = new A(); int num1; int num2 = 0;Obtient transformé en ceci: A obj; int num1; int num2; obj = new A(); num2 = 0;. Java fait cela et num1, num2sont définis par le moment où vous atteignez le new A()constructeur.
Hans Z
31

Ce que staticsignifie le modificateur lorsqu'il est appliqué à une déclaration de variable, c'est que la variable est une variable de classe plutôt qu'une variable d'instance. En d'autres termes ... il n'y a qu'une seule num1variable et une seule num2variable.

(A part: une variable statique est comme une variable globale dans d'autres langages, sauf que son nom n'est pas visible partout. Même s'il est déclaré comme a public static, le nom non qualifié n'est visible que s'il est déclaré dans la classe courante ou dans une superclasse , ou s'il est importé à l'aide d'une importation statique. C'est la distinction. Un vrai global est visible sans qualification n'importe où.)

Donc , lorsque vous parlez obj.num1et obj.num2, vous faites référence réellement les variables statiques dont les désignations sont réelles A.num1et A.num2. Et de même, lorsque le constructeur incrémente num1et num2, il incrémente les mêmes variables (respectivement).

La ride déroutante dans votre exemple est dans l'initialisation de la classe. Une classe est initialisée en initialisant d'abord par défaut toutes les variables statiques, puis en exécutant les initialiseurs statiques déclarés (et les blocs d'initialisation statiques) dans l'ordre dans lequel ils apparaissent dans la classe. Dans ce cas, vous avez ceci:

static A obj = new A();
static int num1;
static int num2=0;

Cela se passe comme ceci:

  1. Les statiques commencent avec leurs valeurs initiales par défaut; A.objest nullet A.num1/ A.num2sont zéro.

  2. La première déclaration ( A.obj) crée une instance de A(), et le constructeur pour les Aincréments A.num1et A.num2. Lorsque la déclaration se termine, A.num1et A.num2sont les deux 1, et A.objfait référence à l' Ainstance nouvellement construite .

  3. La deuxième déclaration ( A.num1) n'a pas d'initialiseur, donc A.num1ne change pas.

  4. La troisième déclaration ( A.num2) a un initialiseur qui affecte zéro à A.num2.

Ainsi, à la fin de l'initialisation de la classe, A.num1est 1et A.num2est 0... et c'est ce que montrent vos instructions d'impression.

Ce comportement déroutant est en fait dû au fait que vous créez une instance avant la fin de l'initialisation statique et que le constructeur que vous utilisez dépend et modifie une statique qui doit encore être initialisée. C'est quelque chose que vous devriez éviter de faire dans du vrai code.

Stephen C
la source
16

1,0 est correct.

Lorsque la classe est chargée, toutes les données statiques sont initialisées ou déclarées. Par défaut, int est égal à 0.

  • le premier A est créé. num1 et num2 devenant 1 et 1
  • que static int num1;ne fait rien
  • que static int num2=0;cela écrit 0 à num2
Léonidos
la source
9

Cela est dû à l'ordre des initialiseurs statiques. Les expressions statiques dans les classes sont évaluées dans un ordre descendant.

Le premier à être appelé est le constructeur de A, qui définit num1et les num2deux à 1:

static A obj = new A();

Ensuite,

static int num2=0;

est appelé et définit à nouveau num2 = 0.

C'est pourquoi num1vaut 1 et num20.

En remarque, un constructeur ne doit pas modifier les variables statiques, c'est une très mauvaise conception. Au lieu de cela, essayez une approche différente pour implémenter un Singleton en Java .

Domi
la source
6

Une section dans JLS peut être trouvée: §12.4.2 .

Procédure d'initialisation détaillée:

9 Ensuite, exécutez soit les initialiseurs de variable de classe et les initialiseurs statiques de la classe, soit les initialiseurs de champ de l'interface, dans l'ordre textuel, comme s'il s'agissait d'un seul bloc, sauf que les variables de classe finales et les champs d'interfaces dont les valeurs sont compilées les constantes -time sont initialisées en premier

Ainsi, les trois variables statiques seront initialisées une par une dans l'ordre textuel.

Alors

static A obj = new A();
//num1 = 1, num2 = 1;
static int num1;
//this is initilized first, see below.
static int num2=0;
//num1 = 1, num2 = 0;

Si je change la commande en:

static int num1;
static int num2=0;
static A obj = new A();

Le résultat sera 1,1.

Notez que le static int num1;n'est pas un initialiseur de variable car ( §8.3.2 ):

Si un déclarateur de champ contient un initialiseur de variable, alors il a la sémantique d'une affectation (§15.26) à la variable déclarée, et: Si le déclarateur est pour une variable de classe (c'est-à-dire un champ statique), alors l'initialiseur de variable est évalué et l'affectation effectuée exactement une fois, lorsque la classe est initialisée

Et cette variable de classe est initialisée lorsque la classe est créée. Cela se produit en premier ( §4.12.5 ).

Chaque variable d'un programme doit avoir une valeur avant que sa valeur ne soit utilisée: chaque variable de classe, variable d'instance ou composant de tableau est initialisé avec une valeur par défaut lors de sa création (§15.9, §15.10): Pour l'octet de type, la valeur par défaut est zéro, c'est-à-dire la valeur de (octet) 0. Pour le type short, la valeur par défaut est zéro, c'est-à-dire la valeur de (short) 0. Pour le type int, la valeur par défaut est zéro, c'est-à-dire 0. Pour le type long, la valeur par défaut est zéro, c'est-à-dire 0L. Pour le type float, la valeur par défaut est zéro positif, c'est-à-dire 0,0f. Pour le type double, la valeur par défaut est zéro positif, c'est-à-dire 0,0d. Pour le type char, la valeur par défaut est le caractère nul, c'est-à-dire «\ u0000». Pour le type boolean, la valeur par défaut est false. Pour tous les types de référence (§4.3), la valeur par défaut est nulle.

StarPinkER
la source
2

Peut-être que cela aidera à y penser de cette façon.

Les classes sont des plans pour les objets.

Les objets peuvent avoir des variables lorsqu'ils sont instanciés.

Les classes peuvent également avoir des variables. Ceux-ci sont déclarés statiques. Ils sont donc définis sur la classe plutôt que sur les instances d'objet.

Vous ne pouvez avoir qu'une seule classe dans une application, c'est donc un peu comme un stockage global spécifiquement pour cette classe. Ces variables statiques peuvent bien sûr être accédées et modifiées de n'importe où dans votre application (en supposant qu'elles soient publiques).

Voici un exemple de classe "Dog" qui utilise une variable statique pour suivre le nombre d'instances qu'elle a créées.

La classe "Dog" est le cloud tandis que les boîtes orange sont des instances "Dog".

Classe de chien

Lire la suite

J'espère que cela t'aides!

Si vous avez envie de questions, cette idée a été introduite pour la première fois par Platon

Goran
la source
1

Le mot-clé static est utilisé en java principalement pour la gestion de la mémoire. Nous pouvons appliquer un mot-clé statique avec des variables, des méthodes, des blocs et une classe imbriquée. Le mot-clé static appartient à la classe plutôt qu'à l'instance de la classe.Pour une brève explication sur le mot-clé static:

http://www.javatpoint.com/static-keyword-in-java

Rachana
la source
0

La plupart des réponses ci-dessus sont correctes. Mais vraiment pour illustrer ce qui se passe, j'ai fait quelques petites modifications ci-dessous.

Comme mentionné plusieurs fois ci-dessus, ce qui se passe, c'est qu'une instance de la classe A est créée avant que la classe A ne soit complètement chargée. Donc, ce qui est considéré comme le «comportement» normal n'est pas observé. Ce n'est pas trop différent de l'appel de méthodes à partir d'un constructeur qui peut être remplacé. Dans ce cas, les variables d'instance peuvent ne pas être dans un état intuitif. Dans cet exemple, les variables de classe ne sont pas dans un état intuitif.

class A {
    static A obj = new A();
    static int num1;
    static int num2;
    static {
        System.out.println("Setting num2 to 0");
        num2 = 0;
    }

    private A() {
        System.out.println("Constructing singleton instance of A");
        num1++;
        num2++;
    }

    public static A getInstance() {
        return obj;
    }
}

public class Main {

    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

La sortie est

Constructing singleton instance of A
Setting num2 to 0
1
0
w25r
la source
0

java n'initialise pas la valeur d'un membre de données statique ou non statique jusqu'à ce qu'il ne soit pas appelé, mais il le crée.

Donc, ici, lorsque num1 et num2 seront appelés dans main, il sera initialisé avec des valeurs

num1 = 0 + 1; et

num2 = 0;

deepak tiwari
la source