隨筆-199  評(píng)論-203  文章-11  trackbacks-0
          有時(shí)候,我們可能想要構(gòu)造一個(gè)很抽象的父類對(duì)象,它可能僅僅代表一個(gè)分類或抽象概念,它的實(shí)例沒有任何意義,因此不希望它能被實(shí)例化。例如:有一個(gè)父類“ 水果(Fruit)”,它有幾個(gè)子類“蘋果(Apple)”、“橘子(Orange)”、“香蕉(Banana)”等。水果在這里僅僅只是作為一個(gè)分類,顯然水果的實(shí)例沒有什么意義(就好像一個(gè)人如果告訴你他買了一些水果但是卻不告訴你是蘋果還是橘子,你很難想象他到底買的是什么。)。而水果類又要能被子類化,這就要求我們使用抽象類(abstract class)來解決這個(gè)問題。
          在java中,通過在class關(guān)鍵字前增加abstract修飾符,就可以將一個(gè)類定義成抽象類。抽象類不能被實(shí)例化。例如:
                    定義抽象類水果(Fruit)
                    public abstract class Fruit {
                            ……
                    }
                     如果我們?cè)噲D用以下語句來獲得一個(gè)實(shí)例,將無法編譯成功。
                     Fruit fruit = new Fruit();
          而我們?nèi)匀豢梢詷?gòu)造水果類的子類,如:
                    子類“蘋果(Apple)”
                     public class Apple extends Fruit {
                             ……
                     }
                    子類“橘子(Orange)”
                     public class Orange extends Fruit {
                             ……
                     }
          這樣就達(dá)到我們的目的了。
          抽象類除了能象普通類一樣可以擁有一般的屬性和方法,也可以擁有抽象方法(abstract method)。例如:
                     抽象類“形狀(Shape)”擁有抽象方法draw()。
                     public abstract class Shape {
                            ……
                            public abstract void draw();
                            ……
                     }
          抽象方法與抽象的行為相對(duì)應(yīng),通常是這個(gè)行為對(duì)父對(duì)象沒有意義,而子對(duì)象有具體動(dòng)作。例如方法draw()對(duì)于類Shape沒有意義,而類Shape的子類矩形(Rectangle)的方法draw()可以有實(shí)際的動(dòng)作(根據(jù)矩形的四個(gè)頂點(diǎn)畫出矩形的四個(gè)邊),子類圓(Circle)的方法draw()也可以有實(shí)際的動(dòng)作(根據(jù)圓心和半徑畫出圓周)。
          抽象類可以有抽象方法也可以沒有抽象方法;但是如果一個(gè)類有抽象方法,那這個(gè)類只能定義為抽象類。
                     如果按照以下代碼類“形狀(Shape)”仍然擁有抽象方法draw(),但沒有定義為抽象類,將會(huì)編譯失敗。
                     public class Shape {
                            ……
                            public abstract void draw();
                            ……
                     }
          抽象方法還有一個(gè)特點(diǎn)是,它強(qiáng)迫子類要么仍然保持抽象性(即不具體實(shí)現(xiàn)該方法并仍然定義為抽象類),要么具體表現(xiàn)出這個(gè)方法的行為(實(shí)現(xiàn)具體的動(dòng)作或者通過拋出UnsupportedOperationException異常來表明不支持該行為)。這樣也可以強(qiáng)化多態(tài)性。
          上面簡(jiǎn)要分析了抽象類,下面談?wù)劷涌冢╥nterface)。java語言使用關(guān)鍵字interface定義一個(gè)接口。接口也是抽象對(duì)象,它甚至比抽象類更抽象。接口中的方法都是抽象方法。
          一個(gè)接口可以繼承其他接口;一個(gè)類通過關(guān)鍵字implements聲明要實(shí)現(xiàn)一個(gè)接口,并具體實(shí)現(xiàn)接口的方法。
                     例如:有一個(gè)接口InterfaceA,

          Java代碼 
          public   interface  InterfaceA {    
                   void  methodA();    
          }   
            
                     類ClassA實(shí)現(xiàn)接口InterfaceA。

          Java代碼 
          public   class  ClassA implements InterfaceA {    
                    public   void  methodA() {    
                         System.out.println( "methodA of ClassA implements InterfaceA" );    
                   }    
          }   
            
          如果是抽象類實(shí)現(xiàn)一個(gè)接口,那么抽象類中可以不具體實(shí)現(xiàn)接口的方法(保持其抽象性),而由其子類去實(shí)現(xiàn)。
                     抽象類ClassB實(shí)現(xiàn)接口InterfaceA,但是沒有具體實(shí)現(xiàn)方法methodA(),

          Java代碼 
          public   abstract   class  ClassB  {           }   
            
                     子類ClassBSub實(shí)現(xiàn)接口InterfaceA,但是沒有具體實(shí)現(xiàn)方法methodA(),

          Java代碼 
          public   class  ClassBSub {    
                   public   void  methodA() {    
                        System.out.println( "methodA of ClassBSub the subclass of ClassB" );    
                  }    
          }   
            
          接口和抽象類顯著的共同點(diǎn)是接口和抽象類都可以有抽象方法。
          接口和抽象類的不同點(diǎn)有:
                     (1)抽象類可以有實(shí)例變量,而接口不能擁有實(shí)例變量,接口中的變量都是靜態(tài)(static)的常量(final)。
                     (2)抽象類可以有非抽象方法,而接口只能有抽象方法。
          java中,類與類之間是不能多繼承的。java之所以禁止類與類之間的多繼承是因?yàn)槎嗬^承有很大的缺點(diǎn)。
          多繼承雖然能使子類同時(shí)擁有多個(gè)父類的特征,但是其缺點(diǎn)也是很顯著的,主要有兩方面:
          (1)如果在一個(gè)子類繼承的多個(gè)父類中擁有相同名字的實(shí)例變量,子類在引用該變量時(shí)將產(chǎn)生歧義,無法判斷應(yīng)該使用哪個(gè)父類的變量。例如:
                     類ClassA:

          Java代碼 
          public   class  ClassA {    
                  protected   int  varSame =  0 ;    
          }   
            
                     類ClassB:

          Java代碼 
          public   class  ClassB {    
                   protected   int  varSame =  1 ;    
          }   
            
                     子類ClassC:(假設(shè)允許類與類之間多繼承)

          Java代碼 
          public   class  ClassC  extends  ClassA, ClassB {    
                   public   void  printOut() {    
                          System.out.println( super .varSame);    
                  }    
                   public   static   void  main(String[] args) {    
                          ClassC classC =  new  ClassC();    
                          classC.printOut();    
                  }    
          }   
            

                     上面程序的運(yùn)行結(jié)果會(huì)是什么呢?輸出0還是1?
          (2)如果在一個(gè)子類繼承的多個(gè)父類中擁有相同方法,子類中有沒有覆蓋該方法,那么調(diào)用該方法時(shí)將產(chǎn)生歧義,無法判斷應(yīng)該調(diào)用哪個(gè)父類的方法。例如:
                     類ClassA:

          Java代碼 
          public   class  ClassA {    
                   public   void  printOut() {    
                          System.out.println( 0 );    
                  }    
          }   
           

                     類ClassB:

           

          Java代碼 
          public   class  ClassB {    
                   public   void  printOut() {    
                          System.out.println( 1 );    
                  }    
          }   
            
                     子類ClassC:(假設(shè)允許類與類之間多繼承)

          Java代碼 
          public   class  ClassC  extends  ClassA, ClassB {    
                              public   static   void  main(String[] args) {    
                                     ClassA classA =  new  ClassC();    
                                     classA.printOut();       // -------------------------  A行    
                                     ClassB classB =  new  ClassC();    
                                     classB.printOut();       // -------------------------  B行    
                                     ClassC classC =  new  ClassC();    
                                     classC.printOut();        //-------------------------  C行    
                             }    
                     }   
            
                     上面程序的運(yùn)行結(jié)果會(huì)是什么呢?A、B、C三行的輸出是0還是1?
          正因?yàn)橛幸陨系闹旅秉c(diǎn),所以java中禁止一個(gè)類繼承多個(gè)父類;但是幸運(yùn)的是java提供了接口,并能通過接口的功能獲得多繼承的許多優(yōu)點(diǎn)而又摒棄了類與類多繼承的缺點(diǎn)。
          java允許一個(gè)接口繼承多個(gè)父接口,也允許一個(gè)類實(shí)現(xiàn)多個(gè)接口,而這樣的多繼承有上面提到的缺點(diǎn)馬?
          答案是沒有,這是由接口的抽象性決定的。
          正如前面介紹的,在接口中不能有實(shí)例變量,只能有靜態(tài)的常量,不能有具體的方法(包含方法體),只能有抽象方法,因此也就摒棄了多繼承的缺點(diǎn)。
          對(duì)于一個(gè)類實(shí)現(xiàn)多個(gè)接口的情況,因?yàn)榻涌谥挥谐橄蠓椒ǎ唧w方法只能由實(shí)現(xiàn)接口的類實(shí)現(xiàn),在調(diào)用的時(shí)候始終只會(huì)調(diào)用實(shí)現(xiàn)類的方法(不存在歧義),因此不存在多繼承的第二個(gè)缺點(diǎn);而又因?yàn)榻涌谥挥徐o態(tài)的常量,但是由于靜態(tài)變量是在編譯期決定調(diào)用關(guān)系的,即使存在一定的沖突也會(huì)在編譯時(shí)提示出錯(cuò);而引用靜態(tài)變量一般直接使用類名或接口名,從而避免產(chǎn)生歧義,因此也不存在多繼承的第一個(gè)缺點(diǎn)。
          對(duì)于一個(gè)接口繼承多個(gè)父接口的情況也一樣不存在這些缺點(diǎn)。
          請(qǐng)看以下示例。
                      接口A:

          Java代碼 
          public   interface  InterfaceA {    
                   int  len =  1 ;    
                   void  output();    
          }   
            
                      接口B:

          Java代碼 
          public   interface  InterfaceB {    
                     int  len =  2 ;    
                     void  output();    
          }   
            
                      接口Sub繼承接口A和接口B:

          Java代碼 
          public   interface  InterfaceSub  extends  InterfaceA, interfaceB {            }   
            
                      類Xyz實(shí)現(xiàn)接口Sub:

          Java代碼 
          public   class  Xyz  implements  InterfaceSub {    
                   public   void  output() {    
                          System.out.println( "output in class Xyz." );    
                  }    
                    public   void  outputLen( int  type) {    
                            switch (type) {    
                                    case  InterfaceA.len:    
                                           System.out.println( "len of InterfaceA=." +type);    
                                            break ;    
                                    case  InterfaceB.len:    
                                           System.out.println( "len of InterfaceB=." +type);    
                                            break ;    
                           }    
                  }    
                  public   static   void  main(String[] args) {    
                         Xyz xyz=  new  Xyz ();    
                         xyz .output();    
                         xyz .outputLen();    
                 }    
            

                     以上代碼不存在什么問題,但是如果試圖編寫以下存在沖突的代碼,則會(huì)編譯失敗。

          Java代碼 
          Xyz xyz =  new  Xyz();    
          int  len = xyz.len;    
          System.out.println(len);   
            

          由于引入了接口,java顯得非常靈活,也使得java中的多態(tài)性更加富有魔力。
          posted on 2009-04-10 08:18 Werther 閱讀(1894) 評(píng)論(3)  編輯  收藏 所屬分類: 10.Java

          評(píng)論:
          # re: Java抽象類和接口和繼承之間關(guān)系 [未登錄] 2009-04-10 14:23 | lip
          很好狠好,講的不錯(cuò)~~  回復(fù)  更多評(píng)論
            
          # re: Java抽象類和接口和繼承之間關(guān)系 [未登錄] 2009-04-10 23:59 | stanleyxu2005
          去關(guān)心一下d語言的mixin吧。這個(gè)是一個(gè)新的解決方案。  回復(fù)  更多評(píng)論
            
          # re: Java抽象類和接口和繼承之間關(guān)系 2009-04-13 12:30 | Java蜘蛛人 --鄭成橋
          說實(shí)話寫的還可以,適合新人,但是對(duì)于老鳥,視乎沒有寫全面,接口的好處是什么? 為什么使用接口? 這些加上去。 Java 與模式里 寫了的  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 兴隆县| 禹州市| 昌宁县| 桃源县| 高阳县| 巴彦淖尔市| 缙云县| 乌审旗| 淮安市| 平泉县| 阳东县| 丹棱县| 禹州市| 朝阳区| 延川县| 肇东市| 措美县| 义乌市| 扶绥县| 晋中市| 富源县| 和田县| 洮南市| 临桂县| 扎鲁特旗| 凭祥市| 红河县| 和静县| 黄浦区| 将乐县| 阳朔县| 安龙县| 横山县| 托克逊县| 绥化市| 沭阳县| 阜新市| 睢宁县| 高雄市| 仪陇县| 浠水县|