Java使用繼承時需要注意的二三事
阿新 • • 發佈:2018-12-10
繼承是實現類複用的重要手段,它能夠有效減少重複程式碼的數量,但同時也帶來一個最大的壞處:破壞封裝。
子類拓展父類時,子類從父類繼承得到了成員變數和方法,如果訪問許可權允許,子類可以直接訪問父類的成員變數和方法,相當於子類可以直接複用父類的成員變數和方法,十分便利。
但是在繼承關係中,子類可以直接訪問父類的內部資訊和方法,導致了父類和子類的嚴重耦合。從這個角度看,父類的實現細節對子類不再透明,子類可以訪問父類的成員變數和方法,可以隨意改變父類的實現細節(比如通過方法重寫來改變父類的實現方法),從而導致子類可以惡意篡改父類的方法。
示例如下:
public class Main { public static void main(String[] args){ person p = new student(); p.talk();//最終輸出I'm a student } } class person{ public void talk(){ System.out.print("I'm a person"); } } class student extends person{ public void talk(){ System.out.print("I'm a student"); } }
可以看到,儘管上面申明的是person引用變數,但是實際上引用的是一個student物件。所以在呼叫p的talk()方法時執行的不再是person的talk()方法,而是student的talk()方法。
為了保證良好的封裝性,不會被子類隨意更改,設計父類時應該遵循如下規則:
- 儘量隱藏父類的內部資料。把父類的所有成員變數設定為private訪問型別,不要讓子類直接訪問父類的成員變數。
- 不要讓子類隨意訪問、修改父類的方法。父類中的那些僅為輔助其他的工具方法應該使用private訪問控制符修飾。如果父類中的方法需要被外部類呼叫,但又不希望子類重寫,則要使用final修飾符來修飾該方法。如果希望該方法被子類重寫但不被外部類呼叫,則應該使用protected來修飾該方法。
- 儘量不要在父類構造器呼叫要被子類重寫的方法。 關於最後一點,我們使用一個例子來演示:
public class Main { public static void main(String[] args){ student p = new student();//輸出子類重寫父類方法子類重寫父類方法 } } class person{ public void talk(){ System.out.print("父類方法"); } public person(){ talk(); } } class student extends person{ public void talk(){ System.out.print("子類重寫父類方法"); } public student(){ talk(); } }
當系統嘗試建立student物件時,會執行其父類構造器,如果父類構造器呼叫了被其子類重寫的方法,則變成呼叫被子類重寫的方法。
如果想把某些類設定為最終類,即不能被當成父類,則可以使用final修飾這個類。除此之外,使用private修飾這個類的所有構造器也可以使得子類無法呼叫該類的構造器,也就無法繼承該類。對於把所有構造器使用private修飾的父類而言,可以另外提供一個靜態方法,用於建立該類例項。
那麼究竟在什麼時候需要從父類派生新的子類呢?
不僅需要保證子類是一種特殊的父類,還要具備以下兩個條件之一:
- 子類需要增加額外的屬性,而不僅僅是屬性值的改變。
- 子類需要增加自己的獨特行為(包括增加新的方法或者重寫父類方法)