Java 介面理解

来源:https://www.cnblogs.com/otakus/archive/2020/01/08/12169072.html
-Advertisement-
Play Games

學習Spring有一段時間了,對java也有了一點瞭解,最不能理解的就是介面, 即使是寫了介面並實現了它,依然無法理解它到底有什麼用?看了其他幾篇博客,總結了一下自己的理解。 在JAVA編程語言中是一個抽象類型,是抽象方法的集合。一個類通過繼承介面的方式,從而來繼承介面的抽象方法。 先從一個通俗的解 ...


學習Spring有一段時間了,對java也有了一點瞭解,最不能理解的就是介面, 即使是寫了介面並實現了它,依然無法理解它到底有什麼用?看了其他幾篇博客,總結了一下自己的理解。

在JAVA編程語言中是一個抽象類型,是抽象方法的集合。一個類通過繼承介面的方式,從而來繼承介面的抽象方法。

先從一個通俗的解釋看起 (原文:C# 介面《通俗解釋》


如果你的工作是一個修水管的,一天客戶找上你讓你幫裝水管,但是有個要求,就是客戶喜歡管子是三角形的。

你立馬買了三角形的水管回來,在牆上弄個三角形的口子,客戶付了錢,你很開心今天有了收入,如下圖,很好:

280101020477366.png

但是好景不長,客戶過了一個星期又來找,因為他覺得三角形不好看,要讓你換成正方形的水管,你不得不換,因為顧客就是上帝。好吧,繼續在牆上弄個正方形的口子,然後又換成正方形的管子來接上。好了,如下圖:(但是可能覺得為什麼一開始不說要正方形的?因為需求總在變化。。。)

280104040472031.png

你累得滿頭大汗,但是還是完成了。可惜不久,客戶又來找你,因為他想換成橢圓形的管子了。雖然你很無奈,但是你還是不得不花了幾個小時完成。如下圖:

280105478602182.png

安裝完成,這時你可能在考慮,為什麼換不同形狀的水管,我都要大動干戈一番呢?於是你想到一個好方法,那就是牆上設計一個固定的水管並且是圓形的,當客戶喜歡什麼形狀的水管,那麼我只需要把客戶喜歡的水管的一頭做成圓形的,這樣,以後都不需要去動牆上的水管了。這是一個好辦法。就先在牆上弄個圓形的口,這個口就叫做介面。如下圖:

280109385166981.png

如你所見,牆上有個圓形的口,但是按照原本的:

三角形水管兩端是三角形
正方形水管兩端是正方形
橢圓形水管兩端是橢圓形

那是肯定接不上的,因為三角形、正方形、橢圓形的口怎麼和牆壁上圓形的口對接呢?

所以先要實現介面,把:

三角形水管一端做成圓形
正方形水管一端做成圓形
橢圓形水管一端做成圓形

如圖,所以,圓形介面做出來了,具體實現是客戶去安裝,介面本身並不會安裝其他形狀的水管,換句話說就是介面沒有具體實現,只是告訴你,你的水管要接入,必須有一端是圓形的(介面的約束),因為我只留這個圓形的介面,你要裝別的形狀的管子,先把一個弄成圓形的就好了(子類去實現介面的方法),不管什麼形狀,都要一個必須做成圓形才能對接得上,它必須要你按照我的規範來做。這就是為什麼新手會覺得介面什麼都不做,只定義介面,沒有任何實現,那不是多此一舉嗎?因為它的實現是子類去完成。這樣只要客戶喜歡什麼形狀的水管,只要實現了我的介面(圓形),都能對接得上,而且改變起來也很方便,只要把水管扭上去就行了,不用在去給牆壁挖洞了。


初步瞭解介面,再用代碼進一步理解

需求:公司有兩個人分別寫了2個動物類,讓你寫一個類來輸出它們各自喜歡的食物。

//A寫的Dog類,裡面有個likeFood方法
class Dog 
{

    public void likeFood()
    {
        Print("我是小狗,我喜歡吃肉");
    }
    
}
//B寫的Cat類,裡面有個likeFood方法,如下:
class Cat 
{

    public void likeFood()
    {
        Print("我是小貓,我喜歡吃魚");
    }
    
}

你寫的Zoo類如下:

//動物園類
class Zoo {

    public void show(Dog dog){
        dog.likeFood();
    }

    public void show(Cat cat){
        cat.likeFood();
    }

}

在輸出的時候使用如下:

void Main() 
{
    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
}

這一切工作良好,但好景不長,公司又需要給動物園增加一個猴子,讓C去寫這個Monkey類,並能輸出它喜歡的食物。


class Dog 
{

    public void likeFood()
    {
        Console.WriteLine("我是小狗,我喜歡吃肉");
    }
    
}
//B寫的Cat類,裡面有個likeFood方法,如下:
class Cat 
{

    public void likeFood()
    {
        Console.WriteLine("我是小貓,我喜歡吃魚");
    }
    
}
//C寫的Monkey類,裡面有個likeFood方法,如下:
class Monkey 
{

    public void likeFood()
    {
        Console.WriteLine("我是猴子,我喜歡吃桃子");
    }
    
}

於是你的Zoo類就變成了這樣,僅僅多了一個重載方法:

class Zoo 
{
    public void show(Dog dog)
    {
        dog.likeFood();
    }

    public void show(Cat cat)
    {
        cat.likeFood();
    }

    public void show(Monkey money)
    {
        money.likeFood();
    }
}

void Main() 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"

}

