这个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()
调用是第一个发生的事情。