Java中的複用類——繼承、組合和代理

一:繼承

  繼承按現有類創建一個對象,不改變現有類,採用現有類的形式向其中添加新代碼;(編譯器強制你去初始化基類,是is_a 的關係,比如說Student繼承Person,則說明Student is a Person。

繼承的優點是子類可以重寫父類的方法來方便地實現對父類的擴展。

  繼承的缺點有以下幾點:

  ①:父類的內部細節對子類是可見的。

  ②:子類從父類繼承的方法在編譯時就確定下來了,所以無法在運行期間改變從父類繼承的方法的行爲。

  ③:如果對父類的方法做了修改的話(比如增加了一個參數),則子類的方法必須做出相應的修改。所以說子類與父類是一種高耦合,違背了面向對象思想。

二:組合

  組合是在新類中使用現有類的對象也就是設計類的時候把要組合的類的對象加入到該類中作爲自己的成員變量。組合是has_a 的關係。

  組合的優點:

  ①:當前對象只能通過所包含的那個對象去調用其方法,所以所包含的對象的內部細節對當前對象時不可見的。

  ②:當前對象與包含的對象是一個低耦合關係,如果修改包含對象的類中代碼不需要修改當前對象類的代碼。

  ③:當前對象可以在運行時動態的綁定所包含的對象。可以通過set方法給所包含對象賦值。

  組合的缺點:①:容易產生過多的對象。②:爲了能組合多個對象,必須仔細對接口進行定義。

由此可見,組合比繼承更具靈活性和穩定性,所以在設計的時候優先使用組合。

只有當下列條件滿足時才考慮使用繼承:

子類是一種特殊的類型,而不只是父類的一個角色 
子類的實例不需要變成另一個類的對象 
子類擴展,而不是覆蓋或者使父類的功能失效

InheritTest.java 使用繼承方式實現目標

[java] view plain copy
  1. //InheritTest.java 使用繼承方式實現目標  
  2. class Animal{  
  3.     private void beat(){  
  4.         System.out.println("心臟跳動...");  
  5.     }  
  6.     public void breath(){  
  7.         beat();  
  8.         System.out.println("吸一口氣,呼一口氣,呼吸中...");  
  9.     }  
  10. }  
  11. //繼承Animal,直接複用父類的breath()方法  
  12. class Bird extends Animal{  
  13.     //創建子類獨有的方法fly()  
  14.     public void fly(){  
  15.         System.out.println("我是鳥,我在天空中自由的飛翔...");  
  16.     }  
  17. }  
  18. //繼承Animal,直接複用父類的breath()方法  
  19. class Wolf extends Animal{  
  20.     //創建子類獨有的方法run()  
  21.     public void run(){  
  22.         System.out.println("我是狼,我在草原上快速奔跑...");  
  23.     }  
  24. }  
  25. public class InheritTest{  
  26.     public static void main(String[] args){  
  27.         //創建繼承自Animal的Bird對象新實例b  
  28.         Bird b=new Bird();  
  29.         //新對象實例b可以breath()  
  30.         b.breath();  
  31.         //新對象實例b可以fly()  
  32.         b.fly();  
  33.         Wolf w=new Wolf();  
  34.         w.breath();  
  35.         w.run();  
  36. /* 
  37. ---------- 運行Java程序 ---------- 
  38. 心臟跳動... 
  39. 吸一口氣,呼一口氣,呼吸中... 
  40. 我是鳥,我在天空中自由的飛翔... 
  41. 心臟跳動... 
  42. 吸一口氣,呼一口氣,呼吸中... 
  43. 我是狼,我在草原上快速奔跑... 
  44.  
  45. 輸出完畢 (耗時 0 秒) - 正常終止 
  46. */  
  47.     }  
  48. }  


CompositeTest.java 使用組合方式實現目標

[java] view plain copy
  1. //CompositeTest.java  使用組合方式實現目標  
  2. class Animal{  
  3.     private void beat(){  
  4.         System.out.println("心臟跳動...");  
  5.     }  
  6.     public void breath(){  
  7.         beat();  
  8.         System.out.println("吸一口氣,呼一口氣,呼吸中...");  
  9.     }  
  10. }  
  11. class Bird{  
  12.     //定義一個Animal成員變量,以供組合之用  
  13.     private Animal a;  
  14.     //使用構造函數初始化成員變量  
  15.     public Bird(Animal a){  
  16.         this.a=a;  
  17.     }  
  18.     //通過調用成員變量的固有方法(a.breath())使新類具有相同的功能(breath())  
  19.     public void breath(){  
  20.         a.breath();  
  21.     }  
  22.     //爲新類增加新的方法  
  23.     public void fly(){  
  24.         System.out.println("我是鳥,我在天空中自由的飛翔...");  
  25.     }  
  26. }  
  27. class Wolf{  
  28.     private Animal a;  
  29.     public Wolf(Animal a){  
  30.         this.a=a;  
  31.     }  
  32.     public void breath(){  
  33.         a.breath();  
  34.     }  
  35.     public void run(){  
  36.         System.out.println("我是狼,我在草原上快速奔跑...");       
  37.     }  
  38. }  
  39. public class CompositeTest{  
  40.     public static void main(String[] args){  
  41.         //顯式創建被組合的對象實例a1  
  42.         Animal a1=new Animal();  
  43.         //以a1爲基礎組合出新對象實例b  
  44.         Bird b=new Bird(a1);  
  45.         //新對象實例b可以breath()  
  46.         b.breath();  
  47.         //新對象實例b可以fly()  
  48.         b.fly();  
  49.         Animal a2=new Animal();  
  50.         Wolf w=new Wolf(a2);  
  51.         w.breath();  
  52.         w.run();  
  53. /* 
  54. ---------- 運行Java程序 ---------- 
  55. 心臟跳動... 
  56. 吸一口氣,呼一口氣,呼吸中... 
  57. 我是鳥,我在天空中自由的飛翔... 
  58. 心臟跳動... 
  59. 吸一口氣,呼一口氣,呼吸中... 
  60. 我是狼,我在草原上快速奔跑... 
  61.  
  62. 輸出完畢 (耗時 0 秒) - 正常終止 
  63. */  
  64.     }  
  65. }  
關於組合和繼承的總結:

組合和繼承都允許在新的類中放置子對象,組合是顯式地這樣做,而繼承則是隱式地這樣做。

繼承是"is-a"關係

組合是"has-a"關係


1)組合(has-a)關係可以顯式地獲得被包含類(繼承中稱爲父類)的對象,而繼承(is-a)則是隱式地獲得父類的對象,被包含類和父類對應,而組合外部類和子類對應。


