抽象類與普通類相比最大的特點(diǎn)是約定了子類的實(shí)現(xiàn)要求,但是抽象類有一個缺點(diǎn):單繼承局限,如果要想約定子類的實(shí)現(xiàn)要求以及避免單繼承的局限就需要使用接口。在你以后的開發(fā)設(shè)計(jì)之中:接口優(yōu)先。在一個操作既可以使用抽象類又可以使用接口的時(shí)候,請優(yōu)先考慮接口。
一. 接口的基本概念
接口就是一個抽象方法和全局常量的集合,在Java中接口可以使用interface關(guān)鍵字來進(jìn)行定義。如果子類要想使用接口,那么就必須利用implements關(guān)鍵字來實(shí)現(xiàn)接口,同時(shí)一個子類可以同時(shí)實(shí)現(xiàn)多個接口,也就是說可以利用接口來實(shí)現(xiàn)多繼承的概念。對于接口的子類(如果不是抽象類)則必須覆寫接口中的全部抽象方法。隨后可以利用子類對象的向上轉(zhuǎn)型通過實(shí)例化子類來得到接口的實(shí)例化對象。
當(dāng)一個子類繼承了多個接口之后,并且接口對象通過子類進(jìn)行實(shí)例化,那么這多個父接口之間是允許互相轉(zhuǎn)換的。
二. 接口使用限制
1. 接口定義完成之后,就需要對其有一個核心的說明,那么首先需要說明的是:接口里面只允許存在有public權(quán)限,也就是說不管是屬性還是方法其權(quán)限只能是public。
在以后編寫接口的時(shí)候,99%的接口里面只會提供有抽象方法,很少在接口里面看見有許多的全局常量。所以很多時(shí)候?yàn)榱朔乐贡苊庖恍╅_發(fā)者出現(xiàn)混亂,所以接口的方法上往往都會加上pubLic。
2. 當(dāng)一個子類需要實(shí)現(xiàn)接口又需要繼承抽象類的時(shí)候,請先使用extends繼承一個抽象類,而后再使用implements實(shí)現(xiàn)多個接口。
3. 一個抽象類可以使用implements實(shí)現(xiàn)多個接口,但是接口不能夠去繼承抽象類。
4. 一個接口可以使用extends來繼承多個父接口。
5. 接口可以定義一系列的內(nèi)部結(jié)構(gòu),包括:內(nèi)部的普通類、內(nèi)部抽象類、內(nèi)部接口,其中使用static定義的內(nèi)部接口就相當(dāng)于一個外部接口。
對于內(nèi)部的結(jié)構(gòu)依然不是你們的首選,而且要想清楚接口的實(shí)際開發(fā)意義,需要大量的項(xiàng)目來驗(yàn)證。
三. 使用接口定義標(biāo)準(zhǔn)
對于接口在實(shí)際的開發(fā)之中有三大核心應(yīng)用環(huán)境:
①定義操作標(biāo)準(zhǔn);
②表示能力;
③在分布式開發(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ū)動
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盤驅(qū)動!") ;
}
public void work() {
System.out.println("進(jìn)行數(shù)據(jù)傳輸!") ;
}
}
class Print implements USB {
public void setup() {
System.out.println("安裝打印機(jī)驅(qū)動!") ;
}
public void work() {
System.out.println("進(jìn)行文件打印!") ;
}
}
發(fā)現(xiàn)使用接口和對象多態(tài)性的概念結(jié)合之后,對于參數(shù)的統(tǒng)一更加明確了。而且可以發(fā)現(xiàn)接口是在類之上的設(shè)計(jì)抽象。
四. 工廠設(shè)計(jì)模式(Factory) ?【重點(diǎn)】
首先來看一個簡單的程序范例: 在進(jìn)行類設(shè)計(jì)的時(shí)候,要求首先需要有接口,而后接口要通過子類才可以進(jìn)行對象的實(shí)例化處理。
此時(shí)的程序?qū)崿F(xiàn)的關(guān)鍵是:“IFruit fruit = new Apple() ;”,如果沒有此語句,接口對象將無法進(jìn)行實(shí)例化的操作處理。但是最大的敗筆也在此。主方法是一個客戶端,那么對于程序的修改不應(yīng)該影響到客戶端。
這個時(shí)候new是整個開發(fā)過程之中最大的耦合元兇,而我們在開發(fā)之中要想進(jìn)行解耦合的關(guān)鍵就在于要引入一個第三方,所以這個類可以使用Factory來描述。
范例:
class TestDemo {
public static void main(String args[]) {
if(args.length != 1) {? // 沒有傳遞一個參數(shù)
System.out.println("對不起,程序執(zhí)行錯誤,正確的格式:java TestDemo 類名稱") ;
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() ;
// 利用第三方來進(jìn)行解耦合
IFruit fruit = Factory.getInstance(args[0]) ;
fruit.eat() ;
}
}
class Factory {
// 因?yàn)榇藭r(shí)Factory產(chǎn)生實(shí)例化對象沒有意義
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ū)動
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盤驅(qū)動!") ;
}
public void work() {
System.out.println("進(jìn)行數(shù)據(jù)傳輸!") ;
}
}
class Print implements USB {
public void setup() {
System.out.println("安裝打印機(jī)驅(qū)動!") ;
}
public void work() {
System.out.println("進(jìn)行文件打?。?) ;
}
}
interface IFruit {
public void eat() ;
}
class Apple implements IFruit {
public void eat() {
System.out.println("\n削皮吃蘋果!") ;
}
}
class Orange implements IFruit {
public void eat() {
System.out.println("\n剝皮吃橘子!") ;
}
}
當(dāng)更換使用的IFruit子類的時(shí)候主方法沒有任何的變化就可以實(shí)現(xiàn)了子類的變更,這樣的設(shè)計(jì)就成為工廠設(shè)計(jì)模式。
總結(jié):以后只要是你編寫的接口如果要想取得接口的實(shí)例化對象,第一反應(yīng):寫工廠類。
五. 代理設(shè)計(jì)模式(Proxy)【重點(diǎn)】
所謂的代理嚴(yán)格來講就是兩個子類共同實(shí)現(xiàn)一個接口,其中一個子類負(fù)責(zé)真實(shí)的業(yè)務(wù)實(shí)現(xiàn),而另外的子類負(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ù)個數(shù)為0") ;
}
if(args.length != 1) {? // 沒有傳遞一個參數(shù)
System.out.println("對不起,程序執(zhí)行錯誤,正確的格式:java TestDemo 類名稱") ;
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() ;
// 利用第三方來進(jìn)行解耦合
IFruit fruit = Factory.getInstance(args[0]) ;
fruit.eat() ;
//ISubject sub = new ProxySubject(new RealSubject()) ;
ISubject sub = Factory.getInstance() ;
// 通過代理類對象發(fā)出,利用代理類來實(shí)現(xiàn)真實(shí)業(yè)務(wù)調(diào)用
sub.save() ;
}
}
class Factory {
// 因?yàn)榇藭r(shí)Factory產(chǎn)生實(shí)例化對象沒有意義
public static IFruit getInstance(String className) {? //通過普通類去操作
if("apple".equals(className)) {
return new Apple() ;
}
if("orange".equals(className)) {? ? ? ? ? ? ? ? ? //通過代理類去操作
return new Orange() ;
}
return null ;
}
public static ISubject getInstance() {
return new ProxySubject(new RealSubject()) ;
}
}
interface USB {
public void setup() ;? // 安裝USB驅(qū)動
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盤驅(qū)動!") ;
}
public void work() {
System.out.println("進(jìn)行數(shù)據(jù)傳輸!") ;
}
}
class Print implements USB {
public void setup() {
System.out.println("安裝打印機(jī)驅(qū)動!") ;
}
public void work() {
System.out.println("進(jìn)行文件打?。?) ;
}
}
interface IFruit {
public void eat() ;
}
class Apple implements IFruit {
public void eat() {
System.out.println("\n削皮吃蘋果!") ;
}
}
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)建代理類對象的時(shí)候必須設(shè)置要代理的真實(shí)主題
public ProxySubject(ISubject subject) {
this.subject = subject ;
}
public void broke() {
System.out.println("1、破門而入!") ;
}
public void get() {
System.out.println("2、得到見義勇為獎!") ;
}
public void save() {
this.broke() ;? ? ? ? ? // 真實(shí)操作前的準(zhǔn)備
this.subject.save() ;? ? // 調(diào)用真實(shí)的業(yè)務(wù)
this.get() ;? ? ? ? ? ? // 操作后的收尾
}
}
代理的本質(zhì):所有的真實(shí)業(yè)務(wù)操作都會有一個與之輔助的功能類共同完成。
六. 抽象類與接口的區(qū)別(面試題)
抽象類和接口都屬于常用的類結(jié)構(gòu)設(shè)計(jì),在開發(fā)之中都會出現(xiàn),不過如果按照優(yōu)先選擇來講,接口一定要比抽象類更方便。
除了單繼承的局限之外,實(shí)際上使用抽象類和接口都是類似的,但是在實(shí)際的開發(fā)之中,抽象類的設(shè)計(jì)要比接口復(fù)雜。

1. 接口是Java的核心,慢慢需要學(xué)習(xí)到接口的更多使用以及設(shè)計(jì);
2. 開發(fā)之中優(yōu)先考慮接口,以避免單繼承局限;