抽象類(lèi)與普通類(lèi)相比最大的特點(diǎn)是約定了子類(lèi)的實(shí)現(xiàn)要求鹤竭,但是抽象類(lèi)有一個(gè)缺點(diǎn):?jiǎn)卫^承局限,如果要想約定子類(lèi)的實(shí)現(xiàn)要求以及避免單繼承的局限就需要使用接口回窘。在你以后的開(kāi)發(fā)設(shè)計(jì)之中:接口優(yōu)先诺擅。在一個(gè)操作既可以使用抽象類(lèi)又可以使用接口的時(shí)候市袖,請(qǐng)優(yōu)先考慮接口啡直。
一. 接口的基本概念
接口就是一個(gè)抽象方法和全局常量的集合,在Java中接口可以使用interface關(guān)鍵字來(lái)進(jìn)行定義苍碟。如果子類(lèi)要想使用接口酒觅,那么就必須利用implements關(guān)鍵字來(lái)實(shí)現(xiàn)接口,同時(shí)一個(gè)子類(lèi)可以同時(shí)實(shí)現(xiàn)多個(gè)接口微峰,也就是說(shuō)可以利用接口來(lái)實(shí)現(xiàn)多繼承的概念舷丹。對(duì)于接口的子類(lèi)(如果不是抽象類(lèi))則必須覆寫(xiě)接口中的全部抽象方法。隨后可以利用子類(lèi)對(duì)象的向上轉(zhuǎn)型通過(guò)實(shí)例化子類(lèi)來(lái)得到接口的實(shí)例化對(duì)象蜓肆。
當(dāng)一個(gè)子類(lèi)繼承了多個(gè)接口之后颜凯,并且接口對(duì)象通過(guò)子類(lèi)進(jìn)行實(shí)例化,那么這多個(gè)父接口之間是允許互相轉(zhuǎn)換的仗扬。
二. 接口使用限制
1. 接口定義完成之后症概,就需要對(duì)其有一個(gè)核心的說(shuō)明,那么首先需要說(shuō)明的是:接口里面只允許存在有public權(quán)限早芭,也就是說(shuō)不管是屬性還是方法其權(quán)限只能是public彼城。
在以后編寫(xiě)接口的時(shí)候,99%的接口里面只會(huì)提供有抽象方法退个,很少在接口里面看見(jiàn)有許多的全局常量募壕。所以很多時(shí)候?yàn)榱朔乐贡苊庖恍╅_(kāi)發(fā)者出現(xiàn)混亂,所以接口的方法上往往都會(huì)加上pubLic语盈。
2. 當(dāng)一個(gè)子類(lèi)需要實(shí)現(xiàn)接口又需要繼承抽象類(lèi)的時(shí)候舱馅,請(qǐng)先使用extends繼承一個(gè)抽象類(lèi),而后再使用implements實(shí)現(xiàn)多個(gè)接口刀荒。
3. 一個(gè)抽象類(lèi)可以使用implements實(shí)現(xiàn)多個(gè)接口习柠,但是接口不能夠去繼承抽象類(lèi)匀谣。
4. 一個(gè)接口可以使用extends來(lái)繼承多個(gè)父接口。
5. 接口可以定義一系列的內(nèi)部結(jié)構(gòu)资溃,包括:內(nèi)部的普通類(lèi)武翎、內(nèi)部抽象類(lèi)、內(nèi)部接口溶锭,其中使用static定義的內(nèi)部接口就相當(dāng)于一個(gè)外部接口宝恶。
對(duì)于內(nèi)部的結(jié)構(gòu)依然不是你們的首選,而且要想清楚接口的實(shí)際開(kāi)發(fā)意義趴捅,需要大量的項(xiàng)目來(lái)驗(yàn)證垫毙。
三. 使用接口定義標(biāo)準(zhǔn)
對(duì)于接口在實(shí)際的開(kāi)發(fā)之中有三大核心應(yīng)用環(huán)境:
①定義操作標(biāo)準(zhǔn);
②表示能力拱绑;
③在分布式開(kāi)發(fā)之中暴露遠(yuǎn)程服務(wù)方法综芥。
class TestDemo {
public static void main(String args[]) {
Computer com = new Computer() ;
com.plugin(new Flash()) ;
com.plugin(new Print()) ;
}
}
interface USB {
public void setup() ;? // 安裝USB驅(qū)動(dòng)
public void work() ;? // 進(jìn)行工作
}
class Computer {
public void plugin(USB usb) {//只能夠插入U(xiǎn)SB設(shè)備
usb.setup() ;
usb.work() ;
}
}
class Flash implements USB {
public void setup() {
System.out.println("安裝U盤(pán)驅(qū)動(dòng)!") ;
}
public void work() {
System.out.println("進(jìn)行數(shù)據(jù)傳輸猎拨!") ;
}
}
class Print implements USB {
public void setup() {
System.out.println("安裝打印機(jī)驅(qū)動(dòng)膀藐!") ;
}
public void work() {
System.out.println("進(jìn)行文件打印红省!") ;
}
}
發(fā)現(xiàn)使用接口和對(duì)象多態(tài)性的概念結(jié)合之后额各,對(duì)于參數(shù)的統(tǒng)一更加明確了。而且可以發(fā)現(xiàn)接口是在類(lèi)之上的設(shè)計(jì)抽象吧恃。
四. 工廠(chǎng)設(shè)計(jì)模式(Factory) ?【重點(diǎn)】
首先來(lái)看一個(gè)簡(jiǎn)單的程序范例: 在進(jìn)行類(lèi)設(shè)計(jì)的時(shí)候虾啦,要求首先需要有接口,而后接口要通過(guò)子類(lèi)才可以進(jìn)行對(duì)象的實(shí)例化處理痕寓。
此時(shí)的程序?qū)崿F(xiàn)的關(guān)鍵是:“IFruit fruit = new Apple() ;”傲醉,如果沒(méi)有此語(yǔ)句,接口對(duì)象將無(wú)法進(jìn)行實(shí)例化的操作處理呻率。但是最大的敗筆也在此硬毕。主方法是一個(gè)客戶(hù)端,那么對(duì)于程序的修改不應(yīng)該影響到客戶(hù)端筷凤。
這個(gè)時(shí)候new是整個(gè)開(kāi)發(fā)過(guò)程之中最大的耦合元兇昭殉,而我們?cè)陂_(kāi)發(fā)之中要想進(jìn)行解耦合的關(guān)鍵就在于要引入一個(gè)第三方,所以這個(gè)類(lèi)可以使用Factory來(lái)描述藐守。
范例:
class TestDemo {
public static void main(String args[]) {
if(args.length != 1) {? // 沒(méi)有傳遞一個(gè)參數(shù)
System.out.println("對(duì)不起挪丢,程序執(zhí)行錯(cuò)誤,正確的格式:java TestDemo 類(lèi)名稱(chēng)") ;
System.exit(1) ;? // 退出程序執(zhí)行
}
System.out.println("hello world !\n") ;
Computer com = new Computer() ;
com.plugin(new Flash()) ;
com.plugin(new Print()) ;
//IFruit fruit = new Apple() ;
// 利用第三方來(lái)進(jìn)行解耦合
IFruit fruit = Factory.getInstance(args[0]) ;
fruit.eat() ;
}
}
class Factory {
// 因?yàn)榇藭r(shí)Factory產(chǎn)生實(shí)例化對(duì)象沒(méi)有意義
public static IFruit getInstance(String className) {
if("apple".equals(className)) {
return new Apple() ;
}
if("orange".equals(className)) {
return new Orange() ;
}
return null ;
}
}
interface USB {
public void setup() ;? // 安裝USB驅(qū)動(dòng)
public void work() ;? // 進(jìn)行工作
}
class Computer {
public void plugin(USB usb) {? //只能夠插入U(xiǎn)SB設(shè)備
usb.setup() ;
usb.work() ;
}
}
class Flash implements USB {
public void setup() {
System.out.println("安裝U盤(pán)驅(qū)動(dòng)卢厂!") ;
}
public void work() {
System.out.println("進(jìn)行數(shù)據(jù)傳輸乾蓬!") ;
}
}
class Print implements USB {
public void setup() {
System.out.println("安裝打印機(jī)驅(qū)動(dòng)!") ;
}
public void work() {
System.out.println("進(jìn)行文件打由骱恪任内!") ;
}
}
interface IFruit {
public void eat() ;
}
class Apple implements IFruit {
public void eat() {
System.out.println("\n削皮吃蘋(píng)果撵渡!") ;
}
}
class Orange implements IFruit {
public void eat() {
System.out.println("\n剝皮吃橘子!") ;
}
}
當(dāng)更換使用的IFruit子類(lèi)的時(shí)候主方法沒(méi)有任何的變化就可以實(shí)現(xiàn)了子類(lèi)的變更死嗦,這樣的設(shè)計(jì)就成為工廠(chǎng)設(shè)計(jì)模式趋距。
總結(jié):以后只要是你編寫(xiě)的接口如果要想取得接口的實(shí)例化對(duì)象,第一反應(yīng):寫(xiě)工廠(chǎng)類(lèi)越除。
五. 代理設(shè)計(jì)模式(Proxy)【重點(diǎn)】
所謂的代理嚴(yán)格來(lái)講就是兩個(gè)子類(lèi)共同實(shí)現(xiàn)一個(gè)接口节腐,其中一個(gè)子類(lèi)負(fù)責(zé)真實(shí)的業(yè)務(wù)實(shí)現(xiàn),而另外的子類(lèi)負(fù)責(zé)輔助真實(shí)業(yè)務(wù)主題的操作摘盆。
class TestDemo {
public static void main(String args[]) {
if(args == null) {
System.out.println("命令行參數(shù)為null") ;
}
if(0 == args.length) {
System.out.println("命令行參數(shù)個(gè)數(shù)為0") ;
}
if(args.length != 1) {? // 沒(méi)有傳遞一個(gè)參數(shù)
System.out.println("對(duì)不起翼雀,程序執(zhí)行錯(cuò)誤,正確的格式:java TestDemo 類(lèi)名稱(chēng)") ;
System.exit(1) ;? // 退出程序執(zhí)行
}
System.out.println("hello world !\n") ;
Computer com = new Computer() ;
com.plugin(new Flash()) ;
com.plugin(new Print()) ;
//IFruit fruit = new Apple() ;
// 利用第三方來(lái)進(jìn)行解耦合
IFruit fruit = Factory.getInstance(args[0]) ;
fruit.eat() ;
//ISubject sub = new ProxySubject(new RealSubject()) ;
ISubject sub = Factory.getInstance() ;
// 通過(guò)代理類(lèi)對(duì)象發(fā)出孩擂,利用代理類(lèi)來(lái)實(shí)現(xiàn)真實(shí)業(yè)務(wù)調(diào)用
sub.save() ;
}
}
class Factory {
// 因?yàn)榇藭r(shí)Factory產(chǎn)生實(shí)例化對(duì)象沒(méi)有意義
public static IFruit getInstance(String className) {? //通過(guò)普通類(lèi)去操作
if("apple".equals(className)) {
return new Apple() ;
}
if("orange".equals(className)) {? ? ? ? ? ? ? ? ? //通過(guò)代理類(lèi)去操作
return new Orange() ;
}
return null ;
}
public static ISubject getInstance() {
return new ProxySubject(new RealSubject()) ;
}
}
interface USB {
public void setup() ;? // 安裝USB驅(qū)動(dòng)
public void work() ;? // 進(jìn)行工作
}
class Computer {
public void plugin(USB usb) {? //只能夠插入U(xiǎn)SB設(shè)備
usb.setup() ;
usb.work() ;
}
}
class Flash implements USB {
public void setup() {
System.out.println("安裝U盤(pán)驅(qū)動(dòng)狼渊!") ;
}
public void work() {
System.out.println("進(jìn)行數(shù)據(jù)傳輸!") ;
}
}
class Print implements USB {
public void setup() {
System.out.println("安裝打印機(jī)驅(qū)動(dòng)类垦!") ;
}
public void work() {
System.out.println("進(jìn)行文件打颖芬亍!") ;
}
}
interface IFruit {
public void eat() ;
}
class Apple implements IFruit {
public void eat() {
System.out.println("\n削皮吃蘋(píng)果护锤!") ;
}
}
class Orange implements IFruit {
public void eat() {
System.out.println("\n剝皮吃橘子官地!") ;
}
}
interface ISubject {
public void save() ;? // 核心功能是救人
}
class RealSubject implements ISubject {
public void save() {
System.out.println("真正的制止了暴力事件酿傍!") ;
}
}
class ProxySubject implements ISubject {? // 代理實(shí)現(xiàn)
private ISubject subject ;? // 真正的操作業(yè)務(wù)
// 在創(chuàng)建代理類(lèi)對(duì)象的時(shí)候必須設(shè)置要代理的真實(shí)主題
public ProxySubject(ISubject subject) {
this.subject = subject ;
}
public void broke() {
System.out.println("1烙懦、破門(mén)而入!") ;
}
public void get() {
System.out.println("2赤炒、得到見(jiàn)義勇為獎(jiǎng)氯析!") ;
}
public void save() {
this.broke() ;? ? ? ? ? // 真實(shí)操作前的準(zhǔn)備
this.subject.save() ;? ? // 調(diào)用真實(shí)的業(yè)務(wù)
this.get() ;? ? ? ? ? ? // 操作后的收尾
}
}
代理的本質(zhì):所有的真實(shí)業(yè)務(wù)操作都會(huì)有一個(gè)與之輔助的功能類(lèi)共同完成。
六. 抽象類(lèi)與接口的區(qū)別(面試題)
抽象類(lèi)和接口都屬于常用的類(lèi)結(jié)構(gòu)設(shè)計(jì)莺褒,在開(kāi)發(fā)之中都會(huì)出現(xiàn)掩缓,不過(guò)如果按照優(yōu)先選擇來(lái)講,接口一定要比抽象類(lèi)更方便遵岩。
除了單繼承的局限之外你辣,實(shí)際上使用抽象類(lèi)和接口都是類(lèi)似的,但是在實(shí)際的開(kāi)發(fā)之中尘执,抽象類(lèi)的設(shè)計(jì)要比接口復(fù)雜舍哄。
1. 接口是Java的核心,慢慢需要學(xué)習(xí)到接口的更多使用以及設(shè)計(jì)誊锭;
2. 開(kāi)發(fā)之中優(yōu)先考慮接口表悬,以避免單繼承局限;