原文http://kikoroc.com/2016/05/10/java-abstract-classes-and-template-method-design-pattern.html發(fā)表在我的個人博客。
java中的抽象類是不能被實例化的配紫,意味著你不能創(chuàng)建一個抽象類的實例午阵。抽象類的目的是為了作為子類的基類,本文將介紹如何在java中創(chuàng)建抽象類以及使用抽象類的一些規(guī)則底桂。
定義抽象類
java中定義抽象類很簡單,在類的定義中加入abstract
關(guān)鍵字即可奋单。下面是java中定義抽象類的實例:
public abstract class MyAbstractClass {
}
上面就是java中定義抽象類的所有內(nèi)容猫十,現(xiàn)在你不能創(chuàng)建MyAbstractClass
的實例,所以下面的代碼不再合法:
MyAbstractClass myClassInstance =
new MyAbstractClass(); // not valid
如果你嘗試編譯上面的代碼拖云,編譯器將報錯,告訴你不能實例化MyAbstractClass
宙项,因為它是一個抽象類。
抽象方法
抽象類中可以定義抽象方法汇荐,定義抽象方法也很簡單,在方法的定義前面添加abstract
關(guān)鍵字即可掀淘,下面是定義抽象方法的實例:
public abstract class MyAbstractClass {
public abstract void abstractMethod();
}
抽象方法是沒有具體實現(xiàn)細(xì)節(jié)的,僅僅是方法的定義倾贰,跟java interface
中的方法類似。
如果一個類中包含抽象方法匆浙,那么這個類必須定義為abstract
抽象類厕妖。但并不要求抽象類中所有方法都是抽象方法。抽象類中既可包含抽象方法也可以包含非抽象方法言秸。
抽象類的子類必須實現(xiàn)(override)父抽象類中所有的抽象方法,非抽象方法直接繼承到子類,如有需要也可以重寫非抽象方法破加。
下面是MyAbstractClass
抽象類子類的實例:
public class MySubClass extends MyAbstractClass {
public void abstractMethod() {
System.out.println("My method implementation");
}
}
注意MySubClass
必須實現(xiàn)抽象父類MyAbstractClass
中的抽象方法abstractMethod
。
抽象類的子類不用實現(xiàn)父類中的抽象方法唯一的情況是:子類也是抽象類合是。
抽象類的作用
抽象類的作用是為了作為子類的基類锭环,這樣子類很容易繼承父類來做具體的細(xì)節(jié)實現(xiàn)。比如辅辩,假如一個流程需要3個步驟:
- 具體動作之前的操作
- 執(zhí)行具體的動作
- 具體動作結(jié)束后的操作
如果第一步和第三步的實現(xiàn)總是相同的,那么這個三步走的流程可以用java抽象類這么實現(xiàn):
public abstract class MyAbstractProcess {
public void process() {
stepBefore();
action();
stepAfter();
}
public void stepBefore() {
//直接在抽象父類中實現(xiàn)
}
public abstract void action(); // 由子類來實現(xiàn)
public void stepAfter() {
//直接在抽象父類中實現(xiàn)
}
}
注意到action()
方法是抽象方法蛾茉,MyAbstractProcess
的子類現(xiàn)在可以繼承MyAbstractProcess
然后重寫action()
方法撩鹿。
當(dāng)子類的process()
方法被調(diào)用,那么整個三步走的流程都會調(diào)用,包括父類中的stepBefore()
和stepAfter()
方法以及子類中實現(xiàn)的action()
方法础爬。
當(dāng)然吼鳞,MyAbstractProcess
作為基類不一定非要是抽象類看蚜,action()
也不是必須要是抽象方法的赖条,你可以直接使用普通類來實現(xiàn)同樣的邏輯,但是碱茁,讓具體的實現(xiàn)方法和類抽象化仿贬,你可以清晰的告訴使用者這個類不能直接使用,它應(yīng)該作為基類茧泪,然后讓子類來實現(xiàn)抽象方法。
上面實例中action()
方法沒有默認(rèn)的實現(xiàn)队伟,是某些情況下父類也可以有默認(rèn)的實現(xiàn),子類也可以重寫該方法港令,當(dāng)然這種情況下這個方法就不能再定義為抽象方法锈颗,但是你依舊可以將類定義為抽象類,即使類里面沒有抽象方法击吱。
下面是一個更具體的實例用來打開一個URL,處理數(shù)據(jù)然后關(guān)閉鏈接朵纷。
public abstract class URLProcessorBase {
public void process(URL url) throws IOException {
URLConnection urlConnection = url.openConnection();
InputStream input = urlConnection.getInputStream();
try{
processURLData(input);
} finally {
input.close();
}
}
protected abstract void processURLData(InputStream input)
throws IOException;
}
processURLData()
是個抽象方法永脓,URLProcessorBase
是個抽象類,那么URLProcessorBase
的子類必須要實現(xiàn)父類的抽象方法processURLData()
憨奸,因為它是一個抽象方法。
URLProcessorBase
的子類可以直接處理url下載的數(shù)據(jù)似芝,而不用擔(dān)心如何打開和關(guān)閉url鏈接。因為這些都由父類實現(xiàn)了党瓮,子類只需要關(guān)注processURLData()
方法中的InputStream
對象,這樣可以使得可以很方便的實現(xiàn)url數(shù)據(jù)處理的子類呛谜。
下面是一個子類的實例:
public class URLProcessorImpl extends URLProcessorBase {
@Override
protected void processURLData(InputStream input) throws IOException {
int data = input.read();
while(data != -1){
System.out.println((char) data);
data = input.read();
}
}
}
注意到子類僅僅只需要重寫processURLData()
方法枪萄,其他的方法直接從父類繼承過來。
下面是如何使用URLProcessorImpl
類的實例:
URLProcessorImpl urlProcessor = new URLProcessorImpl();
urlProcessor.process(new URL("http://jenkov.com"));
父類URLProcessorBase
中實現(xiàn)的process()
方法直接調(diào)用聚凹,然后會調(diào)用URLProcessorImpl
類中的processURLData()
方法
齐帚。
抽象類和模板方法設(shè)計模式
上面的URLProcessorBase
類的實例代碼實際上就是一種模板方法設(shè)計模式,模板方法設(shè)計模式提供了流程中一部分的實現(xiàn)邏輯湘今,然后子類可以繼承基類來完成全部的實現(xiàn)。
譯自:http://tutorials.jenkov.com/java/abstract-classes.html摩瞎,能力有限琅豆,詳細(xì)細(xì)節(jié)可以查閱原文篓吁。
模板方法設(shè)計模式簡述
上面提到抽象類和模板方法設(shè)計模式的關(guān)系,這里也簡單總結(jié)下模板設(shè)計方法冻押。
概述
定義一個操作中的算法框架盛嘿,而將具體的實現(xiàn)細(xì)節(jié)推遲到子類中實現(xiàn)。這樣可以使子類不改變算法結(jié)構(gòu)來重新定義算法的某些特定邏輯次兆。
角色
抽象類:實現(xiàn)模板方法,定義算法框架漓库。
具體類:實現(xiàn)抽象類中的抽象方法,完成完整的算法邏輯渺蒿。
優(yōu)缺點及適用場景
- 優(yōu)點
- 模板方法通過將不變的行為在基類中實現(xiàn),去除了子類中的重復(fù)代碼怠蹂;
- 在子類中負(fù)責(zé)具體的細(xì)節(jié)實現(xiàn)少态,有利于算法的擴(kuò)展;
- 通過父類調(diào)用子類實現(xiàn)的操作况增,通過子類擴(kuò)展增加新的行為,符合“開閉原則”歧强。
- 缺點
每個不同的實現(xiàn)都需要定義一個子類为肮,這會導(dǎo)致類個數(shù)的增加,設(shè)計更加抽象颊艳。 - 適用場景
- 在某些類的算法中,用了相同的方法白修,造成代碼的重復(fù)重斑;
- 控制子類的擴(kuò)展,子類必須遵守算法的框架規(guī)則窥浪。
代碼示例
抽象類:
public abstract class AbstractClass {
// 定義算法的一些抽象行為,讓子類負(fù)責(zé)實現(xiàn)
public abstract void step1();
public abstract void step2();
// 模板方法假颇,定義算法的框架骨稿,調(diào)用抽象方法姜钳,他們將在子類中實現(xiàn)形耗。
public void TemplateMethod() {
step1();
step2();
System.out.println("Completed.");
}
}
具體類A:
public class ConcreteClassA extends AbstractClass {
@Override
public void step1() {
System.out.println("step1 of ConcreteClassA");
}
@Override
public void step2() {
System.out.println("step2 of ConcreteClassA");
}
}
具體類B:
public class ConcreteClassB extends AbstractClass {
@Override
public void step1() {
System.out.println("step1 of ConcreteClassB");
}
@Override
public void step2() {
System.out.println("step2 of ConcreteClassB");
}
}
調(diào)用:
AbstractClass abstractClass = new ConcreteClassA();
// 調(diào)用A的實現(xiàn)
abstractClass.TemplateMethod();
abstractClass = new ConcreteClassB();
// 調(diào)用B的實現(xiàn)
abstractClass.TemplateMethod();
輸出:
step1 of ConcreteClassA
step2 of ConcreteClassA
Completed.
step1 of ConcreteClassB
step2 of ConcreteClassB
Completed.
與抽象工廠模式的區(qū)別
模板方法是一種算法模板趟脂,針對算法的擴(kuò)展重寫泰讽;而抽象工廠模式是為了創(chuàng)建對象昔期,針對對象的擴(kuò)展重寫硼一。