接口與抽象類的比較
1.相同點:
A. 兩者都是抽象類,都不能實例化。
B. interface實現類及abstrct class的子類都必須要實現已經聲明的抽象方法。
2. 不同點:
A. interface需要實現,要用implements,而abstract class需要繼承,要用extends。
B. 一個類可以實現多個interface,但一個類只能繼承一個abstract class。
C. interface強調特定功能的實現,而abstract class強調所屬關係。
D.儘管interface實現類及abstrct class的子類都必須要實現相應的抽象方法,但實現的形式不同。
interface中的每一個方法都是抽象方法,都只是聲明的
(declaration, 沒有方法體),實現類必須要實現。而abstract class的子類可以有選擇地實現。這個選擇有兩點含義:
一是Abastract class中並非所有的方法都是抽象的,只有那些冠有abstract的方法纔是抽象的,子類必須實現。那些沒有abstract的方法,在Abstrct class中必須定義方法體。
二是abstract class的子類在繼承它時,對非抽象方法既可以直接繼承,也可以覆蓋;而對抽象方法,可以選擇實現,也可以通過再次聲明其方法爲抽象的方式,無需實現,留給其子類來實現,但此類必須也聲明爲抽象類。既是抽象類,當然也不能實例化。
E. abstract class是interface與Class的中介。
interface
是完全抽象的,只能聲明方法,而且只能聲明pulic的方法,不能聲明private及protected的方法,不能定義方法體,也
不能聲明實例變量。然而,interface卻可以聲明常量變量,並且在JDK中不難找出這種例子。但將常量變量放在interface中違背了其作爲接
口的作用而存在的宗旨,也混淆了interface與類的不同價值。如果的確需要,可以將其放在相應的abstract class或Class中。
abstract
class在interface及Class中起到了承上啓下的作用。一方面,abstract
class是抽象的,可以聲明抽象方法,以規範子類必須實現的功能;另一方面,它又可以定義缺省的方法體,供子類直接使用或覆蓋。另外,它還可以定義自己
的實例變量,以供子類通過繼承來使用。
3、interface的應用場合
A. 類與類之前需要特定的接口進行協調,而不在乎其如何實現。
B. 作爲能夠實現特定功能的標識存在,也可以是什麼接口方法都沒有的純粹標識。
C. 需要將一組類視爲單一的類,而調用者只通過接口來與這組類發生聯繫。
D. 需要實現特定的多項功能,而這些功能之間可能完全沒有任何聯繫。
4. abstract class的應用場合
一句話,在既需要統一的接口,又需要實例變量或缺省的方法的情況下,就可以使用它。最常見的有:
A. 定義了一組接口,但又不想強迫每個實現類都必須實現所有的接口。可以用abstract class定義一組方法體,甚至可以是空方法體,然後由子類選擇自己所感興趣的方法來覆蓋。
B. 某些場合下,只靠純粹的接口不能滿足類與類之間的協調,還必需類中表示狀態的變量來區別不同的關係。abstract的中介作用可以很好地滿足這一點。
C. 規範了一組相互協調的方法,其中一些方法是共同的,與狀態無關的,可以共享的,無需子類分別實現;而另一些方法卻需要各個子類根據自己特定的狀態來實現特定的功能。
接口與抽象接口
interface實際上就是一個抽象的,再加上abstract修飾沒有多大含義,唯一可能看到這種情況就是用反編譯工具生成的代碼,而java源碼種通常都標明瞭abstract和interface同時修飾一個接口,事實上這完全沒必要
簡要說說 接口,抽象類,實體類的用法
下面參考:http://www.cnblogs.com/iyangyuan/archive/2013/03/11/2954808.html
在程序設計過程中,讀者很可能遇到這樣一種困境:設計了一個接口,但實現這個接口的子類並不需要實現接口中的全部方法,也就是說,接口中的方法過多,對於某些子類是多餘的,我們不得不浪費的寫上一個空的實現。
今天提到的“抽象接口”,就是用來解決這個問題的。
爲了不誤導讀者,先說明一下,什麼是“抽象接口”。
所謂“抽象接口”,即在提供接口的同時,提供一個抽象類,用抽象類實現該接口(實際上這是缺省適配模式)。
下面舉個例子,讓讀者體會這樣做的好處。先上一張類圖:
具體代碼:
ITestInterface.java
1 /* 2 假設有一個頂層接口 3 */ 4 public interface ITestInterface{ 5 void method1(); 6 int method2(); 7 boolean method3(); 8 }
TestAbstract.java
1 /* 2 抽象類abstract實現了ITestInterface頂層接口 3 */ 4 5 public abstract class TestAbstract implements ITestInterface{ 6 //找出接口中必要的方法,也就是子類必須實現的方法,定義成抽象方法,交由子類實現 7 public abstract void method1(); 8 public abstract int method2(); 9 10 //一些獨特的方法可以在抽象類中默認實現 11 public boolean method3(){ 12 return true; 13 } 14 }
TestClass1.java
1 /* 2 普通類TestClass1繼承了TestAbstract抽象類 3 */ 4 5 public class TestClass1 extends TestAbstract{ 6 7 //TestClass1必須實現抽象的method1方法,該方法最早是接口中定義的 8 public void method1(){ 9 10 } 11 //TestClass1必須實現抽象的method2方法,該方法最早是接口中定義的 12 public int method2(){ 13 return 1; 14 } 15 16 //接口中的method3方法對於TestClass1無關緊要,因此不做重寫。 17 }
TestClass2.java
1 /* 2 普通類TestClass2繼承了TestAbstract抽象類 3 */ 4 5 public class TestClass2 extends TestAbstract{ 6 7 //TestClass2必須實現抽象的method1方法,該方法最早是接口中定義的 8 public void method1(){ 9 10 } 11 //TestClass2必須實現抽象的method2方法,該方法最早是接口中定義的 12 public int method2(){ 13 return 2; 14 } 15 16 //method3方法對於TestClass2來說至關重要,因此必須重寫。 17 public boolean method3(){ 18 return false; 19 } 20 21 }
代碼精講:
從以上例子可以看出,最高層的接口被一個抽象類實現,在抽象類中,我們把關鍵的method1、method2方法定義成抽象方法,強制子類去實現,而“獨特”的method3方法在抽象類中做一個默認實現。
等到TestClass1、TestClass2繼承TestAbstract抽象類時,優勢就體現出來了,TestClass1、TestClass2必須實現method1、method2,但如果用不到method3,可以直接無視。
通過接口和抽象類的結合,避免了在實現接口的子類中出現大量的“無意義”實現,這個“無意義”實現,被緩衝到了抽象類中,完美展現了代碼複用(可以把抽象類理解成接口和實現類之間的緩衝)。
需要指出的是,我們既可以選擇繼承抽象類,也可以選擇實現接口,並不是說一定要繼承抽象類,看情況而定,這裏是兩種選擇,兩個機會。
寫到這,或許讀者覺得文章已經結束了,其實沒有。。。
這樣做的好處不僅僅是這一點,細細品味,假如我們向接口中增加了一個方法。。。
具體代碼:
溫馨提示:不要被代碼嚇到,其實這些代碼和上邊的差不多,只不過加了個方法而已。
ITestInterface.java
1 /* 2 假設有一個頂層接口 3 */ 4 public interface ITestInterface{ 5 void method1(); 6 int method2(); 7 boolean method3(); 8 //接口中新增加了方法 9 String method4(); 10 }
TestAbstract.java
1 /* 2 抽象類abstract實現了ITestInterface頂層接口 3 */ 4 5 public abstract class TestAbstract implements ITestInterface{ 6 //找出接口中必要的方法,也就是子類必須實現的方法,定義成抽象方法,交由子類實現 7 public abstract void method1(); 8 public abstract int method2(); 9 10 //一些獨特的方法可以在抽象類中默認實現 11 public boolean method3(){ 12 return true; 13 } 14 15 //抽象類中提供一個默認實現,這樣就可以避免"驚動"所有子類 16 public String method4(){ 17 return ""; 18 } 19 }
TestClass1.java
1 /* 2 普通類TestClass1繼承了TestAbstract抽象類 3 */ 4 5 public class TestClass1 extends TestAbstract{ 6 7 //TestClass1必須實現抽象的method1方法,該方法最早是接口中定義的 8 public void method1(){ 9 10 } 11 //TestClass1必須實現抽象的method2方法,該方法最早是接口中定義的 12 public int method2(){ 13 return 1; 14 } 15 16 //接口中的method3方法對於TestClass1無關緊要,因此不做重寫。 17 18 //新增的方法對於TestClass1來說至關重要,因此必須重寫 19 public String method4(){ 20 return "Class1"; 21 } 22 23 }
TestClass2.java
1 /* 2 普通類TestClass2繼承了TestAbstract抽象類 3 */ 4 5 public class TestClass2 extends TestAbstract{ 6 7 //TestClass2必須實現抽象的method1方法,該方法最早是接口中定義的 8 public void method1(){ 9 10 } 11 //TestClass2必須實現抽象的method2方法,該方法最早是接口中定義的 12 public int method2(){ 13 return 2; 14 } 15 16 //method3方法對於TestClass2來說至關重要,因此必須重寫。 17 public boolean method3(){ 18 return false; 19 } 20 21 //新增的方法對於TestClass2來說無關緊要,無需知道新增method4的存在 22 }
代碼精講:
這段代碼演示了假如項目已經成型,但是需求有變,我們不得不向接口中增加一個新的方法,假如子類直接實現了接口,那麼這些子類都要修改,來實現接口新增的方法。
但本例中的TestClass1、TestClass2子類沒有直接實現接口,而是通過繼承抽象類間接實現接口,這樣好處一下就體現出來了!
向接口中新增的方法,可以在實現接口的抽象類中緩衝一下,提供一個默認的實現,這樣一來,就不必強制所有的子類(通過繼承抽象類間接實現接口的類)都進行修改,可以形象的理解爲“沒有驚動子類”。而需要使用這個方法的子類,直接重寫即可。
人類的智慧真偉大!數組和鏈表結合,產生了高效的哈希表;接口和抽象類結合,產生了優雅的缺省適配模式。大家努力吧!!!
寫在後面的話:
世間沒有完美的事物,設計模式也是如此,過多的討論優缺點沒有意義,合適的就是最好的,什麼是合適的呢?這纔是體現智慧的地方。