在验证《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呢?
The order of initialization of this class is indeed a magical problem, which can only be understood based on the results.
I set a breakpoint to test,
staff[0] = new Employee("Harry", 40000);
andstaff[2] = new Employee();
Both code blocks are executed before the constructor, butstaff[1] = new Employee(60000);
is executed first and reachesthis("Employee #" + nextId, salary);, then code block, then
public Employee(String name, double salary)
constructor.staff[0] = new Employee("Harry", 40000);
和staff[2] = new Employee();
都是代码块先于构造方法执行,但staff[1] = new Employee(60000);
却先执行走到this("Employee #" + nextId, salary);
,然后代码块,然后public Employee(String name, double salary)
构造函数。如果你使用
2
If you use2
, then the code block precedes the constructor as you would expect.Normally, the Java compiler will copy the instance initialization block into the constructor. The specific location is after calling the constructor of the parent class and before the statements in the constructor, but there are exceptions. The official Java Tutorials say that the initialization block will be copied to each constructor, which is actually not rigorous.
Specifically for this example, there is a problem that needs to be considered. If the compiler copies the initialization block into each constructor, then if other constructors are called in the constructor, the initialization block will be executed twice, just like
in the exampleIf the compiler copies the code in the initialization block to
public Employee(double salary)
和public Employee(String name, double salary)
里面,这个初始化块就会执行两次,为了避免这种情况,编译器作了一个简单的处理,编译器发现public Employee(double salary)
调用了本类的另一个构造方法,就没有把初始化块的代码拷贝到这个构造方法里面。也就是说在初始化第二个对象的时候,这个初始化块是推迟到调用
this("Employee #" + nextId, salary);
后,在执行Employee(String name, double salary)
and then executes it, due to delaying the execution of the initialization block, when determining the passed parameter nextId, it will still be an unincremented value.If you modify this construction method to
The output result will become
As for the situation before modification, you can see the final output of the compiler by decompiling the class file. Only three construction methods are posted here. It can be clearly seen that the second construction method has not been copied from the initialization block. content, directly calling another constructor.