在實際的項目中,整個項目的代碼一般可以分為結構代碼和邏輯的代碼。就像建造房屋時,需要首先搭建整個房屋的結構,然後再細化房屋相關的其它的結構,也像制造汽車時,需要首先制作汽車的框架,然後才是安裝配件以及美化等工作。程序項目的實現也遵循同樣的道理。
在項目設計時,一個基本的原則就是——“設計和實現相分離”。也就是說結構代碼和邏輯代碼的分離,就像設計汽車時只需要關注汽車的相關參數,而不必過於關心如何實現這些要求的制作。程序設計時也是首先設計項目的結構,而不用過多的關系每個邏輯的代碼如何進行實現。
前面介紹的流程控制知識,主要解決的是邏輯的代碼的編寫,而類和對象的知識,則主要解決結構代碼的編寫。那麼還有一個主要的問題:如何設計結構代碼呢?這就需要使用下面介紹的抽象類和接口的知識了。
8.9.1 抽象類
抽象類(Abstract Class)是指使用abstract關鍵字修飾的類,也就是在聲明一個類時加入了abstract關鍵字。抽象類是一種特殊的類,其它未使用abstract關鍵字修飾的類一般稱作實體類。例如:
public abstract class A{
public A(){}
}
抽象方法(Abstract Method)是指使用abstract關鍵字修飾的方法。抽象方法是一種特殊的方法,其它未使用abstract關鍵字修飾的方法一般稱作實體方法。
public abstract void test();
抽象類和實體類相比,主要有以下兩點不同:
l 抽象類不能使用自身的構造方法創建對象(語法不允許)
例如下面的語法是錯誤的:
A a = new A();
但是抽象類可以聲明對象,例如下面的代碼是正確的:
A a;
A a1,a2;
只是聲明出的對象默認都是null的,無法調用其內部的非靜態屬性和非靜態方法。
說明:抽象類可以使用子類的構造方法創建對象。
l 抽象類內部可以包含任意個(0個、1個或多個)抽象方法
抽象類內部可以包含抽象方法,也可以不包含抽象方法,對於包含的個數沒有限制。而實體類內部不能包含抽象方法。
在抽象類內部,可以和實體類一樣,包含構造方法、屬性和實體方法,這點和一般的類一樣。
抽象方法和實體方法相比,主要有以下幾點不同:
l 抽象方法沒有方法體
也就是說在聲明抽象方法時,不能書寫方法體的{},而只能以分號結束方法。下面是實體方法和抽象方法聲明的比較:
抽象方法聲明:
public abstract void test(int a);
實體方法聲明:
public void test(int a){
方法體
}
l 抽象方法所在的類必須為抽象類
也就是說,如果抽象方法聲明在一個類內部,則該類必須為抽象類。(說明:抽象方法也可以出現在接口內部,這個將在後續進行介紹)。
這樣,在繼承時,如果繼承的類是抽象類,而該抽象類中還包含抽象方法時,則該子類必須聲明成抽象類,否則將出現語法錯誤。如果子類需要做成實體類的話,則必須覆蓋繼承的所有抽象方法。這個是抽象類最核心的語法功能——強制子類覆蓋某些方法。
介紹了這麼多抽象類和抽象方法的知識以後,那麼抽象類有什麼用途呢?
抽象類的用途主要有兩個:
l 嚴禁直接創建該類的對象
如果一個類內部包含的所有方法都是static方法,那麼為了避免其它程序員誤用,則可以將該類聲明為abstract,這樣其它程序員只能使用類名.方法名調用對應方法,而不能使用對象名.方法名進行調用。這樣的類例如API中的Math類
說明:配合final關鍵字使用,將必須該類被繼承,這樣將獲得更加完美的效果。
l 強制子類覆蓋抽象方法
這樣可以使所有的子類在方法聲明上保持一致,在邏輯上也必須將方法的功能保持一致。例如游戲中設計類時,設計了怪物類以及相關的子類,每個怪物類都有移動方法,但是每種怪物的移動規則又不相同,這樣通過使每個怪物類的移動方法的聲明保持一致,方便調用。可以參看前面多態部分的介紹獲得更多的關於調用統一知識。
這是抽象類最主要的用途。就像現實社會中,各種銀行網點保持統一的裝修風格,各種快餐店(肯德基、麥當勞等)保持統一的裝修甚至風味,這樣便於生活中的識別。通過讓存在繼承關系的類中功能一樣(但是內部實現規則不同)的方法聲明成一樣的,方便多態的使用。
那麼什麼時候在設計時使用抽象類呢?這個問題參看一下抽象類的用途自然就知道了。關於抽象類的知識先介紹這麼多,下面介紹接口的知識,最終將對抽象類和接口進行一下比較。