雖然只是增加了一個類,但善於思考的你馬上就會想到:“如果後面還有更多動物要輸出它們喜歡的食物,我的Zoo類都要修改,這對我來說不是一件好事。”
仔細觀察Zoo類,發現不變的是show方法,變化的是show方法是參數。因為每個動物都不一樣,所以參數也就不一樣。所以原來就需要重載多個方法。
如果有一個類,能接收所有動物,那不就解決了?沒錯,於是你想到了定義一個父類叫Animal,裡面有個likeFood方法,讓所有動物類去繼承Animal。

class Zoo 
{

    public void show(Animal animal)
    {
        animal.likeFood();
    }

}

class Animal 
{

    public void likeFood()
    {
        Print("我是Animal類");
    }

}

你告訴原來的A和B兩人,讓它們寫的動物類都去繼承Animal,並且裡面有個輸出動物喜歡食物的方法。

class Dog : Animal 
{

    public void likeFood()
    {
        Print("我是小狗,我喜歡吃肉");
    }
    
}

class Cat : Animal 
{

    public void likeFood()
    {
        Print("我是小貓,我喜歡吃魚");
    }
    
}

class Monkey : Animal 
{

    public void likeFood()
    {
        Print("我是猴子,我喜歡吃桃子");
    }
    
}

運行也一切良好,不管你以後還有什麼類,只要讓需要添加的動物類,繼承Animal,並有個likeFood方法,那麼你無需修改Zoo,只需要再main方法里傳入動物類的實例就能正常工作。
你大贊你聰明絕頂,這樣一來,你的Zoo根本不需要改變了。
有一天,公司新來一個人,暫時叫D吧,公司讓D寫個兔子類,你告訴D,你寫的Rabbit類必須繼承Animal,並且有一個它喜歡的食物的方法。

class Rabbit : Animal 
{

    public void favoriteFood()
    {
        Print("我是兔子,我喜歡吃蘿蔔");
    }
    
}

void Main() 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"
    zoo.show(new Rabbit()); //"我是Animal類"

}

Raabit並沒有輸出預想的結果,你不得不花了點時間去排查原因,最後你發現這不是什麼大問題,因為新來的D雖然寫了Rabbit類,但是他並不知道你們之前約定的動物喜歡的食物命名為:likeFood()
D寫的Rabbit類,裡面的方法叫:favoriteFood()
雖然最後D修改他的Rabbit類的方法為likeFood(),但你還是對這個問題做了一番思考,為什麼會導致這個問題?
那是因為沒有一種約束,使得子類繼承父類的時候必須實現父類的方法。有沒有一個類,能讓它的子類必須實現它定義的方法?有,那就是介面
於是你修改Animal為介面,代碼如下:

interface Animal {

    public void likeFood();
    
}

總結

由上述的例子可以總結介面的幾個用處:

  1. 使代碼易於維護,不管增加多少動物類,只要實現了animal介面,就都能拿來用
  2. 制定一種標準和規範,減少命名不統一帶來的一系列問題
  3. 方便團隊開發,設計者不用關心具體方法的實現,實現者可以清楚地知道自己的任務是什麼。

這裡還有一個問題:在我寫代碼時,都是先有方法,對象,再有類,然後才去抽象成介面,那麼我的每一個類都需要抽象成介面嗎?在開發過程中,每寫一個方法都要去寫它的介面的抽象方法,實現它的serviceTest以及調用它的controllerTest,最後才能真正放到程式中去用,即使是一個小小的功能都要做很多“無用功”,而且需求一旦變更,改起來不是更麻煩?


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0-前言 當我們發佈了微服務後,我們希望對各個應用的各個運行狀況進行一個監控;這個時候spring boot admin,就出場了; spring boot admin:是一個監控和管理spring boot 應用的開源監控組件, 它能夠對Actuator 中的信息進行界面化的展示,也可以監控所有 ...
  • JFrame圖解結構 有一視窗框架實例:JFrame win = new JFrame("視窗");在new JFrame()時,構建了JFrame實例對象,在實例中的Layered Pane層面版中,也預設構建了一個JPanel控制項面板。 使用 win.getContentPane( ); 是獲取 ...
  • 一、參考資料 1.《Python網路數據採集》圖靈工業出版社 2.《精通Python爬蟲框架Scrapy》人民郵電出版社 3.[Scrapy官方教程](http://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html) 4.[Python ...
  • 1.<% %>叫做腳本片段,其中寫的內容會翻譯在Servlet的Service方法中,顯然我們可以在Service方法中定義局部變數或者調用其他方法,但是不能 在Service中再定義其他的方法,也就是我們可以在<%%>中定義局部變數或者調用方法,但不能定義方法。在jsp頁面可以有多個腳本片段,但是 ...
  • [toc] logrus介紹 golang標準庫的日誌框架非常簡單,僅僅提供了print,panic和fatal三個函數。對於更精細的日誌級別、日誌文件分割,以及日誌分發等方面,並沒有提供支持。在golang的世界,流行的日誌框架包括logrus、zap、zerolog、seelog等。 logru ...
  • [toc] 一. JWT是什麼 "JSON Web Token" (JWT)是目前最流行的跨域身份驗證解決方案。 簡單說,OAuth 就是一種授權機制。數據的所有者告訴系統,同意授權第三方應用進入系統,獲取這些數據。系統從而產生一個短期的進入令(token),用來代替密碼,供第三方應用使用。 。 傳 ...
  • 創建一個web項目 導入依賴: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema- ...
  • 2個實體:訂單、商品,一個訂單可以包含多種商品,同時一種商品可以屬於多個訂單,即多對多。 商品表goods_tb: 訂單表order_tb: no是訂單編號,user_id與用戶表的id關聯。 需要新建一張中間表order_item_tb,引入2個“多”的主鍵作為外鍵,把這2個“多”聯繫起來: pu ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...