2)組合關係在運行期決定,而繼承關係在編譯期就已經決定了。


3)組合是在組合類和被包含類之間的一種鬆耦合關係,而繼承則是父類和子類之間的一種緊耦合關係。

4)當選擇使用組合關係時,在組合類中包含了外部類的對象,組合類可以調用外部類必須的方法,而使用繼承關係時,父類的所有方法和變量都被子類無條件繼承,子類不能選擇。

5)最重要的一點,使用繼承關係時,可以實現類型的回溯,即用父類變量引用子類對象,這樣便可以實現多態,而組合沒有這個特性。

6)還有一點需要注意,如果你確定複用另外一個類的方法永遠不需要改變時,應該使用組合,因爲組合只是簡單地複用被包含類的接口,而繼承除了複用父類的接口外,它甚至還可以覆蓋這些接口,修改父類接口的默認實現,這個特性是組合所不具有的。

7)從邏輯上看,組合最主要地體現的是一種整體和部分的思想,例如在電腦類是由內存類,CPU類,硬盤類等等組成的,而繼承則體現的是一種可以回溯的父子關係,子類也是父類的一個對象。

 

8)這兩者的區別主要體現在類的抽象階段,在分析類之間的關係時就應該確定是採用組合還是採用繼承。

 

9)引用網友的一句很經典的話應該更能讓大家分清繼承和組合的區別:組合可以被說成“我請了個老頭在我家裏幹活” ,繼承則是“我父親在家裏幫我幹活"。

 10)組合技術通常是用於在新類中使用現有類的功能而非它的接口的情形。

三、代理

代理是繼承和組合的中庸之道。將一個成員對象置於要構造的類中(就像組合),與此同時在新類中暴露了該類成員所有的方法(就像繼承)。

(組合+繼承)java中沒有直接的支持代理,(將基類對象作爲代理類的成員,而代理類有對應於基類的所有方法,各方法內使用基類對象成員調用基類的方法。

代理的實例一:

[java] view plain copy
  1. public class test {    
  2.     public static void main(String[] args) {    
  3.         Demo2 d2=new Demo2();    
  4.         d2.print();    
  5.         //組合    
  6.         Demo1 d1=new Demo1();    
  7.         d1.method();    
  8.         //代理    
  9.         Demo3 d3=new Demo3();    
  10.         d3.stop();    
  11.     }    
  12. }    
  13. class Demo{    
  14.     Demo(){System.out.println("hello world!");}    
  15.     public static void stop(){System.out.println("Hello stop!");}    
  16. }    
  17. class Demo1{//組合機制    
  18.     public static void method(){    
  19.         Demo d=new Demo();//在新類中創建現有類的對象    
  20.     }    
  21. }    
  22. class Demo2 extends Demo{//繼承機制,按照現有類來創建新類,無需改變現有類的形式,採用現有類的形式並且在其中添加新代碼    
  23.     public static void print(){    
  24.         System.out.println("YES!");    
  25.     }    
  26. }    
  27. class Demo3{    
  28.     private Demo d=new Demo();    
  29.     public void stop(){//代理,既有繼承功能 又有組合功能    
  30.         System.out.println("Hello 繼承!");    
  31.         d.stop(); }}  





輸出結果:


hello world!

YES!

hello world!

hello world!

hello 繼承!

hello stop!


圖文總結:




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章