抽象類
定義
在面向對象的概念中,所有的對象都是通過類來描繪的,但是反過來,並不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。
解釋
比如一個food
類,我們知道他是一個食物,但是不知道它的形狀、大小、味道等等,所以它是抽象的,需要一個具體的餅干,面條來給它特定的描述。
特點
- 1) 不能被實例化
這個我考慮很久,查閱了一些資料,個人理解有幾點:
- 空間分配,內存垃圾問題。在實例化對象的時候(
new food()
)會開辟一個堆內存空間,而它沒有實現的方法,是個東西,但沒用。那就是垃圾。 - 安全。如果調用未實現的類就會報異常。比如
public abstract class Food{
public void print(){
System.out.println("un_abstract method");
}
public abstract void abstractmethod();
}
假設我們new
一個對象出來,Food food = new Food()
,那么執行food.abstractmethod
呢?
3. 面向對象思想不允許。可能也是最重要的吧。面向對象領域的一切都是對象,抽象在問題領域是沒有概念的。在映射到現實社會的模擬,抽象是沒有意義的。簡單的來說,你實例化一個抽象的東西能干嗎,沒有實際的意義,違背了面向對象思想。
關於這三點,屬於個人觀點,希望得到探討和批評,不保證正確性。
- 2) 抽象類除了不能實例化對象之外,類的其它功能依然存在,成員變量、成員方法和構造方法的訪問方式和普通類一樣。
public abstract class Person{
private String name; // 變量
private String address;
public Employee(String name, String address){ // 構造方法
this.name = name;
this.address = address;
}
public abstract void cry(); // 抽象方法
public void jump(){ // 普通方法
System.out.println(this.name + "跳起來了")
}
// getter/setter 略
}
- 3)類中有抽象方法就必須定義為抽象類,抽象類中不一定包含抽象方法;
關於抽象類中不一定包含抽象方法,這個也有點鑽牛角尖問題。如果一個抽象類不包含任何抽象方法,為何還要設計為抽象類?所以暫且記住這個概念吧,不必去深究為什么。
- 4)抽象類必須被繼承使用。子類必須重寫父類所有抽象方法或者聲明自己為抽象類(實現部分抽象方法);
public class Employee extends Person{
private int number;
public Employee(String name, String address, int number){
super(name,address);
this.number = number;
}
@Override
public void cry(){ //
System.out.println(this.number + "在叫")
}
// public abstract void *();
// 子類也可以繼續聲明自己的抽象方法,繼續被繼承實現
}
- 5)abstract不能與final並列修飾同一個類。
- 6)abstract 不能與private、static、final或native並列修飾同一個方法。
- 7)一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
為什么需要抽象類?
抽象方法和抽象類看上去是多余的,對於抽象方法,不知道如何實現,定義一個空方法體不就行了嗎,而抽象類不讓創建對象,看上去只是增加了一個不必要的限制。
引入抽象方法和抽象類,是Java提供的一種語法工具,對於一些類和方法,引導使用者正確使用它們,減少被誤用。
使用抽象方法,而非空方法體,子類就知道他必須要實現該方法,而不可能忽略。
使用抽象類,類的使用者創建對象的時候,就知道他必須要使用某個具體子類,而不可能誤用不完整的父類。
無論是寫程序,還是平時做任何別的事情的時候,每個人都可能會犯錯,減少錯誤不能只依賴人的優秀素質,還需要一些機制,使得一個普通人都容易把事情做對,而難以把事情做錯。抽象類就是Java提供的這樣一種機制。
接口
在軟件工程中,接口泛指供別人調用的方法或者函數。從這里,我們可以體會到 Java 語言設計者的初衷,它是對行為的抽象。在 Java 中,定一個接口的形式如下:
[public] interface InterfaceName {
}
特征
- 1)接口中可以含有變量和方法
但是要注意,接口中變量只能是 public static final變量,用其他報錯。
方法且只能是 public abstract 方法,用其他關鍵字,比如 private、protected、static、 final 等修飾會報編譯錯誤
- 2)接口中的方法必須都是抽象方法
- 3)允許一個類遵循多個特定的接口
class ClassName implements Interface1,Interface2,[....]{
}
如果一個非抽象類遵循了某個接口,就必須實現該接口中的所有方法。對於遵循某個接口的抽象類,可以不實現該接口中的抽象方法。
抽象類和接口的區別
1. 語法層面上
-
抽象類可以提供成員方法的實現細節,而接口中只能存在public abstract 方法;
-
抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是public static final類型的;
-
接口中不能含有靜態代碼塊以及靜態方法,而抽象類可以有靜態代碼塊和靜態方法;
-
一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
說明抽象類只是類的另一種形式。而接口是一個公共的方法集合,不能有別的,一旦用了一個方法還得全部都得用,很純粹,很牛XX
2. 設計層面上
要真正理解和區分,就要在設計層面上下功夫了,這樣才能知道在什么場景下用它。
- 抽象類是對一種事物的抽象,即對類抽象,而接口是對行為的抽象。
舉個栗子,飛機和鳥是不同的事物,但有一個共同的特征就是會飛。因此我們可以設計的時候,設計Airplan
和Bird
兩個類,但飛是一個動作,一種行為,不是一類事物的抽象描述。所以就可以設計一個接口Fly
,包含方法fly()
,然后Airplane和Bird分別根據自己的需要實現Fly這個接口。
然后至於有不同種類的飛機,比如戰斗機、民用飛機等直接繼承Airplane即可,對於鳥也是類似的,不同種類的鳥直接繼承Bird類即可。
從這里可以看出,類繼承屬於‘是不是’的問題,你屬不屬於飛機類,鳥類。接口實現是‘有沒有’的問題,你有沒有飛這個功能。
- 抽象是對類的抽象,是一種模板設計,而接口是對行為的抽象,是一種行為的規范。
我認為這就話就是精髓所在。直接再舉起來一個栗子:門和報警
門都有open()
和close()
兩個動作,但現在要給門加上alarm()
,這就要分析一下了。
Door
的 open()
、close()
和 alarm()
屬於兩個不同范疇內的行為,open() 和 close() 屬於門本身固有的行為特性,而 alarm() 屬於延伸的附加行為。門也分很多種,玻璃門、防盜門、推拉門等等。不是所有的都具有報警功能。以后添加報警裝置,也只是增加了一個報警功能。
因此最好的解決辦法是單獨將報警設計為一個接口,包含 alarm()
行為,Door
設計為單獨的一個抽象類,包含 open
和 close
兩種行為。再設計一個報警門繼承 Door
類和實現 Alarm
接口。
即:
public abstract class Door{
public abstract void open();
public abstract void close();
}
interface Alarm{
void alarm();
}
class AlarmDoor extends Door implements Alarm{
@Override
void oepn() {
//....
}
@Override
void close() {
//....
}
@Override
void alarm() {
//....
}
}
參考資料:
https://www.runoob.com/w3cnote/java-abstract-interface-different.html