コンパイル時のタイプと実行時のタイプが異なる場合

1670 ワード

class Base {
	private int i = 2;          // 2

	public Base() {
		this.display();         // 3
	}

	public void display() {
		System.out.println(i);  //
	}
}

class Derived extends Base {
	private int i = 22;         // 5

	public Derived() {
		i = 222;                // 6
	}

	public void display() {
		System.out.println(i);  // 4
	}
}

public class Test {
	public static void main(String[] args) {
		new Derived();          // 1
	}
}

以上のコード出力結果は0
コード行の注釈は実行順序を表す.
1:システムはまずDerivedオブジェクトにメモリ空間を割り当て、2つのメモリ空間がそれぞれDerivedオブジェクトの2つのiインスタンス変数を格納し、1つはBaseに属する1つはDerivedであり、このとき、この2つのiインスタンス変数の値はいずれも0である.
2:Derivedクラスのコンストラクタを実行する前に、Baseクラスのコンストラクタを実行します.表面的には、Baseクラスがコンパイラ処理された後、そのコンストラクタには、i=2の2行のコードが含まれています.       this.display();
3: this.display();のthisはここでDerivedを指します.つまり、サブクラスを実装する方法が親クラスを上書きする方法です.
4:このときDerivedのiはまだ初期化されておらず、0のため出力0
5:Derivedのi初期化
6:Derivedのコンストラクタ
----------------------------------
ベースクラスのコンストラクタを次の形式に変更します.
public Base() { 
           System.out.println(this.i); 
           this.display(); 
} 


プログラムを再実行すると、出力が2と0であることがわかります.
変数のコンパイル時のタイプと実行時のタイプが異なる場合、その変数によって参照するオブジェクトのインスタンス変数にアクセスする場合、そのインスタンス変数の値は、その変数を宣言するタイプによって決定されるが、その変数によって参照するオブジェクトのインスタンスメソッドが呼び出されると、そのメソッドの動作は、実際に参照するオブジェクトによって決定され、プログラムがthisにアクセスする.iの場合、Baseクラスで定義されたiインスタンス変数、すなわち出力2にアクセスします.しかし、これを実行します.display()では実際にDerivedオブジェクトの挙動,すなわちDerivedオブジェクトを出力するiインスタンス変数,すなわち0を示す.