Java接口学习(接口的使用、简单工厂、代理模式、接口和抽象类的区别)
前言引入
官方解釋:Java接口是一系列方法的聲明,是一些方法特征的集合,一個(gè)接口只有方法的特征沒(méi)有方法的實(shí)現(xiàn),因此這些方法可以在不同的地方被不同的類(lèi)實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)。
我的理解:接口可以理解為一種特殊的抽象類(lèi),里面全部是由全局常量和公共的抽象方法所組成。接口是解決Java無(wú)法使用多繼承的一種手段,可以實(shí)現(xiàn)多個(gè)接口的實(shí)現(xiàn),但是接口在實(shí)際中更多的作用是制定標(biāo)準(zhǔn)的?;蛘呶覀兛梢灾苯影呀涌诶斫鉃?00%的抽象類(lèi),接口中的方法必須全部是抽象方法。
一、接口的基本概念與主要特點(diǎn);
二、接口的使用;
三、接口應(yīng)用:簡(jiǎn)單工廠設(shè)計(jì)模式、代理設(shè)計(jì)模式簡(jiǎn)單實(shí)現(xiàn)。
一、接口的基本概念與主要特點(diǎn)
- 如果一個(gè)類(lèi)中只是由抽象方法和全局變量所組成,那么在這種情況下不會(huì)定義為抽象類(lèi),而會(huì)定義為接口,接口嚴(yán)格來(lái)講是一個(gè)抽象類(lèi),而且這個(gè)類(lèi)里面只有抽象方法和全局變量,沒(méi)有構(gòu)造方法。
1.1 接口特點(diǎn)
就像一個(gè)類(lèi)一樣,一個(gè)接口也能夠擁有方法和屬性,但是在接口中聲明的方法默認(rèn)是抽象的。(即只有方法標(biāo)識(shí)符,而沒(méi)有方法體)。
- 接口不能實(shí)例化,可以按照多態(tài)的方式來(lái)實(shí)例化;
- 接口沒(méi)有構(gòu)造方法;
- 接口指明了一個(gè)類(lèi)必須要做什么和不能做什么,相當(dāng)于類(lèi)的行為的規(guī)劃;
- 一個(gè)接口就是描述一種能力,比如 “Animal” 也可以作為一個(gè)接口,并且任何實(shí)現(xiàn)“Animal”接口的類(lèi)都必須有能力實(shí)現(xiàn) “奔跑”這個(gè)動(dòng)作(或者implement run()方法),所以接口的作用就是告訴類(lèi),你要實(shí)現(xiàn)我這種接口代表的功能,你就必須實(shí)現(xiàn)某些方法,我才能承認(rèn)你確實(shí)擁有該接口代表的某種能力;
- 如果一個(gè)類(lèi)實(shí)現(xiàn)了一個(gè)接口中要求的所有的方法,然而沒(méi)有提供方法體而僅僅只有方法標(biāo)識(shí),那么這個(gè)類(lèi)一定是一個(gè)抽象類(lèi)。(牢記:抽象方法只能存在于抽象類(lèi)或者接口中,但抽象類(lèi)中卻能存在非抽象方法,即有方法體的方法。接口是百分之百的抽象類(lèi))
一個(gè)JAVA庫(kù)中接口的例子是:Iterator 接口,這個(gè)接口代表了“能夠進(jìn)行迭代遍歷”這種能力,任何類(lèi)只要實(shí)現(xiàn)了這個(gè) “ Iterator” 接口的話,這個(gè)類(lèi)也具備了 “迭代遍歷” 這種能力,那么就可以用來(lái)進(jìn)行元素遍歷操作了。
1.2 為什么要用接口
二、接口的使用
2.1 接口的定義
要定義一個(gè)接口使用 interface 關(guān)鍵字完成。
interface A{ // 定義接口public static final String MSG="hello";// 抽象方法public abstract void print(); }由于接口里面存在有抽象方法,所以接口對(duì)象不能用關(guān)鍵字new進(jìn)行實(shí)例化的操作。先說(shuō)幾個(gè)接口使用的限制:
- 接口必須要有子類(lèi)實(shí)現(xiàn),此時(shí)一個(gè)子類(lèi)可以使用implement關(guān)鍵字實(shí)現(xiàn)多個(gè)接口;
- 接口的子類(lèi)(如果不是抽象類(lèi)),必須要覆寫(xiě)接口中的全部抽象方法;
- 接口中的對(duì)象可以利用子類(lèi)對(duì)象的向上轉(zhuǎn)型進(jìn)行實(shí)例化的操作。
范例:實(shí)現(xiàn)接口
interface A { // 定義了接口public static final String MSG = "hello";// 抽象方法public abstract void print(); } interface B {public abstract void get(); } class X implements A,B { // 實(shí)現(xiàn)多個(gè)接口public void get() {System.out.println("B接口的抽象方法");}public void print() {System.out.println("A接口的抽象方法");} } public class TestDemo {public static void main(String args[]){X x = new C();//實(shí)例化子類(lèi)對(duì)象A a = x;B b = x;// A a = new X();//向上轉(zhuǎn)型// B b = new X();//向上轉(zhuǎn)型a.print();b.get();} }以上的代碼實(shí)例化了 “x” 對(duì)象,由于 X 是 A 和 B 的子類(lèi),那么 X 類(lèi)對(duì)象可以變?yōu)?A 接口或 B 接口類(lèi)的對(duì)象。
在定義上 A 和 B 接口沒(méi)有任何的直接聯(lián)系,但是這兩個(gè)接口卻同時(shí)擁有一個(gè)子類(lèi): X 子類(lèi),不要被類(lèi)型和名稱(chēng)所迷惑,因?yàn)樽罱K實(shí)例化的 X 的子類(lèi),而這個(gè)子類(lèi)屬于 B 類(lèi)的對(duì)象,所以以上的代碼行的通,代碼編寫(xiě)上并不是很友好。
- 子類(lèi)除了可以實(shí)現(xiàn)接口,還可能去繼承抽象類(lèi),所以說(shuō)一個(gè)子類(lèi)又要繼承抽象類(lèi),還要實(shí)現(xiàn)接口的話,先使用extends繼承,而后使用implements實(shí)現(xiàn)。
2.2 子類(lèi)繼承和接口實(shí)現(xiàn)使用
代碼示例(即有繼承關(guān)系又有接口實(shí)現(xiàn))
interface A { // 定義了接口public static final String MSG = "hello";// 抽象方法public abstract void print();}interface B {public abstract void get();}abstract class C { // 定義一個(gè)抽象類(lèi)public abstract void change();}class X extends C implements A,B {//先繼承,再實(shí)現(xiàn)接口public void get() {System.out.println("B接口的抽象方法");}public void print() {System.out.println("A接口的抽象方法");}public void change(){System.out.println("C類(lèi)的抽象方法");}}對(duì)接口而言,發(fā)現(xiàn)里面的組成是抽象方法和全局變量,所以很多的時(shí)候有些人為了簡(jiǎn)略不會(huì)寫(xiě) abstract 和 public static final,并且在方法上是否編寫(xiě) public 結(jié)果都是一樣的,因?yàn)樵诮涌诶锩嬷荒軌蚴褂靡环N訪問(wèn)權(quán)限——public。以下兩個(gè)接口的定義效果是一樣的:
interface A{public static final String MSG="HELLO";public void fun(); } // 另一種定義方式 ,常量的話 我們 一般 寫(xiě)成 public static final String MSG = “Hello”; Interface A{String MSG=”HELLO”;void fun(); }一個(gè)抽象類(lèi)可以繼承一個(gè)抽象類(lèi),一個(gè)接口可以使用extends關(guān)鍵字同時(shí)繼承多個(gè)接口,接口不可以繼承抽象類(lèi)。
2.3 接口的多繼承
范例:接口的多繼承
interface A {public void funA(); } interface B {public void funB(); } interface C extends A,B {public void funC(); } class X implements C {public void funA() { } // 覆寫(xiě)全部的方法public void funB() { }public void funC() { } }從繼承關(guān)系上講接口比抽象類(lèi)的優(yōu)勢(shì)明顯:
- 一個(gè)抽象類(lèi)只能繼承一個(gè)抽象類(lèi),而接口沒(méi)有這個(gè)限制;
- 一個(gè)子類(lèi)只能夠繼承一個(gè)抽象類(lèi),而卻可以實(shí)現(xiàn)多個(gè)接口。
在java中,接口解決了單繼承的局限性問(wèn)題。 雖然從接口本身的概念來(lái)講只能夠由抽象方法和全局變量所組成,但是所有的內(nèi)部結(jié)構(gòu)不受這些要求的限制,也就是說(shuō)在接口中可以定義普通內(nèi)部類(lèi)、抽象內(nèi)部類(lèi)、內(nèi)部接口。
2.4 在接口中定義抽象類(lèi)和 static 接口
范例:在接口里定義抽象類(lèi)
interface A{public void funA();// 獨(dú)立的class文件,abstract class B{// 在接口中的abstract可以不用寫(xiě),但在抽象類(lèi)中的抽象方法必須要寫(xiě)abstractpublic abstract void funB();}interface Entry { // 接口中定義接口 ,Map.Entry HashMap 源碼 Collection 中的 Iterator 接口} } class X implements A { // X 實(shí)現(xiàn)了A接口public void funA() {System.out.println("Hello World");}class Y extends B { // 內(nèi)部類(lèi) Y繼承了抽象類(lèi) B 實(shí)現(xiàn)了 funB() 方法public void funB () {System.out.println("hello C");} } }在接口中定義static接口
interface A {public void funA();// static聲明的內(nèi)部接口為外部接口,static聲明的內(nèi)部類(lèi)為外部類(lèi)static interface B{ // 外部接口,public void funB();}}class X implements A.B{ // 實(shí)現(xiàn)時(shí)使用“類(lèi)名.內(nèi)部接口”public void funB() {}先期總結(jié):接口在實(shí)際的開(kāi)發(fā)中三大核心作用:
- 定義不同層之間的操作標(biāo)準(zhǔn);
- 表示一種操作的能力;
- 表示將服務(wù)端的遠(yuǎn)程方法視圖暴露給客戶(hù)。
2.5 接口中的實(shí)際應(yīng)用——標(biāo)準(zhǔn)
電腦上可以使用U盤(pán)、Mp3、打印機(jī),這些設(shè)備都是連接到USB設(shè)備上的。
- 所有的代碼如果要進(jìn)行開(kāi)發(fā),一定要首先開(kāi)發(fā)出USB接口標(biāo)準(zhǔn),因?yàn)橛辛藰?biāo)準(zhǔn)后電腦才可以去使用這些標(biāo)準(zhǔn),設(shè)備廠商才可以設(shè)計(jì)USB設(shè)備。
范例:定義USB標(biāo)準(zhǔn)(標(biāo)準(zhǔn)可以連接不同層的操作)
// 標(biāo)準(zhǔn)可以連接不同層的操作 interface USB { // 定義標(biāo)準(zhǔn)一定是接口public void start();public void stop(); }范例:定義電腦
不管以后有多少個(gè)設(shè)備,只要它是 USB 標(biāo)準(zhǔn)的實(shí)現(xiàn)子類(lèi),它都可以在電腦上使用。
范例:定義U盤(pán)
class Flash implements USB {public void start(){System.out.println("U盤(pán)開(kāi)始使用");}public void stop(){System.out.println("u盤(pán)停止使用");} }范例:定義打印機(jī)
class Print implements USB {public void start(){System.out.println("打印機(jī)開(kāi)始工作");}public void stop(){System.out.println("打印機(jī)停止工作");} }按照這樣的方式,準(zhǔn)備好多個(gè)子類(lèi)都可以在電腦的plugin()方法上使用
interface USB{//定義標(biāo)準(zhǔn)一定是接口public void start();public void stop(); } class Computer {public void plugin(USB usb){//插入usb.start();usb.stop();} } class Flash implements USB{public void start(){System.out.println("U盤(pán)開(kāi)始使用");}public void stop(){System.out.println("u盤(pán)停止使用");} } class Print implements USB{public void start(){System.out.println("打印機(jī)開(kāi)始工作");}public void stop(){System.out.println("打印機(jī)停止工作");} } public class TestDemo {public static void main(String args[]){Computer com = new Computer();com.plugin(new Flash());com.plugin(new Print());} }在現(xiàn)實(shí)生活中,標(biāo)準(zhǔn)的概念隨處可見(jiàn),而在程序里面標(biāo)準(zhǔn)就是用接口來(lái)實(shí)現(xiàn)的。
三、接口的應(yīng)用(簡(jiǎn)單工廠和代理)
3.1 接口的應(yīng)用——工廠設(shè)計(jì)模式(Factory 簡(jiǎn)單介紹)
下面觀察一段程序代碼
interface Fruit {public void eat(); } class Apple implements Fruit {public void eat() {System.out.println("吃蘋(píng)果");} } public class TestDemo {public static void main(String args[]){Fruit f = new Apple();f.eat(); }以上的程序可以通過(guò)主方法得到Fruit接口對(duì)象,這種代碼設(shè)計(jì)有問(wèn)題嗎?
本端程序的問(wèn)題就是出現(xiàn)了關(guān)鍵字“new”。
評(píng)判一段代碼是否真的好,有這么幾個(gè)標(biāo)準(zhǔn):
-
客戶(hù)端可以調(diào)用,不需要關(guān)注具體的細(xì)節(jié);
-
客戶(hù)端之外的代碼修改,不影響用戶(hù)端的使用,即:用戶(hù)端可以不關(guān)
心代碼是否變更。
一個(gè)接口不可能只有一個(gè)子類(lèi),對(duì)于Fruit也有可能產(chǎn)生多個(gè)子類(lèi)對(duì)象(Apple,Orange)。
現(xiàn)在每次客戶(hù)端想要得到新的子類(lèi)對(duì)象,都需要修改代碼,如果在客戶(hù)端實(shí)例化對(duì)象,那么每一要更換對(duì)象對(duì)象,都需要修改客戶(hù)端上的代碼,這樣的做法是不友好的。
- 現(xiàn)在要解決是如何得到一個(gè)Fruit接口對(duì)象,而后進(jìn)行方法的調(diào)用,至于這個(gè)接口對(duì)象是被誰(shuí)實(shí)例化的,不是我客戶(hù)端的工作?,F(xiàn)在最大的問(wèn)題在于關(guān)鍵字new,這一問(wèn)題可以理解為耦合度太高。耦合度太高的直接問(wèn)題就是代碼不方便維護(hù),相當(dāng)于A與B綁定在一起。
程序 -> JVM -> 適應(yīng)不同的操作系統(tǒng)(A->C->B)
范例:增加一個(gè)過(guò)渡
class Factory {public static Fruit getInstance(String className){if ("apple".equals(className)) {return new Apple();} else if ("orange".equals(className)) {return new Orange();} else {return null;}} } public class TestDemo {public static void main(String args[]){Fruit f = Factory.getInstance("apple");f.eat();} }- 現(xiàn)在的客戶(hù)端不會(huì)看見(jiàn)具體的子類(lèi),因?yàn)樗械慕涌趯?duì)象都是通過(guò)Factory類(lèi)取得子類(lèi)對(duì)象,則只需要修改Factory類(lèi)即可,但是客戶(hù)端不會(huì)發(fā)生變化。
工廠類(lèi)跟操作的接口類(lèi)有關(guān),也跟所有的子類(lèi)有關(guān);客戶(hù)端可看見(jiàn)接口,還可以看見(jiàn)工廠,客戶(hù)端使用getInstance()方法找到工廠類(lèi)中定義的方法,這個(gè)方法返回接口對(duì)象,通過(guò)接口對(duì)象就可以獲得接口中的操作方法。
面試題:請(qǐng)編寫(xiě)一個(gè)Factory程序
3.2 接口的應(yīng)用——代理設(shè)計(jì)模式(Proxy簡(jiǎn)單介紹)
皇帝寵幸妃子的為例,具體步驟圖中已經(jīng)列出。
范例:轉(zhuǎn)換為程序
- 代理設(shè)計(jì)模式的核心精髓就在于一個(gè)主題操作接口(可能有多種方法),核心業(yè)務(wù)主題只完成核心功能。而代理主題負(fù)責(zé)所有與核心主題有關(guān)的輔助型操作。
代理模式的主要角色如下。
-
代理主題類(lèi)(Subject ):通過(guò)接口或抽象類(lèi)聲明真實(shí)主題和代理對(duì)象實(shí)現(xiàn)的業(yè)務(wù)方法。
-
真實(shí)主題類(lèi)(RealSubject):實(shí)現(xiàn)了抽象主題中的具體業(yè)務(wù),是代理對(duì)象所代表的真實(shí)對(duì)象,是最終要引用的對(duì)象。
-
代理(ProxySubject)類(lèi):提供了與真實(shí)主題相同的接口,其內(nèi)部含有對(duì)真實(shí)主題的引用,它可以訪問(wèn)、控制或擴(kuò)展真實(shí)主題的功能。
使用代理(靜態(tài)代理)的目的和缺陷:
-
可以做到在不修改目標(biāo)對(duì)象的功能前提下,對(duì)目標(biāo)功能擴(kuò)展.
缺點(diǎn):
- 因?yàn)榇韺?duì)象需要與目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類(lèi),類(lèi)太多.同時(shí),一旦接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要維護(hù).
面試題:請(qǐng)編寫(xiě)一個(gè)Proxy模式程序
四、抽象類(lèi)與接口的區(qū)別(面試題)
抽象類(lèi)和接口使用的形式上是十分相似的。
4.1 表格對(duì)比
4.2 文字描述
接口與抽象類(lèi)的區(qū)別:
-
抽象類(lèi)是對(duì)一種事物的抽象,即對(duì)類(lèi)抽象,而接口是對(duì)行為的抽象,也就是對(duì)方法的抽象。
-
抽象類(lèi)可以有具體的成員方法,而接口中只能存在抽象方法;
-
抽象類(lèi)中的成員變量可以是各種類(lèi)型的,而接口中的成員變量只能是public static final類(lèi)型的;
-
接口中不能含有靜態(tài)代碼塊以及靜態(tài)方法,而抽象類(lèi)可以有靜態(tài)代碼塊和靜態(tài)方法;
-
一個(gè)類(lèi)只能繼承一個(gè)抽象類(lèi),而一個(gè)類(lèi)卻可以實(shí)現(xiàn)多個(gè)接口。
-
抽象類(lèi)如果需要添加新的方法,可以直接在抽象類(lèi)中添加具體的實(shí)現(xiàn),子類(lèi)可以不進(jìn)行變更;而接口進(jìn)行了變更,則所有實(shí)現(xiàn)這個(gè)接口的類(lèi)都必須進(jìn)行相應(yīng)的改動(dòng)(功能擴(kuò)展)。經(jīng)過(guò)比較可以發(fā)現(xiàn),抽象類(lèi)中支持的功能絕對(duì)要比接口更多,但是抽象類(lèi)有單繼承局限性。
代碼編寫(xiě)的習(xí)慣:
- 在進(jìn)行某些公共操作的時(shí)候一定要定義出接口;
- 有了接口后需利用子類(lèi)完善方法;
- 如果是自己寫(xiě)的接口,絕對(duì)不要使用關(guān)鍵字new直接實(shí)例化接口子類(lèi),使用工廠類(lèi)完成。
四、總結(jié)
參考文檔:https://www.cnblogs.com/qmdx00/p/7469379.html
https://blog.csdn.net/qq_19782019/article/details/80259836
總結(jié)
以上是生活随笔為你收集整理的Java接口学习(接口的使用、简单工厂、代理模式、接口和抽象类的区别)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: rust(51)-rust工具,prel
- 下一篇: 你知道Java中final和static