接口和抽象類
簡要說明
當初在學習Java的時候,對於接口和抽象類的概念不是十分清晰,感覺二者幾乎一樣,沒有什麼本質的區別,在這個假期第二次讀了《Android源碼設計模式解析與實戰》後,深刻認識到了抽象類和接口的重要性,在這裏和大家一起梳理一下二者。
參考資料
- http://www.cnblogs.com/dolphin0520/p/3811437.html
- http://blog.csdn.net/chenssy/article/details/12858267
- http://www.importnew.com/7302.html
例子對比
- 請大家仔細對比,下面看一個網上流傳最廣泛的例子:門和警報的例子:門都有open( )和close( )兩個動作,此時我們可以定義通過抽象類和接口來定義這個抽象概念:
- 抽象類
abstract class Door {
String door; //可以有默認變量
private String Name; //可以有私有變量
protected String name; //可以有保護變量
public String brand;//可以有公有變量
//可以有構造函數
public Component(String name){
this.name = name;
}
//可以有抽象方法
public abstract void open();
public abstract void close();
//可以有非抽象方法
public void doSomething(){
System.out.println("Open");
}
}
- 接口
interface Door {
String door; //只有默認變量,爲 public static final 修飾
public abstract void open(); //只有抽象方法
public abstract void close();
}
抽象類
- 抽象方法:一種特殊的方法:它只有聲明,而沒有具體的實現
- 抽象方法必須用abstract關鍵字進行修飾。
- 抽象類必須在類前用abstract關鍵字修飾。
- 如果一個類含有抽象方法,則稱這個類爲抽象類,
- 因爲抽象類中含有無具體實現的方法,所以不能用抽象類創建對象。
- 在其他方面,抽象類和普通的類並沒有區別。
接口
- 要讓一個類遵循某組特地的接口需要使用implements關鍵字,具體格式如下:
class ClassName implements Interface1,Interface2,[....]{
}一個類遵循多個特定的接口。
- 如果一個非抽象類遵循了某個接口,就必須實現該接口中的所有方法。
- 如果一個抽象類遵循某個接口,可以不實現該接口中的抽象方法。
區別與聯繫
語法區別
抽象類可以提供成員方法的實現細節,而接口中只能存在public abstract 方法;
抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是public static final類型的;
接口中不能含有靜態代碼塊以及靜態方法,而抽象類可以有靜態代碼塊和靜態方法;
一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
設計區別
- 抽象類是對一種事物的抽象,即對類抽象,而接口是對行爲的抽象。
- 回到上面門的例子上,將其簡化
- 門和警報的例子:門都有open( )和close( )兩個動作,此時我們可以定義通過抽象類和接口來定義這個抽象概念:
設計成抽像類:
abstract class Door {
public abstract void open();
public abstract void close();
}或者設計成接口:
interface Door {
public abstract void open();
public abstract void close();
}但是現在如果我們需要門具有報警alarm( )的功能,那麼該如何實現?下面提供兩種思路:
1)將這三個功能都放在抽象類裏面,但是這樣一來所有繼承於這個抽象類的子類都具備了報警功能,但是有的門並不一定具備報警功能;
2)將這三個功能都放在接口裏面,需要用到報警功能的類就需要實現這個接口中的open( )和close( ),也許這個類根本就不具備open( )和close( )這兩個功能,比如火災報警器。
從這裏可以看出, Door的open() 、close()和alarm()根本就屬於兩個不同範疇內的行爲,open()和close()屬於門本身固有的行爲特性,而alarm()屬於延伸的附加行爲。因此最好的解決辦法是單獨將報警設計爲一個接口,包含alarm()行爲,Door設計爲單獨的一個抽象類,包含open和close兩種行爲。再設計一個報警門繼承Door類和實現Alarm接口。
interface Alram {
void alarm();
}
abstract class Door {
void open();
void close();
}
class AlarmDoor extends Door implements Alarm {
void oepn() {
//....
}
void close() {
//....
}
void alarm() {
//....
}
}
JDK8中的新特性-default方法(defender方法)(轉載)
原文鏈接
我們都知道在Java語言的接口中只能定義方法名,而不能包含方法的具體實現代碼。接口中定義的方法必須在接口的非抽象子類中實現。下面就是關於接口的一個例子:
public interface SimpleInterface {
public void doSomeWork();
}
class SimpleInterfaceImpl implements SimpleInterface{
@Override
public void doSomeWork() {
System.out.println("Do Some Work implementation in the class");
}
public static void main(String[] args) {
SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
simpObj.doSomeWork();
}
}
那麼,如果我們在SimpleInterface裏面添加一個新方法,會怎樣呢?
public interface SimpleInterface {
public void doSomeWork();
public void doSomeOtherWork();
}
- 如果我們嘗試編譯上面的這段代碼,會得到如下結果:
$javac .\SimpleInterface.java
.\SimpleInterface.java:18: error: SimpleInterfaceImpl is not abstract and does not
override abstract method doSomeOtherWork() in SimpleInterface
class SimpleInterfaceImpl implements SimpleInterface{
^
1 error
因爲接口有這個語法限制,所以要直接改變/擴展接口內的方法變得非常困難。我們在嘗試強化Java 8 Collections API,讓其支持lambda表達式的時候,就面臨了這樣的挑戰。爲了克服這個困難,Java 8中引入了一個新的概念,叫做default方法,也可以稱爲Defender方法,或者虛擬擴展方法(Virtual extension methods)。Default方法是指,在接口內部包含了一些默認的方法實現(也就是接口中可以包含方法體,這打破了Java之前版本對接口的語法限制),從而使得接口在進行擴展的時候,不會破壞與接口相關的實現類代碼。接下來,讓我們看一個例子:
public interface SimpleInterface {
public void doSomeWork();
//A default method in the interface created using "default" keyword
//使用default關鍵字創在interface中直接創建一個default方法,該方法包含了具體的實現代碼
default public void doSomeOtherWork(){
System.out.println("DoSomeOtherWork implementation in the interface");
}
}
class SimpleInterfaceImpl implements SimpleInterface{
@Override
public void doSomeWork() {
System.out.println("Do Some Work implementation in the class");
}
/*
1. Not required to override to provide an implementation
2. for doSomeOtherWork.
3. 在SimpleInterfaceImpl裏,不需要再去實現接口中定義的doSomeOtherWork方法
*/
public static void main(String[] args) {
SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
simpObj.doSomeWork();
simpObj.doSomeOtherWork();
}
}
該程序的輸出是:
Do Some Work implementation in the class
DoSomeOtherWork implementation in the interface
如果一個類實現了兩個接口(可以看做是“多繼承”),這兩個接口又同時都包含了一個名字相同的default方法,那麼會發生什麼情況? 在這樣的情況下,編譯器會報錯。用例子來解釋一下:
public interface InterfaceWithDefaultMethod {
public void someMethod();
default public void someOtherMethod(){
System.out.println("Default method implementation in the interface");
}
}
public interface InterfaceWithAnotherDefMethod {
default public void someOtherMethod(){
System.out.println("Default method implementation in the interface");
}
}
然後我們定義一個類,同時實現以上兩個接口:
public class DefaultMethodSample implements
InterfaceWithDefaultMethod, InterfaceWithAnotherDefMethod{
@Override
public void someMethod(){
System.out.println("Some method implementation in the class");
}
public static void main(String[] args) {
DefaultMethodSample def1 = new DefaultMethodSample();
def1.someMethod();
def1.someOtherMethod();
}
}
如果編譯以上的代碼,會得到一個編譯器錯誤,因爲編譯器不知道應該在兩個同名的default方法中選擇哪一個,因此產生了二義性。