咱們知道,父類是不能訪問子類的實例變量的。但這裏有一個特殊的案例java
class Base { private int i = 2; public Base(){ System.out.println(this.i); this.display(); //System.out.println(this.getClass()); //this.test(); } public void display(){ System.out.println(i); } } public class Sub extends Base { private int i = 22; public Sub(){ i = 222; } public void display(){ System.out.println(i); } public static void main(String[] args) { new Test(); ① } public void test(){ } }
結果卻出人意料,並非2, 2.而是2, 0.函數
分析:在①出代碼,新建了Sub對象,在Sub類的構造函數中,先隱式調用Base類的無參構造函數。在分析Base的構造函數以前,咱們首先要明白三點:this
1)Java類的對象並非由構造函數建立的,而是在構造函數賦初值以前,就已經給類成員變量分配了內存空間,而且內存內的值爲空值。並且類中的定義時即賦值的成員變量和非靜態代碼塊中初始化元素,都是先分配內存(存儲默認值),而後在歸併到構造函數中賦初始值,並按前後順序排列在構造函數本來的代碼以前。code
2)關於子類與父類有多個同名實例變量的問題,要知道,子類的實例變量並不能覆蓋父類的同名實例變量。因此在實例化Sub類時,要先分配兩塊內存空間,用來存儲父類的i和子類的i,這時兩個i的值都爲0。對象
3)當this在構造器中時,表明正在初始化的對象。從源代碼來看,this在Base的構造器中執行。可是實際上,如今正在初始化的Java類是Sub,由於是在Sub的構造器中隱式地調用了父類的默認構造器,因此this是Sub類,這一點能夠從this.getClass()驗證。內存
那麼問題來了,this表明Sub類,但this.i卻輸出2,而this.display()卻輸出0。get
這是由於this位於Base的構造器中,它的編譯類型是Base,可是它引用的對象則是一個Sub類型,即它的運行時類型是一個Sub類型,這一點能夠由this.test()驗證,由於這回編譯不經過。編譯
總結:當變量的編譯時類型和運行時類型不一樣時,當經過該變量來訪問它所引用的對象的實例變量時,該實例變量的值由聲明它的類型決定,但當經過該變量來訪問它所引用對象的實例方法時,該方法行爲將由它所引用的對象類型決定。class
因此this.i的值爲Base中的i,this.display()爲Sub中的方法,i爲0.test