Maison  >  Questions et réponses  >  le corps du texte

Java类的实例化顺序

在验证《Core Java》第9版4-5代码时,发现程序输出结果和自己理解的不太一样。

import java.util.Random;

class Employee {
    private static int nextId;

    private int id;
    private String name = "";
    private double salary;

    static {
        Random generator = new Random();
        nextId = generator.nextInt(10000);
    }

    {
        id = nextId;
        nextId++;
    }

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public Employee(double salary) {
        this("Employee #" + nextId, salary);
    }

    public Employee() {

    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public int getId() {
        return id;
    }

}

public class ConstructorTest {

    public static void main(String[] args) {
        Employee[] staff = new Employee[3];

        staff[0] = new Employee("Harry", 40000);

        staff[1] = new Employee(60000);

        staff[2] = new Employee();

        for (Employee e : staff) {
            System.out.println("id = " + e.getId() + ", name = " + e.getName()
                    + ", salary = " + e.getSalary());
        }
    }

}

以下是输出结果:

id = 6943, name = Harry, salary = 40000.0
id = 6944, name = Employee #6944, salary = 60000.0
id = 6945, name = , salary = 0.0

根据第一条语句得出静态初始化块生成的nextId为6943,然后在初始化块中id被赋值为6943,nextId自增后为6944。再执行第一个构造函数;

那么对于第二个对象来说,就应该直接执行初始化块,此时id为6944,nextId自增为6945。
再执行第二个构造函数,此时this("Employee #" + nextId, salary);语句中的nextId应该为6945,为什么输出结果为6944呢?

巴扎黑巴扎黑2744 Il y a quelques jours668

répondre à tous(2)je répondrai

  • 迷茫

    迷茫2017-04-18 10:49:39

    L'ordre d'initialisation de cette classe est en effet un problème magique, qui ne peut être compris qu'en fonction des résultats.
    J'ai défini un point d'arrêt à tester. staff[0] = new Employee("Harry", 40000); et staff[2] = new Employee(); sont des blocs de code qui sont exécutés avant le constructeur, mais staff[1] = new Employee(60000); est exécuté d'abord sur this("Employee #" + nextId, salary);, puis sur le bloc de code, puis sur public Employee(String name, double salary). Constructeur.
    Si vous utilisez 2, le bloc de code précède le constructeur comme on peut s'y attendre.

    public Employee(double salary) {
        // 1
        this("Employee #" + nextId, salary); 
        // 2
    //    this.name = "Employee #" + nextId; 
    //    this.salary = salary;
    }

    répondre
    0
  • PHPz

    PHPz2017-04-18 10:49:39

    Normalement, le compilateur Java copie le bloc d'initialisation de l'instance dans le constructeur. L'emplacement spécifique est après l'appel du constructeur de la classe parent et avant les instructions dans le constructeur, mais il existe des exceptions. Les didacticiels Java officiels indiquent que le bloc d'initialisation sera copié dans chaque constructeur, ce qui n'est en réalité pas rigoureux.

    Spécifiquement pour cet exemple, il y a un problème à considérer. Si le compilateur copie le bloc d'initialisation dans chaque constructeur, alors si d'autres constructeurs sont appelés dans le constructeur, le bloc d'initialisation sera exécuté deux fois. Tout comme

    dans. l'exemple
    public Employee(double salary) {
            this("Employee #" + nextId, salary);  // 调用了另一个构造方法
    }

    Si le compilateur copie le code du bloc d'initialisation vers public Employee(double salary) et public Employee(String name, double salary), ce bloc d'initialisation sera exécuté deux fois Afin d'éviter cette situation, le compilateur a effectué un processus simple Le compilateur a trouvé public Employee(double salary). Lorsqu'un autre constructeur de cette classe est appelé, le code du bloc d'initialisation n'est pas copié dans ce constructeur.
    C'est-à-dire que lors de l'initialisation du deuxième objet, ce bloc d'initialisation est reporté jusqu'après l'appel de this("Employee #" + nextId, salary); puis exécuté lorsque Employee(String name, double salary) est exécuté. En raison du report de l'exécution du bloc d'initialisation, il est décidé. passer Lorsque le paramètre nextId est utilisé, il s'agit toujours d'une valeur non incrémentée.
    Si vous changez cette méthode de construction en

    public Employee(double salary) {
        // this("Employee #" + nextId, salary);
        this.name = "Employee #" + nextId;
        this.salary = salary;
    }

    Le résultat de sortie deviendra

    id = 5473, name = Harry, salary = 40000.0
    id = 5474, name = Employee #5475, salary = 60000.0
    id = 5475, name = , salary = 0.0

    Après avoir modifié la situation précédente, vous pouvez voir le résultat final du compilateur en décompilant le fichier de classe. Seules trois méthodes de construction sont publiées ici. On voit clairement que la deuxième méthode de construction n'a pas été copiée et initialisée. le contenu du bloc appelle directement une autre méthode constructeur.

      public Employee(java.lang.String, double);
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: aload_0
           5: ldc           #2                  // String
           7: putfield      #3                  // Field name:Ljava/lang/String;
          10: aload_0
          11: getstatic     #4                  // Field nextId:I
          14: putfield      #5                  // Field id:I
          17: getstatic     #4                  // Field nextId:I
          20: iconst_1
          21: iadd
          22: putstatic     #4                  // Field nextId:I
          25: aload_0
          26: aload_1
          27: putfield      #3                  // Field name:Ljava/lang/String;
          30: aload_0
          31: dload_2
          32: putfield      #6                  // Field salary:D
          35: return
    
      public Employee(double);
        Code:
           0: aload_0
           1: new           #7                  // class java/lang/StringBuilder
           4: dup
           5: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
           8: ldc           #9                  // String Employee #
          10: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          13: getstatic     #4                  // Field nextId:I
          16: invokevirtual #11                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          19: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          22: dload_1
          23: invokespecial #13                 // Method "<init>":(Ljava/lang/String;D)V
          26: return
    
      public Employee();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: aload_0
           5: ldc           #2                  // String
           7: putfield      #3                  // Field name:Ljava/lang/String;
          10: aload_0
          11: getstatic     #4                  // Field nextId:I
          14: putfield      #5                  // Field id:I
          17: getstatic     #4                  // Field nextId:I
          20: iconst_1
          21: iadd
          22: putstatic     #4                  // Field nextId:I
          25: return

    répondre
    0
  • Annulerrépondre