java小心機(4)| 繼承與組合的愛恨情仇
在java中,有兩種主要複用程式碼的方法:繼承和組合。
繼承,是OOP的一大特性,想必大家都非常熟悉了;組合,其實也很常見,只是不知道它的名字罷了。
繼承
子類擁有父類的基本特性,需使用extend關鍵字實現,宣告某子類繼承於某父類
如下例子,麻雀繼承鳥類
//鳥類 public class Bird { public void eat(){ System.out.println("Bird eat"); } public void fly(){ System.out.println("Bird fly"); } } //麻雀 public class Sparrow extends Bird{ public static void main(String[] args){ Sparrow sparrow = new Sparrow(); sparrow.eat(); sparrow.fly(); } }
優缺點
優點
-
子類能自動繼承父類的介面
-
建立子類的物件時,無須建立父類的物件
缺點
-
破壞封裝,子類與父類之間緊密耦合,子類依賴於父類的實現,子類缺乏獨立性
-
支援擴充套件,但是往往以增加系統結構的複雜度為代價
-
不支援動態繼承。在執行時,子類無法選擇不同的父類
-
緊耦合
缺點分析
1.為什麼說破壞封裝性? 封裝是指將物件的狀態資訊隱藏在物件內部,不允許外部程式直接訪問物件內部的資訊,而是通過該類所提供的方法來實現對內部資訊的操作和訪問。
如下例子中父類Fruit中有成員變數weight。Apple繼承了Fruit之後,Apple可直接操作Fruit類的成員變數,因此破壞了封裝性!
public class Fruit { //成員變數 public double weight; public void info(){ System.out.println("我是一個水果!重" + weight + "g!"); } } public class Apple extends Fruit { public static void main(String[] args){ Apple a = new Apple(); a.weight = 10; a.info(); }
2.子類對父類的擴充套件往往會增加系統結構複雜度,繼承樹深度加深,結構越複雜
3.繼承不能支援動態繼承 因為繼承是編譯期就決定下來的,無法在執行時改變
4.為什麼說緊耦合? 意思是父類和子類的耦合性很高,比如說將父類的一個成員變數名稱修改了,子類用到這個變數的地方就需要做修改。 做為一個設計者,應當努力減小耦合關係。
組合
組合通常用於想在新類中使用現有類的功能,而非它的介面。 可能對於名字很陌生,但是用法很熟悉,看下面例子
//鳥類
public class Bird {
public void eat(){
System.out.println("Bird eat");
}
public void fly(){
System.out.println("Bird fly");
}
}
//麻雀
public class Sparrow {
private Bird bird = new Bird ();
public void eat(){
bird.eat();
}
public void fly(){
bird.fly();
}
public void walk(){
System.out.print("Sparrow walk");
}
public static void main(String[] args){
Sparrow sparrow = new Sparrow();
sparrow.eat();
sparrow.fly();
sparrow.walk();
}
}
優缺點
優點
-
不破壞封裝,整體類與區域性類之間鬆耦合,彼此相對獨立
-
具有較好的可擴充套件性
-
支援動態組合。在執行時,整體物件可以選擇不同型別的區域性物件
-
整體類可以對區域性類進行包裝,封裝區域性類的介面,提供新的介面
缺點
-
整體類不能自動獲得和區域性類同樣的介面
-
建立整體類的物件時,需要建立所有區域性類的物件
繼承與組合對比
相對於組合,繼承的優點:
1、在繼承中,子類自動繼承父類的非私有成員,在需要時,可選擇直接使用或重寫。
2、在繼承中,建立子類物件時,無需建立父類物件,因為系統會自動完成;而在組合中,建立組合類的物件時,通常需要建立其所使用的所有類的物件。
相對於整合,組合的優點:
1、在組合中,組合類與呼叫類之間低耦合;而在繼承中子類與父類高耦合。
2、可動態組合。
如何選擇
從前面的介紹已經優缺點對比中也可以看出,組合確實比繼承更加靈活,也更有助於程式碼維護。 所以,建議在同樣可行的情況下,優先使用組合而不是繼承。因為組合更安全,更簡單,更靈活,更高效。
面向物件中有一個比較重要的原則『多用組合、少用繼承』或者說『組合優於繼承』,這也是六大設計原則之一的合成複用原則。
那我們該如何判斷是否應該使用繼承呢?在java程式設計思想中提供了一個簡單的判斷方法,問一下自己“真的需要向上轉型嗎?”。 如果是必須的,則繼承是必要的。反之則應該好好考慮是否需要繼承。
擴充套件:向上轉型
即用父類引用指向子類物件
什麼時候用到向上轉型?
方法呼叫需要同時接受兩個子類的型別,這時就需要將他們的父類作為方法引數,使用向上轉型將子類轉換為父類型別
以上文中繼承的例子Fruit和Apple,新增Banner類和一個測試類,如下
public class Bananer extends Fruit {
}
public class Test{
public static void main(String[] args){
Fruit a = new Apple();//向上轉型
Fruit b = new Bananer ();//向上轉型
getWight(new Apple());//傳入子類,自動向上轉型
getWight(new Bananer ());//傳入子類,自動向上轉型
}
public void getWight(Fruit f){
System.out.println(f.wight)
}
}
ANDKS
- End -
一個立志成大腿而每天努力奮鬥的年輕人
伴學習伴成長,成長之路你並不孤單!