web-dev-qa-db-fra.com

Les champs sont-ils initialisés avant que le code constructeur ne soit exécuté en Java?

Quelqu'un peut-il expliquer le résultat du programme suivant? Je pensais que les constructeurs sont initialisés avant les variables d'instance. Donc, je m'attendais à ce que la sortie soit "XZYY".

class X {
    Y b = new Y();

    X() {
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {
    Y y = new Y();

    Z() {
        System.out.print("Z");
    }

    public static void main(String[] args) {
        new Z();
    }
}
57
Praveen Kumar

L'ordre correct d'initialisation est:

  1. Initialiseurs de variables statiques et blocs d'initialisation statiques, dans l'ordre textuel, si la classe n'a pas déjà été initialisée.
  2. L'appel super () dans le constructeur, qu'il soit explicite ou implicite.
  3. Initialiseurs de variables d'instance et blocs d'initialisation d'instance, dans l'ordre textuel.
  4. Corps restant du constructeur après super ().

Voir les sections §2.17.5-6 de la spécification Java Virtual Machine .

83
user207421

Si vous regardez la version décompilée du fichier de classe

class X {
    Y b;

    X() {
        b = new Y();
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {

    Y y;

    Z() {
        y = new Y();
        System.out.print("Z");
    }

    public static void main(String args[]) {
        new Z();
    }
}

Vous pouvez constater que la variable d'instance y est déplacée à l'intérieur du constructeur. La séquence d'exécution est la suivante.

  1. Appelez le constructeur de Z
  2. Il déclenche le constructeur par défaut de X
  3. La première ligne de X constructeur new Y() est appelée.
  4. Imprimer Y
  5. Imprimer X
  6. Appelez la première ligne du constructeur Z new Y()
  7. Imprimer Y
  8. Imprimer Z

Toutes les variables d'instance sont initialisées à l'aide d'instructions de constructeur. 

53
Arun P Johny

Lorsque vous appelez un constructeur, les initialiseurs de variables d'instance sont exécutés avant le corps du constructeur. Que pensez-vous du résultat du programme ci-dessous?

public class Tester {
    private Tester internalInstance = new Tester();
    public Tester() throws Exception {
        throw new Exception("Boom");
    }
    public static void main(String[] args) {
        try {
            Tester b = new Tester();
            System.out.println("Eye-Opener!");
        } catch (Exception ex) {
            System.out.println("Exception catched");
        }
    }
}

La méthode principale appelle le constructeur Tester, qui lève une exception. Vous pourriez vous attendre à ce que la clause catch intercepte cette exception et imprime Exception catched . Mais si vous essayez de l'exécuter, vous constaterez qu'elle ne fait rien de ce type et jette un StackOverflowError.

1
Vivek

Pour clarifier les idées fausses avec statique, je me contenterai de citer ce petit morceau de code:

public class Foo {
  { System.out.println("Instance Block 1"); }
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  { System.out.println("Instance Block 2"); }
  static { System.out.println("Static Block 2 (Weird!!)"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

La surprise est que le résultat est le suivant:

Static Block 1
Instance Block 1
Instance Block 2
Constructor
Static Block 2 (Weird!!)
In Main
Instance Block 1
Instance Block 2
Constructor

Notez que nous avons un static {} qui s'appelle after deux instances {}. cela se produit parce que nous lançons le constructeur au milieu, en interjetant l'ordre d'exécution la première fois que le constructeur est appelé.

J'ai découvert cela lorsque je travaillais sur cette réponse - https://stackoverflow.com/a/30837385/744133 .

Fondamentalement, nous observons ce qui se passe: 

  1. Lors de la première initialisation d'un objet, Initialise l'objet actuel pour l'initialisation statique et l'instance mélangée en fonction de l'ordre d'apparition.

  2. Pour toutes les initialisations suivantes, l'initialisation de l'instance est effectuée uniquement dans l'ordre d'apparition, l'initialisation statique ayant déjà eu lieu. 

J'ai besoin de rechercher comment le mélange de l'héritage, et les appels explicites et implicites à super et cela affectera cela, et sera mis à jour avec les résultats. Ce serait probablement similaire aux autres réponses fournies, sauf qu'elles se sont trompées lors de l'initialisation statique. 

1
YoYo