歡迎您光臨本站 註冊首頁

Java抽象類和介面和繼承之間關係

←手機掃碼閱讀     火星人 @ 2014-03-10 , reply:0
有時候,我們可能想要構造一個很抽象的父類對象,它可能僅僅代表一個分類或抽象概念,它的實例沒有任何意義,因此不希望它能被實例化.例如:有一個父類「 水果(Fruit)」,它有幾個子類「蘋果(Apple)」、「橘子(Orange)」、「香蕉(Banana)」等.水果在這裡僅僅只是作為一個分類,顯然水果的實例沒有什麼意義(就好像一個人如果告訴你他買了一些水果但是卻不告訴你是蘋果還是橘子,你很難想象他到底買的是什麼.).而水果類又要能被子類化,這就要求我們使用抽象類(abstract class)來解決這個問題.
在java中,通過在class關鍵字前增加abstract修飾符,就可以將一個類定義成抽象類.抽象類不能被實例化.例如:
定義抽象類水果(Fruit)
public abstract class Fruit {
……
}
如果我們試圖用以下語句來獲得一個實例,將無法編譯成功.
Fruit fruit = new Fruit();
而我們仍然可以構造水果類的子類,如:
子類「蘋果(Apple)」
public class Apple extends Fruit {
……
}
子類「橘子(Orange)」
public class Orange extends Fruit {
……
}
這樣就達到我們的目的了.
抽象類除了能象普通類一樣可以擁有一般的屬性和方法,也可以擁有抽象方法(abstract method).例如:
抽象類「形狀(Shape)」擁有抽象方法draw().
public abstract class Shape {
……
public abstract void draw();
……
}
抽象方法與抽象的行為相對應,通常是這個行為對父對象沒有意義,而子對象有具體動作.例如方法draw()對於類Shape沒有意義,而類Shape的子類矩形(Rectangle)的方法draw()可以有實際的動作(根據矩形的四個頂點畫出矩形的四個邊),子類圓(Circle)的方法draw()也可以有實際的動作(根據圓心和半徑畫出圓周).
抽象類可以有抽象方法也可以沒有抽象方法;但是如果一個類有抽象方法,那這個類只能定義為抽象類.
如果按照以下代碼類「形狀(Shape)」仍然擁有抽象方法draw(),但沒有定義為抽象類,將會編譯失敗.
public class Shape {
……
public abstract void draw();
……
}
抽象方法還有一個特點是,它強迫子類要麼仍然保持抽象性(即不具體實現該方法並仍然定義為抽象類),要麼具體表現出這個方法的行為(實現具體的動作或者通過拋出UnsupportedOperationException異常來表明不支持該行為).這樣也可以強化多態性.


上面簡要分析了抽象類,下面談談介面(interface).java語言使用關鍵字interface定義一個介面.介面也是抽象對象,它甚至比抽象類更抽象.介面中的方法都是抽象方法.
一個介面可以繼承其他介面;一個類通過關鍵字implements聲明要實現一個介面,並具體實現介面的方法.
例如:有一個介面InterfaceA,

Java代碼
public interface InterfaceA {
void methodA();
}

類ClassA實現介面InterfaceA.

Java代碼
public class ClassA implements InterfaceA {
public void methodA() {
System.out.println( "methodA of ClassA implements InterfaceA" );
}
}

如果是抽象類實現一個介面,那麼抽象類中可以不具體實現介面的方法(保持其抽象性),而由其子類去實現.
抽象類ClassB實現介面InterfaceA,但是沒有具體實現方法methodA(),

Java代碼
public abstract class ClassB { }

子類ClassBSub實現介面InterfaceA,但是沒有具體實現方法methodA(),

Java代碼
public class ClassBSub {
public void methodA() {
System.out.println( "methodA of ClassBSub the subclass of ClassB" );
}
}

介面和抽象類顯著的共同點是介面和抽象類都可以有抽象方法.
介面和抽象類的不同點有:
(1)抽象類可以有實例變數,而介面不能擁有實例變數,介面中的變數都是靜態(static)的常量(final).
(2)抽象類可以有非抽象方法,而介面只能有抽象方法.
java中,類與類之間是不能多繼承的.java之所以禁止類與類之間的多繼承是多繼承有很大的缺點.
多繼承雖然能使子類同時擁有多個父類的特徵,但是其缺點也是很顯著的,主要有兩方面:
(1)如果在一個子類繼承的多個父類中擁有相同名字的實例變數,子類在引用該變數時將產生歧義,無法判斷應該使用哪個父類的變數.例如:
類ClassA:

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

類ClassB:

Java代碼
public class ClassB {
protected int varSame = 1 ;
}

子類ClassC:(假設允許類與類之間多繼承)

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();
}
}




上面程序的運行結果會是什麼呢?輸出0還是1?
(2)如果在一個子類繼承的多個父類中擁有相同方法,子類中有沒有覆蓋該方法,那麼調用該方法時將產生歧義,無法判斷應該調用哪個父類的方法.例如:
類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:(假設允許類與類之間多繼承)

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行
}
}

上面程序的運行結果會是什麼呢?A、B、C三行的輸出是0還是1?
有以上的致命缺點,所以java中禁止一個類繼承多個父類;但是幸運的是java提供了介面,並能通過介面的功能獲得多繼承的許多優點而又摒棄了類與類多繼承的缺點.
java允許一個介面繼承多個父介面,也允許一個類實現多個介面,而這樣的多繼承有上面提到的缺點馬?
答案是沒有,這是由介面的抽象性決定的.
正如前面介紹的,在介面中不能有實例變數,只能有靜態的常量,不能有具體的方法(包含方法體),只能有抽象方法,因此也就摒棄了多繼承的缺點.
對於一個類實現多個介面的情況,介面只有抽象方法,具體方法只能由實現介面的類實現,在調用的時候始終只會調用實現類的方法(不存在歧義),因此不存在多繼承的第二個缺點;而又介面只有靜態的常量,但是由於靜態變數是在編譯期決定調用關係的,即使存在一定的衝突也會在編譯時提示出錯;而引用靜態變數一般直接使用類名或介面名,從而避免產生歧義,因此也不存在多繼承的第一個缺點.
對於一個介面繼承多個父介面的情況也一樣不存在這些缺點.
請看以下示例.
介面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實現介面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();
}


以上代碼不存在什麼問題,但是如果試圖編寫以下存在衝突的代碼,則會編譯失敗.

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


由於引入了介面,java顯得非常靈活,也是的java中的多態性更加富有魔力.


[火星人 ] Java抽象類和介面和繼承之間關係已經有726次圍觀

http://coctec.com/docs/java/show-post-62153.html