Java构造函数顺序



这个java程序很简单,充满了注释,所以你可以很快理解它。然而,为什么在construct staff[1]中,程序首先转到语句:

this("Employee #" + nextId, s);
然后转到对象初始化块,再转回语句,多么混乱。为什么不先使用对象初始化块
import java.util.*;
public class ConstructorTest
{
   public static void main(String[] args)
   {
      // fill the staff array with three Employee objects
      Employee[] staff = new Employee[3];
      staff[0] = new Employee("Harry", 40000);
      staff[1] = new Employee(60000);
      staff[2] = new Employee();
      // print out information about all Employee objects
      for (Employee e : staff)
         System.out.println("name=" + e.getName()
            + ",id=" + e.getId()
            + ",salary=" + e.getSalary());
   }
}
class Employee
{
   // three overloaded constructors
   public Employee(String n, double s)
   {
      name = n;
      salary = s;
   }
   public Employee(double s)
   {
      // calls the Employee(String, double) constructor
      this("Employee #" + nextId, s);
   }
   // the default constructor
   public Employee()
   {
      // name initialized to ""--see below
      // salary not explicitly set--initialized to 0
      // id initialized in initialization block
   }
   public String getName()
   {
      return name;
   }
   public double getSalary()
   {
      return salary;
   }
   public int getId()
   {
      return id;
   }
   private static int nextId;
   private int id;
   private String name = ""; // instance field initialization
   private double salary;
   // static initialization block
   static
   {
      Random generator = new Random();
      // set nextId to a random number between 0 and 9999
      nextId = generator.nextInt(10000);
   }
   // object initialization block
   {
      id = nextId;
      nextId++;
   }
}

因为this("Employee #" + nextId, s);包含对超类构造函数的隐式调用,当然必须在子类的初始化块之前执行。

使用实例初始化器通常是一个坏主意,因为它们不是众所周知的,除了构造函数之外不能做任何事情,并且将两者混合会导致混淆。

这遵循JLS第8.8.7.1节指定的顺序:

(最后两个项目)

设C为被实例化的类,S为C的直接超类,i为被创建的实例。显式构造函数调用的求值过程如下:

    首先,如果构造函数调用语句是超类构造函数调用,(删减,因为它不是在我们的情况下)
  • 下一步,调用构造函数。
  • 最后,如果构造函数调用语句是超类构造函数调用,并且构造函数调用语句正常完成,则执行C的所有实例变量初始化项和C的所有实例初始化项。另一个构造函数调用不会执行这个额外的隐式操作。

所以实例初始化器在超构造函数被调用后立即执行——这是(隐式地)来自public Employee(String n, double s)。这应该在执行该双形参构造函数体之前执行。

不确定实际问题是什么,这有点令人困惑。初始化(静态、对象、构造函数)的顺序是预定义的,与它们在代码中出现的顺序无关。至于一般风格,我通常不鼓励使用对象初始化块。这是一个非常常见的错误来源,它使异常处理更加复杂,并且难以调试。此外,它不是一个非常常见的模式,因此开发人员在分析bug时往往会错过寻找它。

Java编译器必须确保从每个构造函数调用对象初始化块中的代码。对于大多数构造函数,它通过在隐式或显式调用super()之后将代码插入构造函数来实现这一点。然而,对于以this()开头的编译器,首先,没有对super()的隐式调用——其他构造函数处理它。其次,在这样的构造函数中,对象初始化块根本不能插入,因为当调用第二个构造函数时,代码将被拾取。因此,this()调用是第一个发生的事情。

最新更新