## Java基礎(chǔ)——接口
---
### 接口概念
---
官方解釋:Java接口是一系列方法的聲明,是一些方法特征的集合垒玲,**一個(gè)接口只有方法的特征沒有方法的實(shí)現(xiàn),因此這些方法可以在不同的地方被不同的類實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)**顾翼。
我的解釋:接口可以理解為一種特殊的類,里面全部是由**全局常量**和**公共的抽象方法**所組成蒜撮。接口是解決**Java無法使用多繼承**的一種手段暴构,但是接口在實(shí)際中更多的作用是**制定標(biāo)準(zhǔn)**的《文ィ或者我們可以直接把接口理解為**100%的抽象類**取逾,既接口中的方法**必須全部**是抽象方法。(JDK1.8之前可以這樣理解)
### 接口的特點(diǎn)
---
就像一個(gè)類一樣苹支,一個(gè)接口也能夠擁有方法和屬性砾隅,但是在接口中聲明的方法默認(rèn)是抽象的。(**即只有方法標(biāo)識(shí)符债蜜,而沒有方法體**)晴埂。
- 接口指明了一個(gè)類必須要做什么和不能做什么,相當(dāng)于類的藍(lán)圖寻定。
- 一個(gè)接口就是描述一種能力儒洛,比如“運(yùn)動(dòng)員”也可以作為一個(gè)接口,并且任何實(shí)現(xiàn)“運(yùn)動(dòng)員”接口的類都必須有能力實(shí)現(xiàn)奔跑這個(gè)動(dòng)作(或者implement move()方法)狼速,所以接口的作用就是告訴類琅锻,你要實(shí)現(xiàn)我這種接口代表的功能,你就必須實(shí)現(xiàn)某些方法向胡,我才能承認(rèn)你確實(shí)擁有該接口代表的某種能力恼蓬。
- 如果一個(gè)類實(shí)現(xiàn)了一個(gè)接口中要求的所有的方法,然而沒有提供方法體而僅僅只有方法標(biāo)識(shí)僵芹,那么這個(gè)類一定是一個(gè)抽象類处硬。(必須記住:**抽象方法只能存在于抽象類或者接口中拇派,但抽象類中卻能存在非抽象方法荷辕,即有方法體的方法。接口是百分之百的抽象類**)
- 一個(gè)JAVA庫中接口的例子是:Comparator 接口件豌,這個(gè)接口代表了“能夠進(jìn)行比較”這種能力桐腌,任何類只要實(shí)現(xiàn)了這個(gè)Comparator接口的話,這個(gè)類也具備了“比較”這種能力苟径,那么就可以用來進(jìn)行排序操作了案站。
### 為什么要用接口
---
1.? 接口被用來描述一種抽象。
2.? **因?yàn)镴ava不像C++一樣支持多繼承,所以Java可以通過實(shí)現(xiàn)接口來彌補(bǔ)這個(gè)局限**蟆盐。
3.? 接口也被用來實(shí)現(xiàn)解耦承边。
4.? 接口被用來實(shí)現(xiàn)抽象,而抽象類也被用來實(shí)現(xiàn)抽象石挂,為什么一定要用接口呢博助?接口和抽象類之間又有什么區(qū)別呢?原因是抽象類內(nèi)部可能包含非final的變量痹愚,但是在接口中存在的變量一定是final富岳,public,static的。
### 接口的語法實(shí)現(xiàn)
---
為了聲明一個(gè)接口拯腮,我們使用**interface**這個(gè)關(guān)鍵字窖式,在接口中的所有方法都必須只聲明方法標(biāo)識(shí),而不要去聲明具體的方法體动壤,因?yàn)榫唧w的方法體的實(shí)現(xiàn)是由繼承該接口的類來去實(shí)現(xiàn)的萝喘,因此,接口并不用管具體的實(shí)現(xiàn)琼懊。接口中的屬性默認(rèn)為Public Static Final.一個(gè)類實(shí)現(xiàn)這個(gè)接口必須實(shí)現(xiàn)這個(gè)接口中定義的所有的抽象方法阁簸。
一個(gè)簡單的接口就像這樣:擁有全局變量和抽象方法。
![](https://i-blog.csdnimg.cn/blog_migrate/14495312fa7d71be1dbed6ec0ab2da6b.png)
為了實(shí)現(xiàn)這個(gè)接口哼丈,我們使用implements關(guān)鍵詞去實(shí)現(xiàn)接口:
![](https://i-blog.csdnimg.cn/blog_migrate/9f68770a0bce4aef9db750e080c922a0.png)
其中testClass類實(shí)現(xiàn)了我們上面剛才定義的 in1 這個(gè)接口启妹,既然你要實(shí)現(xiàn)接口,也就是實(shí)現(xiàn)接口代表的一種能力醉旦,那么你就必須去實(shí)現(xiàn)接口給你規(guī)定的方法翅溺,只有把接口給你規(guī)定的抽象方法都給實(shí)現(xiàn)了,才承認(rèn)你這個(gè)類實(shí)現(xiàn)了這個(gè)接口髓抑,實(shí)現(xiàn)了這個(gè)接口代表的某種功能。上圖實(shí)現(xiàn)了接口中規(guī)定的display()方法优幸。
![](https://i-blog.csdnimg.cn/blog_migrate/698a27e9cbda9c6d9e45f92e7e42d358.png)
寫一個(gè)測(cè)試類吨拍,用來測(cè)試一下我們剛才實(shí)現(xiàn)的這個(gè)接口,因?yàn)閠estclass類的對(duì)象t實(shí)現(xiàn)了接口規(guī)定的display方法网杆,那么自然而然就可以調(diào)用display()方法咯羹饰。
![](https://i-blog.csdnimg.cn/blog_migrate/2ecd959f0576e6e9d068f7d8c590a96a.png)
有興趣的同學(xué)可以去這個(gè)在線IDE親自試一試:[點(diǎn)擊打開鏈接](https://ide.geeksforgeeks.org/9MpGUQC5uc "點(diǎn)擊打開鏈接")
### 接口的進(jìn)一步理解
---
我們知道,如果某個(gè)設(shè)備需要向電腦中讀取或者寫入某些東西碳却,這些設(shè)備一般都是采用USB方式與電腦連接的队秩,我們發(fā)現(xiàn),只要帶有USB功能的設(shè)備就可以插入電腦中使用了昼浦,那么我們可以認(rèn)為USB就是一種功能馍资,這種功能能夠做出很多的事情(實(shí)現(xiàn)很多的方法),其實(shí)USB就可以看做是一種標(biāo)準(zhǔn)关噪,一種接口鸟蟹,只要實(shí)現(xiàn)了USB標(biāo)準(zhǔn)的設(shè)備我就認(rèn)為你已經(jīng)擁有了USB這種功能乌妙。(因?yàn)槟銓?shí)現(xiàn)了我USB標(biāo)準(zhǔn)中規(guī)定的方法),下面是具體的例子:
先聲明USB接口:其中規(guī)定了要實(shí)現(xiàn)USB接口就必須實(shí)現(xiàn)接口規(guī)定實(shí)現(xiàn)的read( )和write( )這兩個(gè)方法建钥。
> ### **interface** USB {<!-- -->?
> **void** read();?
>?
> **void** write();?
> }
然后在寫一個(gè)U盤類和一個(gè)鍵盤類藤韵,這兩個(gè)類都去實(shí)現(xiàn)USB接口。(實(shí)現(xiàn)其中的方法)
> ### **class** YouPan **implements** USB {<!-- -->
>
> ### @Override
>
> ### **public void** read() {<!-- -->
>
> ### System.**_out_**.println(**"U****盤正在通過USB功能讀取數(shù)據(jù)"**);
>
> ### }
>
> ### @Override
>
> ### **public void** write() {<!-- -->
>
> ### System.**_out_**.println(**"U****盤正在通過USB功能寫入數(shù)據(jù)"**);
>
> ### }
>
> ### }
```html
這是U盤的具體實(shí)現(xiàn)熊经。
```
> ### **class** JianPan **implements** USB {<!-- -->
>
> ### @Override
>
> ### **public void** read() {<!-- -->
>
> ### System.**_out_**.println(**"****鍵盤正在通過USB功能讀取數(shù)據(jù)"**);
>
> ### }
>
> ### @Override
>
> ### **public void** write() {<!-- -->
>
> ### System.**_out_**.println(**"****鍵盤正在通過USB功能寫入數(shù)據(jù)"**);
>
> ### }
>
> ### }
```html
這是鍵盤的具體實(shí)現(xiàn)泽艘。
```
那么,現(xiàn)在U盤和鍵盤都實(shí)現(xiàn)了USB功能镐依,也就是說U盤和鍵盤都能夠調(diào)用USB接口中規(guī)定的方法匹涮,并且他們實(shí)現(xiàn)的方式都不一樣。
我們?cè)趯懸粋€(gè)測(cè)試馋吗,來看看具體的實(shí)現(xiàn):
> ### **public class** Main {<!-- -->
>
> ### **public static void** main(String[] args) {<!-- -->
>
> ### _//__生成一個(gè)實(shí)現(xiàn)可USB接口(標(biāo)準(zhǔn))的U盤對(duì)象_
>
> ### YouPan youPan = **new** YouPan();
>
> ### _//__調(diào)用U盤的read( )方法讀取數(shù)據(jù)_
>
> ### youPan.read();
>
> ### _//__調(diào)用U盤的write( )方法寫入數(shù)據(jù)_
>
> ### youPan.write();
>
> ### _//__生成一個(gè)實(shí)現(xiàn)可USB接口(標(biāo)準(zhǔn))的鍵盤對(duì)象_
>
> ### JianPan jianPan = **new** JianPan();
>
> ### _//__調(diào)用鍵盤的read( )方法讀取數(shù)據(jù)_
>
> ### jianPan.read();
>
> ### _//__調(diào)用鍵盤的write( )方法寫入數(shù)據(jù)_
>
> ### jianPan.write();
>
> ### }
>
> ### }
```html
結(jié)果如下:
```
![](https://i-blog.csdnimg.cn/blog_migrate/cbdad2ada2d6e1cc111686040cb89a3a.png)
感興趣的同學(xué)可以去在線IDE平臺(tái)自己驗(yàn)證一下:[點(diǎn)擊打開鏈接](https://ide.geeksforgeeks.org/ONQ6pbZ4mP "點(diǎn)擊打開鏈接")
### 關(guān)于接口的幾個(gè)重點(diǎn)
---
1.? **我們不能直接去實(shí)例化一個(gè)接口焕盟,因?yàn)榻涌谥械姆椒ǘ际浅橄蟮模菦]有方法體的**宏粤,這樣怎么可能產(chǎn)生具體的實(shí)例呢脚翘?但是,**我們可以使用接口類型的引用指向一個(gè)實(shí)現(xiàn)了該接口的對(duì)象绍哎,并且可以調(diào)用這個(gè)接口中的方法**来农。因此,上圖中最后的方法調(diào)用我們還可以這樣寫:(實(shí)際上就是使用了Java中多態(tài)的特性)
> ### **public class** Main {<!-- -->
>
> ### **public static void** main(String[] args) {<!-- -->
>
> ### _//__生成一個(gè)實(shí)現(xiàn)可USB接口(標(biāo)準(zhǔn))的U盤對(duì)象_
>
> ### //但是使用一個(gè)接口引用指向?qū)ο?/p>
>
> ### //USB接口類引用可以指向一個(gè)實(shí)現(xiàn)了USB接口的對(duì)象
>
> ### USB youPan = **new** YouPan();
>
> ### _//__調(diào)用U盤的read( )方法讀取數(shù)據(jù)_
>
> ### youPan.read();
>
> ### _//__調(diào)用U盤的write( )方法寫入數(shù)據(jù)_
>
> ### youPan.write();
>
> ### _//__生成一個(gè)實(shí)現(xiàn)可USB接口(標(biāo)準(zhǔn))的鍵盤對(duì)象_
>
> ### //但是使用一個(gè)接口引用指向?qū)ο?/p>
>
> ### //USB接口類引用可以指向一個(gè)實(shí)現(xiàn)了USB接口的對(duì)象
>
> ### USB jianPan = **new** JianPan();
>
> ### _//__調(diào)用鍵盤的read( )方法讀取數(shù)據(jù)_
>
> ### jianPan.read();
>
> ### _//__調(diào)用鍵盤的write( )方法寫入數(shù)據(jù)_
>
> ### jianPan.write();
>
> ### }
>
> ### }
2.一個(gè)類可以實(shí)現(xiàn)不止一個(gè)接口崇堰。
3.一個(gè)接口可以繼承于另一個(gè)接口沃于,或者另一些接口,接口也可以繼承海诲,并且可以多繼承繁莹。
4.一個(gè)類如果要實(shí)現(xiàn)某個(gè)接口的話,那么它必須要實(shí)現(xiàn)這個(gè)接口中的所有方法特幔。
5.接口中所有的方法都是抽象的和public的咨演,所有的屬性都是public,static,final的。
6.接口用來彌補(bǔ)類無法實(shí)現(xiàn)多繼承的局限蚯斯。
7.接口也可以用來實(shí)現(xiàn)解耦薄风。
### 接口的通俗理解
---
前面我們講多態(tài)的時(shí)候用“空調(diào)”——“遙控器”的方式去理解多態(tài),實(shí)際上在上面的的幾個(gè)重點(diǎn)中的第一條講的也是多態(tài)的實(shí)現(xiàn)拍嵌,比如遭赂,我們可以把“節(jié)能”作為一種標(biāo)準(zhǔn),或者說節(jié)能就是一個(gè)“接口”横辆,這個(gè)接口中有一個(gè)方法撇他,叫做變頻方法,任何空調(diào),如果要稱得上叫做節(jié)能空調(diào)的話逆粹,那么必須實(shí)現(xiàn)“節(jié)能”這個(gè)接口募疮,實(shí)現(xiàn)“節(jié)能”這個(gè)接口,也就必須實(shí)現(xiàn)“節(jié)能”接口中規(guī)定實(shí)現(xiàn)的“變頻”方法僻弹,這樣才算是真正的實(shí)現(xiàn)了“節(jié)能”這個(gè)接口阿浓,實(shí)現(xiàn)了“節(jié)能”這個(gè)功能。
當(dāng)某個(gè)空調(diào)實(shí)現(xiàn)了“節(jié)能”接口后蹋绽,這個(gè)空調(diào)就具備了節(jié)能的功能芭毙,那么我們也可以不用空調(diào)類的引用指向空調(diào)對(duì)象,我們可以直接使用一個(gè)“節(jié)能”接口類型引用的“遙控器”去指向“空調(diào)”卸耘,雖然這個(gè)“遙控器”上面只有一個(gè)按鍵退敦,只有一個(gè)“變頻”的方法,但是“遙控器”所指向的空調(diào)是實(shí)現(xiàn)了“節(jié)能”這個(gè)接口的蚣抗,是有“變頻”方法的實(shí)現(xiàn)的侈百,我們用這個(gè)只有一個(gè)“變頻”方法的遙控器去命令空調(diào)調(diào)用“變頻”方法,也是行得通的翰铡,實(shí)在不清楚的同學(xué)可以去看我的另一篇文章:[JAVA之對(duì)象的多態(tài)性](https://blog.csdn.net/qq_19782019/article/details/79788326 "JAVA之對(duì)象的多態(tài)性")钝域。
### 接口的標(biāo)識(shí)用法
---
雖然接口內(nèi)部定義了一些抽象方法,**但是并不是所有的接口內(nèi)部都必須要有方法**锭魔,比如Seriallizable接口例证,Seriallizable接口的作用是使對(duì)象能夠“序列化”,但是Seriallizable接口中卻沒有任何內(nèi)容迷捧,也就是說织咧,如果有一個(gè)類需要實(shí)現(xiàn)“序列化”的功能,則這個(gè)類必須去實(shí)現(xiàn)Seriallizable接口漠秋,但是卻并不用實(shí)現(xiàn)方法(因?yàn)榻涌谥袥]有方法)笙蒙,此時(shí),這個(gè)Serilizable接口就僅僅是一個(gè)“標(biāo)識(shí)”接口庆锦,是用來標(biāo)志一個(gè)類的捅位,標(biāo)志這個(gè)類具有這個(gè)“序列化”功能。具體的實(shí)現(xiàn)請(qǐng)參考我的另一篇文章——JAVA之IO流肥荔。
### 接口在生活中的思想體現(xiàn)
---
其實(shí),在我們的生活當(dāng)中朝群,有很多地方都體現(xiàn)了“接口”的思想燕耿,想必,正在閱讀這篇博文的你姜胖,是不是也喜歡攝影呢誉帅?
![佳能(Canon)EOS 80D 單反相機(jī) 單反套機(jī)(EF-S 18-200mm f/3.5-5.6 IS 單反鏡頭)](https://i-blog.csdnimg.cn/blog_migrate/59a00ce9c158150a9e47f38d1884a666.jpeg)
玩攝影的童鞋都知道,單反由**相機(jī)**和**鏡頭**組成,相機(jī)分不同的型號(hào)蚜锨,有半畫幅的档插,也有全畫幅的。鏡頭也是一樣的亚再,分長焦郭膛,短焦;還有定焦和變焦氛悬。每種鏡頭都有各自特定的發(fā)揮場(chǎng)景则剃。正是因?yàn)殓R頭的多元化,使得我們的攝影能夠“術(shù)業(yè)有專攻”如捅。大家想一想棍现,如果我們的單反相機(jī)部分和鏡頭部分是固定在一起的,不能夠更換鏡頭镜遣,那么將會(huì)多么的糟糕凹喊埂耿眉!
因此她肯,每個(gè)相機(jī)品牌為了能夠兼容不同的鏡頭,各自發(fā)布了一套鏡頭卡口的標(biāo)準(zhǔn)斗蒋,這套標(biāo)準(zhǔn)就好比我們前面提到的“接口”坚洽,都是某種“約束”戈稿。舉個(gè)栗子,我們佳能的相機(jī)讶舰,不管你是哪一家鏡頭生產(chǎn)廠商鞍盗,騰龍也好,適馬也好跳昼,只要你按照我佳能卡口的標(biāo)準(zhǔn)來生產(chǎn)鏡頭般甲,你生產(chǎn)的鏡頭都能夠很好的在我佳能相機(jī)上面驅(qū)動(dòng)。
```html
佳能EF卡口鏡頭是佳能公司為其單反相機(jī)和電影攝影機(jī)設(shè)計(jì)的一系列鏡頭鹅颊。除了佳能原廠生產(chǎn)的EF鏡頭外敷存,還有一些第三方鏡頭制造商生產(chǎn)的鏡頭也可以適配佳能EF卡口。以下是一些可以適配佳能EF卡口的鏡頭品牌:
1. 騰龍(Tamron):騰龍?zhí)峁┝硕嗫罴嫒菁涯蹺F卡口的鏡頭堪伍,如騰龍EF 18-400锚烦,這是一款覆蓋超廣角到超遠(yuǎn)攝的變焦鏡頭。
2. 適馬(Sigma):適馬也生產(chǎn)了一系列兼容佳能EF卡口的鏡頭帝雇,包括適馬EF 20mm F1.8 EX DG和適馬ART系列鏡頭涮俄,如35mm F1.4和85mm F1.4。
3. 永諾(Yongnuo):永諾提供了價(jià)格相對(duì)便宜的EF卡口鏡頭選項(xiàng)尸闸,例如永諾EF 50mm F1.4 EX和EF 100 F2.0彻亲。
4. 佳能(Canon):佳能原廠的EF鏡頭系列非常龐大孕锄,包括廣角、標(biāo)準(zhǔn)苞尝、遠(yuǎn)攝畸肆、微距等多種類型的鏡頭。
5. 第三方適配器:通過使用適配器宙址,其他品牌的鏡頭也可以安裝在佳能EF卡口相機(jī)上轴脐。例如,佳能推出了EF-EOS R 0.71x卡口適配器曼氛,允許在EOS R系列相機(jī)上使用EF鏡頭豁辉。
```
![](https://i-blog.csdnimg.cn/blog_migrate/34796e19db77bcd9a8c8e2dfcb375675.png)
因此,當(dāng)我們打開“狗東”舀患,準(zhǔn)備給自己的新相機(jī)買鏡頭的時(shí)候徽级,就不難發(fā)現(xiàn),我們需要根據(jù)自己相機(jī)的品牌來挑選特定卡口的鏡頭聊浅,這樣的鏡頭才能被我們的相機(jī)正常驅(qū)動(dòng)餐抢。
![](https://i-blog.csdnimg.cn/blog_migrate/fad39462e799446392514addc5a3f1b4.png)
回到Java上面來說,其實(shí)接口給我們帶來的最大的好處就是“解耦”了低匙,相機(jī)能夠搭配不同的鏡頭旷痕,才能有各種各樣的搭配玩法,變得更加的靈活顽冶。在軟件系統(tǒng)中也是一樣的欺抗,接口可以有很多不同“特色”的實(shí)現(xiàn)類,我們只需要聲明同一個(gè)接口强重,卻可以引用很多個(gè)該“接口”引申出來的“子類”绞呈,這不也大大增強(qiáng)了我們軟件系統(tǒng)中組件的靈活性嗎?
聰明的你间景,對(duì)于“接口”的理解是不是又更加的深入了呢佃声?
### 接口在編碼中的實(shí)踐
---
本文發(fā)表也有些年頭了,最開始僅僅只是想自己記錄一些學(xué)習(xí)心得倘要,沒想到竟然受到大家如此熱愛圾亏!陸陸續(xù)續(xù)看到評(píng)論區(qū)有些同學(xué)可能因?yàn)檫€沒有實(shí)際的工作經(jīng)驗(yàn),所以對(duì)接口在實(shí)際項(xiàng)目中的應(yīng)用封拧,還是缺少些感性的認(rèn)知志鹃,因此本著寵粉的精神,覺得可以通過模擬【發(fā)送短信】這個(gè)場(chǎng)景泽西,來給到工作經(jīng)驗(yàn)少的同學(xué)曹铃,對(duì)于接口有一個(gè)更加感性的認(rèn)知。
假設(shè)尝苇,你剛參加實(shí)習(xí)工作铛只,組長說:來來來,先給你個(gè)小需求練練手糠溜,我們系統(tǒng)現(xiàn)在需要接入短信平臺(tái)淳玩,你仔細(xì)看看阿里云的文檔,把阿里云SDK引入我們系統(tǒng)非竿,然后大概有100處地方蜕着,需要發(fā)送短信,你把發(fā)送短信的代碼分別加到這100處去红柱。
好家伙承匣,這不就是CTRL+C+V的事情嗎?于是你看到了阿里云發(fā)送短信只需要一個(gè)class就能完成工作,這個(gè)class長下面這樣:
```java
package com.lyj.demo.sms;
/**
* 阿里云短信實(shí)現(xiàn)類
*/
public class AliyunSMS {
? ? /**發(fā)送登錄短信
? ? * @param phoneNumbers 電話號(hào)碼
? ? * @param message 短信內(nèi)容
? ? */
? ? void sendLoginSMS(String phoneNumbers,String message) {
? ? ? ? System.out.println("【阿里云】登錄短信發(fā)送給"+phoneNumbers+":"+message);
? ? }
? ? /**
? ? * 忘記密碼發(fā)送短信
? ? *
? ? * @param phoneNumbers 電話號(hào)碼
? ? * @param message 短信內(nèi)容
? ? */
? ? void sendForgetSMS(String phoneNumbers,String message) {
? ? ? ? System.out.println("【阿里云】忘記密碼發(fā)送給"+phoneNumbers+":"+message);
? ? }
? ? /**發(fā)送營銷短信
? ? * @param phoneNumbers 電話號(hào)碼
? ? * @param message 短信內(nèi)容
? ? */
? ? void sendMarketingSMS(String phoneNumbers, String message) {
? ? ? ? System.out.println("【阿里云】營銷短信發(fā)送給"+phoneNumbers+":"+message);
? ? }
}
```
好家伙锤悄,100處代碼分散在項(xiàng)目中各個(gè)文件里面(雖然下圖100條發(fā)送語句都在一個(gè)文件中韧骗,但模擬100條發(fā)送語句分散在各種代碼文件中),光打開這些文件零聚,都耗費(fèi)了你一個(gè)下午的時(shí)間袍暴,終于在快下班的時(shí)候,你感嘆到:小小短信平臺(tái)隶症,拿下政模!你跑了跑項(xiàng)目,看到發(fā)送的短信內(nèi)容蚂会,露出了欣慰的笑容淋样。
![](https://i-blog.csdnimg.cn/blog_migrate/2f97909514a20af41144a954e9417273.png)
```java
【阿里云】登錄短信發(fā)送給15067471274:您的登錄短信驗(yàn)證碼為:231453
【阿里云】忘記密碼發(fā)送給15412345678:您的忘記密碼驗(yàn)證碼為:341531
【阿里云】營銷短信發(fā)送給13875232346:今晚20點(diǎn)優(yōu)惠多多,限時(shí)搶購胁住!
【阿里云】登錄短信發(fā)送給15067471274:您的登錄短信驗(yàn)證碼為:231453
【阿里云】忘記密碼發(fā)送給15412345678:您的忘記密碼驗(yàn)證碼為:341531
【阿里云】營銷短信發(fā)送給13875232346:今晚20點(diǎn)優(yōu)惠多多趁猴,限時(shí)搶購!
【阿里云】登錄短信發(fā)送給15067471274:您的登錄短信驗(yàn)證碼為:231453
【阿里云】忘記密碼發(fā)送給15412345678:您的忘記密碼驗(yàn)證碼為:341531
【阿里云】營銷短信發(fā)送給13875232346:今晚20點(diǎn)優(yōu)惠多多措嵌,限時(shí)搶購躲叼!
【阿里云】登錄短信發(fā)送給15067471274:您的登錄短信驗(yàn)證碼為:231453
【阿里云】忘記密碼發(fā)送給15412345678:您的忘記密碼驗(yàn)證碼為:341531
【阿里云】營銷短信發(fā)送給13875232346:今晚20點(diǎn)優(yōu)惠多多,限時(shí)搶購企巢!
```
正當(dāng)你準(zhǔn)備下班和女朋友約飯的時(shí)候枫慷,小組長來了,說:“完成的不錯(cuò)浪规,但是領(lǐng)導(dǎo)覺得阿里云太貴了或听,我們還是準(zhǔn)備用騰訊云,把阿里云換成騰訊云吧笋婿!明天就要上線哦誉裆!”
你笑著對(duì)小組長說:“好的,我今晚沒事缸濒,放心足丢,我加個(gè)班一下子就能搞定的事情粱腻!”,心里卻想著"vocal !斩跌,這B組長绍些,一下班就搞事情是吧!看我10分鐘就能搞定耀鸦,今晚和女朋友的飯局穩(wěn)穩(wěn)地柬批!"說著,你看著代碼袖订,卻發(fā)現(xiàn)代碼散落在各個(gè)文件中氮帐,你不禁陷入了沉思,這咋改奥骞谩上沐?阿里云和騰訊云的短信參數(shù)順序也不一樣,名字也不一樣楞艾,看來只能一個(gè)一個(gè)替換了奄容,說罷,你便開始一個(gè)一個(gè)代碼的刪除产徊,新增昂勒,忙的手忙腳亂。
下面是騰訊云發(fā)送短信的class:
```java
package com.lyj.demo.sms;
/**
* 騰訊云短信實(shí)現(xiàn)類
*/
public class TencentSMS {
? ? /**
? ? * /**發(fā)送登錄短信
? ? *
? ? * @param message? ? ? 短信內(nèi)容
? ? * @param phoneNumbers 電話號(hào)碼
? ? */
? ? void sendLoginSMS(String message, String phoneNumbers) {
? ? ? ? System.out.println("【騰訊云】登錄短信發(fā)送給"+phoneNumbers+":"+message);
? ? }
? ? /**
? ? * 忘記密碼發(fā)送短信
? ? *
? ? * @param message? ? ? 短信內(nèi)容
? ? * @param phoneNumbers 電話號(hào)碼
? ? */
? ? void sendForgetSMS(String message, String phoneNumbers) {
? ? ? ? System.out.println("【騰訊云】忘記密碼發(fā)送給"+phoneNumbers+":"+message);
? ? }
? ? /**
? ? * 發(fā)送營銷短信
? ? *
? ? * @param message? ? ? 短信內(nèi)容
? ? * @param phoneNumbers 電話號(hào)碼
? ? */
? ? void sendMarketingSMS(String message, String phoneNumbers) {
? ? ? ? System.out.println("【騰訊云】營銷短信發(fā)送給"+phoneNumbers+":"+message);
? ? }
}
```
你直接通過ctrl+R把阿里云替換成騰訊云
![](https://i-blog.csdnimg.cn/blog_migrate/330a83ec7a497966e44d5a5248b9eb29.png)
一跑項(xiàng)目舟铜,發(fā)現(xiàn)全是Bug,原來騰訊云的參數(shù)和阿里云全部是反的戈盈,你直接人傻了,這可咋辦啊谆刨,眼淚在眼眶里打轉(zhuǎn)塘娶。
![](https://i-blog.csdnimg.cn/blog_migrate/d8001232e1ad62d1eb1dfafe763fbe82.png)
此時(shí)小組長看到你難堪的樣子,說到:“你忘記你學(xué)的接口嗎痊夭?試試看刁岸,能不能用它去解決問題”。
此時(shí)你心里想到:“接口她我,接口這破玩意兒有啥用虹曙?”,以下引用的是本文真實(shí)的評(píng)論番舆,哈哈酝碳!
> 有個(gè)問題想不明白,接口由不同的類去實(shí)現(xiàn)恨狈,我干嘛不直接寫幾個(gè)類疏哗,為什么非要實(shí)現(xiàn)接口,接口里面屁也沒有禾怠,就定義了幾個(gè)方法名返奉。就好比人是一個(gè)類贝搁,你非要定義一個(gè)接口,規(guī)定了吃飯芽偏、拉屎徘公、睡覺等等方法,然后去實(shí)現(xiàn)這些方法哮针,有人用刀叉吃飯,有人用手抓坦袍,有人用筷子十厢。我干嘛不直接印度人定義一個(gè)類,中國人定義一個(gè)類捂齐,美國人定義一個(gè)類呢蛮放?想不通,在我看來接口屁用沒用奠宜。
那么包颁,我們看看,怎樣用接口去實(shí)現(xiàn)呢压真?小組長說娩嚼,既然騰訊云和阿里云有不同的地方,是不是也有相同的地方滴肿,即共性的地方岳悟,我們把共性的地方,即接收一個(gè)號(hào)碼參數(shù)泼差,一個(gè)短信內(nèi)容參數(shù)贵少,最后發(fā)送出去。這樣一個(gè)抽取成公共的接口堆缘,散落各個(gè)文件的100處代碼調(diào)用滔灶,都直接調(diào)用接口,而不是具體的實(shí)現(xiàn)類(阿里云或騰訊云)吼肥,這樣录平,后續(xù)我們無論變成什么短信平臺(tái),都不會(huì)需要去找到這100處代碼去做修改缀皱,因?yàn)樗麄円蕾嚨氖浅橄蟮慕涌谔蜒模蛔兊某橄蟆?/p>
下面是基于阿里云和騰訊云抽取出來的公共的短信接口CommonSmsService:
```java
package com.lyj.demo.sms;
public interface CommonSmsServiceInterface {
? ? /**發(fā)送登錄短信
? ? * @param phoneNumbers 號(hào)碼
? ? * @param message 短信
? ? */
? ? void sendLoginSMS(String phoneNumbers,String message);
? ? /**
? ? * 忘記密碼發(fā)送短信
? ? *
? ? * @param phoneNumbers 電話號(hào)碼
? ? * @param message 短信內(nèi)容
? ? */
? ? void sendForgetSMS(String phoneNumbers,String message) ;
? ? /**發(fā)送營銷短信
? ? * @param phoneNumbers 電話號(hào)碼
? ? * @param message 短信內(nèi)容
? ? */
? ? void sendMarketingSMS(String phoneNumbers, String message);
}
```
阿里云實(shí)現(xiàn)CommonSmsService這個(gè)接口的類AliyunSmsServiceImpl:
```java
package com.lyj.demo.sms;
public class AliyunSmsServiceInterfaceImpl implements CommonSmsServiceInterface {
? ? private final AliyunSMS aliyunSMS;
? ? public AliyunSmsServiceInterfaceImpl(AliyunSMS aliyunSMS) {
? ? ? ? this.aliyunSMS = aliyunSMS;
? ? }
? ? @Override
? ? public void sendLoginSMS(String phoneNumbers, String message) {
? ? ? ? aliyunSMS.sendLoginSMS(phoneNumbers, message);
? ? }
? ? @Override
? ? public void sendForgetSMS(String phoneNumbers, String message) {
? ? ? ? aliyunSMS.sendForgetSMS(phoneNumbers, message);
? ? }
? ? @Override
? ? public void sendMarketingSMS(String phoneNumbers, String message) {
? ? ? ? aliyunSMS.sendMarketingSMS(phoneNumbers, message);
? ? }
}
```
騰訊云實(shí)現(xiàn) CommonSmsService這個(gè)接口的實(shí)現(xiàn)類TencentSmsServiceImpl(此處要注意騰訊云的參數(shù)是反的哈!實(shí)現(xiàn)類里就要把他糾正過來):
```java
package com.lyj.demo.sms;
public class TencentSmsServiceInterfaceImpl implements CommonSmsServiceInterface {
? ? private final TencentSMS tencentSMS;
? ? public TencentSmsServiceInterfaceImpl(TencentSMS tencentSMS) {
? ? ? ? this.tencentSMS = tencentSMS;
? ? }
? ? @Override
? ? public void sendLoginSMS(String phoneNumbers, String message) {
? ? ? ? tencentSMS.sendLoginSMS(message,phoneNumbers);
? ? }
? ? @Override
? ? public void sendForgetSMS(String phoneNumbers, String message) {
? ? ? ? tencentSMS.sendForgetSMS(message,phoneNumbers);
? ? }
? ? @Override
? ? public void sendMarketingSMS(String phoneNumbers, String message) {
? ? ? ? tencentSMS.sendMarketingSMS(message.phoneNumbers);
? ? }
}
```
此時(shí)唆鸡,分散在各個(gè)文件中的100處代碼涝影,就可以寫成下面這樣:
![](https://i-blog.csdnimg.cn/blog_migrate/ad7bfd597eb90ff62d726f3394f6ad3c.png)
如果,你的小組長在“惡心”你争占,你再也不需要改100代碼了燃逻,只需要改下面兩行代碼這樣:
![](https://i-blog.csdnimg.cn/blog_migrate/074b2807acb62c47d156878b37e18bb9.png)
此時(shí)序目,你還會(huì)覺得接口沒有用嗎?原來要改100處的任務(wù)伯襟,使用接口后猿涨,只需要改2處,而且大大減少修改代碼導(dǎo)致的bug問題姆怪,此時(shí)叛赚,聰明的你,還會(huì)覺得【接口】是雞肋了嗎稽揭?
```bash
“依賴抽象俺附,而不是具體實(shí)現(xiàn)”是面向?qū)ο缶幊讨械囊粭l核心原則,它強(qiáng)調(diào)在設(shè)計(jì)和實(shí)現(xiàn)軟件系統(tǒng)時(shí)溪掀,應(yīng)該依賴于抽象的接口或抽象類事镣,而不是依賴于具體的實(shí)現(xiàn)類。這個(gè)原則有助于提高代碼的靈活性揪胃、可維護(hù)性和可測(cè)試性璃哟。下面是這個(gè)原則的一些關(guān)鍵點(diǎn):
1. 接口隔離:定義清晰的接口,使得客戶端代碼只依賴于它們需要的特定功能喊递,而不是依賴于一個(gè)龐大的随闪、多功能的類。
2. 開閉原則:軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開放骚勘,對(duì)修改封閉蕴掏。這意味著當(dāng)需要增加新功能時(shí),可以通過繼承或?qū)崿F(xiàn)新的抽象接口來擴(kuò)展系統(tǒng)调鲸,而不需要修改現(xiàn)有的代碼盛杰。
3. 單一職責(zé):每個(gè)類應(yīng)該只有一個(gè)引起它變化的原因,這通常意味著每個(gè)類應(yīng)該只負(fù)責(zé)一個(gè)具體的功能藐石。
4. 替換性:由于依賴的是抽象即供,所以具體的實(shí)現(xiàn)可以被替換,只要它們遵循相同的接口或抽象類于微。
5. 解耦:抽象層次上的依賴關(guān)系使得各個(gè)組件之間的耦合度降低逗嫡,從而更容易地進(jìn)行修改和替換。
6. 控制反轉(zhuǎn):依賴抽象而不是具體實(shí)現(xiàn)是控制反轉(zhuǎn)(IoC)的基礎(chǔ)株依,其中對(duì)象的創(chuàng)建和它們之間的依賴關(guān)系由外部容器管理驱证,而不是由對(duì)象自身管理。
7. 依賴注入:這是實(shí)現(xiàn)依賴抽象的一種方式恋腕,通過將依賴關(guān)系在運(yùn)行時(shí)注入到對(duì)象中抹锄,而不是在對(duì)象內(nèi)部創(chuàng)建。
通過遵循“依賴抽象,而不是具體實(shí)現(xiàn)”的原則伙单,開發(fā)者可以構(gòu)建出更加模塊化获高、靈活和可維護(hù)的系統(tǒng)。這種設(shè)計(jì)方式在現(xiàn)代軟件開發(fā)中非常普遍吻育,特別是在使用面向?qū)ο缶幊毯驮O(shè)計(jì)模式的場(chǎng)合念秧。
```
### 基礎(chǔ)不牢?新手不友好布疼?無人帶路摊趾?關(guān)注《揚(yáng)俊的小屋》公眾號(hào)吧!
---
![](https://i-blog.csdnimg.cn/blog_migrate/cb1290801e407ddc5e4ff6ae11a11e4d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d9d233a29f1390c2c1f9489614250e62.png)
### 參考資料
---
1.《Java開發(fā)實(shí)戰(zhàn)經(jīng)典》 李興華著 清華大學(xué)出版社
2.https://www.geeksforgeeks.org/interfaces-in-java 作者:Mehak Kumar. and Nitsdheerendra. 翻譯:劉揚(yáng)俊
## 博客文章版權(quán)說明
---
第一條? 本博客文章僅代表作者本人的觀點(diǎn)游两,不保證文章等內(nèi)容的有效性砾层。
第二條? 本博客部分內(nèi)容轉(zhuǎn)載于合作站點(diǎn)或摘錄于部分書籍,但都會(huì)注明作/譯者和原出處器罐。如有不妥之處,敬請(qǐng)指出渐行。
第三條? 在**征得本博客作者同意的情況下**轰坊,本博客的作品**允許非盈利性引用,并請(qǐng)注明出處:“作者:____轉(zhuǎn)載自____”字樣祟印,以尊重作者的勞動(dòng)成果**肴沫。版權(quán)歸原作/譯者所有。**未經(jīng)允許蕴忆,嚴(yán)禁轉(zhuǎn)載**颤芬。
第四條 **對(duì)非法轉(zhuǎn)載者,“揚(yáng)俊的小屋”和作/譯者保留采用法律手段追究的權(quán)利**套鹅。
第五條? 本博客之聲明以及其修改權(quán)站蝠、更新權(quán)及最終解釋權(quán)均屬“揚(yáng)俊的小屋”。
第六條 以上聲明的解釋權(quán)歸“揚(yáng)俊的小屋”所有卓鹿。