Java类的实例化顺序
巴扎黑
巴扎黑 2017-04-18 10:47:39
0
2
716

在验证《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呢?

巴扎黑
巴扎黑

répondre à tous(2)
迷茫

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;
}
PHPzhong

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
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal