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

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

巴扎黑
巴扎黑

reply all(2)
迷茫

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); and staff[2] = new Employee(); Both code blocks are executed before the constructor, but staff[1] = new Employee(60000); is executed first and reaches this("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)构造函数。
如果你使用2If you use 2, then the code block precedes the constructor as you would expect.

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

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 example
public Employee(double salary) {
        this("Employee #" + nextId, salary);  // 调用了另一个构造方法
}

If 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

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

The output result will become

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

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.

  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
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!