Java中子父類的類在記憶體中的建立分析..經典的案例..
以上程式碼 如果在Main方法中 通過 Animal a = new Cat(); 來實現一個父類引用子類物件。
這句話首先是建立了一個Animal型別的a的引用,然後 new Cat();建立了一個Cat的物件,最後把這個a這個引用指向了
new Cat();這個物件的地址。在這個物件建立的過程中其實有很多步驟
首次訪問:(在此沒有顯示的寫出類中的構造方法)
順序:子類的靜態欄位==》子類靜態構造==》子類非靜態欄位==》父類的靜態欄位==》父類的靜態構造==》父類的非靜態欄位
==》父類的建構函式==》子類的建構函式
非首次訪問:順序是一樣的,只不過少了中間靜態欄位和構造的過程
這個過程依次類推直到遞迴到Object結束(在次過程中也是依次給父類分配記憶體的過程),且欄位的在記憶體中的儲存順序是由上到下排列,object類的欄位 排在最前面,
原因是如果父類和子類出現了同名欄位,則在子類物件建立時,編譯器會自動認為這是兩個不同的欄位而加以區別。
說了物件的建立,其次是方法列表的建立:
方法列表的建立是在編譯時建立的,而物件的建立是在執行時,物件的建立是為了給方法列表一個引用的指標,使其它們動態關聯起來。
方法列表的建立順序跟欄位的的順序是一樣的,也是先父類後子類。(override 和 new 的不同 new主要是會阻斷繼承樹,和隱藏父類方法,建立子類和父類同名的方法
父先子後的原因是:在編譯時建立方法列表的過程是,先生成父類的方法列表,而後在生成子類的方法列表的時候,會把父類的方法複製一份
出來,然後拿子類的方法去和父類的比較,如果發現同名的方法,則看子類的方法修飾符是override 還是 new,如果是override 則覆蓋父類
同名的方法(以上所說的父類方法皆是virtual方法,並且這裡說的覆蓋只是說覆蓋方法的實現,而並沒有覆蓋父類的方法列表,通過base.父類方法名還是可以呼叫父類的方法),
如果是new 則在記憶體中的不同位置建立一個同名的方法。 不同名的,則直接建立。
完成之後,我們可以通過 a這個引用來來呼叫Cat中的方法。
1.思考:如果把上例中Animal的play方法移到Cat中,在Main方法中打算通過a.Paly();來呼叫子類的Paly方法會發生什麼現象? 會編譯不通過,為什麼呢?
按理說子類就是用來擴充套件父類的,理論上也允許子類有自己的特性啊(方法、欄位……)。但問題不是出在子類,而是出在了呼叫的位置,不能通過a.Play();來呼叫這個方法,可能大家又不解了,會想 a就是通過 new Cat();這個物件啊出來的,為什麼不能呼叫自己的方法勒,一層層的,最終我們找到原因是在 Animal a 這個申明引用的位置。
在此要引入OO 的一個原則: 關注物件原則——
由此結合:子類可以呼叫父類方法和欄位,而父類不能呼叫子類方法和欄位 這個概念。就可以知道原因了。
在說說OO中的另一個原則:就近原則——對於同名欄位或者方法,編譯器是按照其順序查詢來引用的,也就是首先訪問離它建立最近的欄位或者方法。先貼一段程式碼,然後通過程式碼來分析。
View Code在程式碼的Main方法中 Animal a = new Cat(); 這個A是Animal 型別的,結合文章開始將的 在編譯和執行過程中 子類、父類 的欄位和方法以及例項化時候在記憶體中分配的先後位置可以得出:Animal 類中的 AnimalName 在記憶體中的位置一定位於 Cat中AnimalName在記憶體中的位置的前面,根據就近原則打印出的應該是Animal。
以上文章大致概括了在繼承過程中的 在編譯和執行過程中 子類、父類 的欄位和方法以及例項化時候在記憶體中分配 和 執行的先後,以及兩個原則,如有錯誤或者不足的地方請拍磚。後續後更深入的學習oo中的其它內容。