在验证《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呢?
迷茫2017-04-18 10:49:39
このクラスの初期化順序はまさに魔法の問題であり、結果に基づいてのみ理解できます。
テストするブレークポイントを設定しました。 staff[0] = new Employee("Harry", 40000);
と staff[2] = new Employee();
両方のコードブロックはコンストラクターの前に実行されますが、 staff[1] = new Employee(60000);
が最初に実行され、 this("Employee #" + nextId,給与);< /code> に達します。 、次にコード ブロック、次に
public Employee(String name, double給与)
コンストラクターです。 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
2
を使用する場合、ご想像のとおり、コード ブロックがコンストラクターの前に置かれます。
PHPz2017-04-18 10:49:39
通常、Java コンパイラーはインスタンス初期化ブロックをコンストラクターにコピーします。特定の場所は、親クラスのコンストラクターを呼び出した後、コンストラクター内のステートメントの前です。ただし、例外もあります。公式 Java チュートリアルでは、初期化ブロックが各コンストラクターにコピーされると述べていますが、これは実際には厳密ではありません。
特にこの例では、コンパイラが初期化ブロックを各コンストラクターにコピーする場合、コンストラクター内で他のコンストラクターが呼び出される場合、初期化ブロックは
と同様に 2 回実行されます。例 リーリーコンパイラが初期化ブロックのコードを public Employee(double salary)
和public Employee(String name, double salary)
里面,这个初始化块就会执行两次,为了避免这种情况,编译器作了一个简单的处理,编译器发现public Employee(double salary)
调用了本类的另一个构造方法,就没有把初始化块的代码拷贝到这个构造方法里面。
也就是说在初始化第二个对象的时候,这个初始化块是推迟到调用this("Employee #" + nextId, salary);
后,在执行Employee(String name, double salary)
にコピーして実行すると、初期化ブロックの実行が遅延するため、渡されたパラメーター nextId を決定するときに、それはインクリメントされていない値のままになります。
この構築方法を次のように変更すると
出力結果は
となります リーリー変更前の状況ですが、クラスファイルを逆コンパイルするとコンパイラの最終出力が確認できます。ここでは 3 つの構築メソッドのみが掲載されています。2 番目の構築メソッドが初期化ブロックからコピーされていないことがわかります。コンテンツ、別のコンストラクターを直接呼び出します。
リーリー