半深入理解Java屬性繼承


前幾天在面試的時候又被問到了一個問題,“Java重寫和重載有什么區別?”。這個問題在Java領域是一個老生常談的問題了,事實上我認為這兩個東東除了中文名長得很像以外(英文名好像也很像),基本就沒半毛錢關系了。我們很難找出他們的共性,卻一直要嘗試找出他們之間的區別,呵呵。


然而本文的主題並非重寫和重載,而是重寫的的孿生兄弟,屬性繼承。
故事的開始,我們先看一段代碼

 1 public class Parent{  2 public String color;  3 public Parent(){  4 this.color="green";  5 }  6 public void printColor(){  7 System.out.println(color);  8 }  9 public static void main(String args[]){ 10 new Child().printColor(); 11 } 12 } 13 class Child extends Parent{ 14 public String color; 15 public Child(){ 16 } 17 public void printColor(){ 18 System.out.println(color); 19 } 20 }

 

這段代碼很簡單,我們先寫了一個類Parent,給他添加了一個屬性color,並在構造函數中初始化為“green”,同時提供一個打印函數將color的值輸出。
然后我們編寫了一個Child類繼承Parent,重寫color屬性,重寫打印方法。
我們在main函數里面創建一個Child對象,並調用printColor方法。
那么,輸出的結果是什么呢?


答案是null。


很多人會問,你Child類構造函數都沒有初始化color,怎么可能有值。
慢着慢着,我們把這段代碼改改。

public class Parent{ public String color; public Parent(){ this.color="green"; } public void printColor(){ System.out.println(color); } public static void main(String args[]){ new Child().printColor(); } } class Child extends Parent{ public String color; public Child(){ } }

 

這段代碼里面我們只是刪掉了子類的printColor方法。運行,結果是green。 事情好像就不那么簡單了。 事實上,事情的關鍵點有三個。

  • 父類的構造方法是否在子類對象創建時調用?
  • 父類和子類擁有相同屬性時,是覆蓋還是共存?
  • 父類和子類printColor方法中的color究竟是誰的?父類的還是子類的?

我們一個一個來,首先是繼承時構造方法的調用順序。
是這樣的,在幾乎所有語言里面,如果想要正確運行一段代碼的,前提條件是這段代碼的所有依賴都能正常運行。所以,在搞定一段代碼之前,都要先搞定他的依賴關系。例如我們加載一個類的時候,要先加載完成類中所有引用到的其他類。對象的創建也是一樣,當我們創建一個對象時,首要任務就是“創建父類對象”,父類的構造方法也就是在這個時候調用的。
當我們“創建父類對象”之后明顯會碰到一個問題,父類對象創建並初始化所有屬性后,子類又打算再創建一個相同的屬性,此時會怎樣呢?
答案是:而是再創建一個該屬性。

於是一個子類對象里面就有了兩個同名的屬性,一個是父類創建並且初始化為green的color,一個是還沒有被初始化的子類的color。那么在printColor方法里面的color到底是哪一個呢?
這里要先說兩個規則:

1.“逐級查找,找到則停”。屬性和方法都可以適用這條規則。他的大意是,當我們需要調用一個方法或者屬性時,我們先看看自己有沒有,沒有的話,就去上級(父類)找找,直到找到為止。這也可以很好地解釋方法重寫是如何實現的。不過屬性和方法有一點區別,當子類對象向上轉型為父類對象后,調用同名方法調用的只可以是子類方法,調用屬性則調用父類屬性。
2.調用的是誰的方法,屬性查找的起點就是誰。這條規則的重點是在於判斷調用的究竟是誰的方法。當一個子類重寫父類方法時,調用的必然是子類方法,反之則調用的必然是父類的方法。
以上兩點看似非常容易理解,現在我們來結合前面的代碼理解一下。
第一段代碼,當我們調用printColor方法時。

  • 1. 先在child對象中查找printColor方法,很容易就在Child類中找到了,於是調用的方法必然是Child類中的方法。
  • 2. 查找color屬性,因為Child類中已經有color屬性,於是該屬性率先被查找到,並停止繼續查找,打印出來,為null。

第二段代碼

  • 1.先找到printColor方法,發現找不到,於是去父類找。
  • 2.在父類中找到了printColor方法,停止查找。
  • 3.找到color屬性,由於調用的是父類的方法,所有屬性查找起點為父類對象,在父類對象中成功找到初始化為green的color
  • 4.打印出green

如果以上兩個流程你都理解了,那么屬性繼承你基本上也就掌握了。總結一下關鍵點。

  1. 父類子類有相同屬性時,是在父類基礎上添加而非覆蓋。
  2. 方法和屬性調用時,是從當前類開始一直向上查找。找到就停止。
  3. 調用的是誰的方法,查找的起點就是誰。所以,准確判斷是哪個類的方法很重要。

問題1:當我們處在在第一段代碼中的情況,並且想調用父類的color屬性打印green方法怎么辦呢

使用super.color就可以訪問

同樣,使用super.printColor()可以調用父類的printColor()方法。

 

問題2:如果層級Parent還有一個父類PerParent也有相同屬性,我們在Child該怎么才能調用到?
super.super.color?
明顯不對。

此時我們只要向上轉型即可。(Perparent)child.color;

不過這里要注意的是,方法重寫后,使用(Parent)child.printColor()是無法訪問到父類方法的。

文章的結尾,我留下兩個習題,如果你看一眼能正確給出答案的話,我只能為你默默刷66個6666。

題1:

public class Parent{ public String color; public Parent(){ this.color="green"; } public void printColor(){ System.out.println(color); } public static void main(String args[]){ new Child().printColor(); } } class Child extends Parent{ //我們在這里刪掉color屬性 //public String color;
public Child(){ } public void printColor(){ System.out.println(color); } }

 

題2:

public class Parent{ public String color; public Parent(){ this.color="green"; } //在這里刪掉父類的printColor方法 /* public void printColor(){ System.out.println(color); }*/
public static void main(String args[]){ new Child().printColor(); } } class Child extends Parent{ //我們在這里刪掉color屬性 //public String color;
public Child(){ } public void printColor(){ System.out.println(color); } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM