在验证《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呢?
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);
etstaff[2] = new Employee();
sont des blocs de code qui sont exécutés avant le constructeur, maisstaff[1] = new Employee(60000);
est exécuté d'abord surthis("Employee #" + nextId, salary);
, puis sur le bloc de code, puis surpublic 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.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'exempleSi le compilateur copie le code du bloc d'initialisation vers
public Employee(double salary)
etpublic 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é lorsqueEmployee(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
Le résultat de sortie deviendra
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.