java基礎(11):介面、多態

来源:https://www.cnblogs.com/liuhui0308/archive/2019/10/07/11632728.html
-Advertisement-
Play Games

1. 介面 1.1 介面概念 介面是功能的集合,同樣可看做是一種數據類型,是比抽象類更為抽象的”類”。 介面只描述所應該具備的方法,並沒有具體實現,具體的實現由介面的實現類(相當於介面的子類)來完成。這樣將功能的定義與實現分離,優化了程式設計。 請記住:一切事物均有功能,即一切事物均有介面。 1.2 ...


1. 介面

1.1 介面概念

介面是功能的集合,同樣可看做是一種數據類型,是比抽象類更為抽象的”類”。

介面只描述所應該具備的方法,並沒有具體實現,具體的實現由介面的實現類(相當於介面的子類)來完成。這樣將功能的定義與實現分離,優化了程式設計。

請記住:一切事物均有功能,即一切事物均有介面。

1.2 介面的定義

與定義類的class不同,介面定義時需要使用interface關鍵字。

定義介面所在的仍為.java文件,雖然聲明時使用的為interface關鍵字的編譯後仍然會產生.class文件。這點可以讓我們將介面看做是一種只包含了功能聲明的特殊類。

定義格式:

public interface 介面名 {
抽象方法1;
抽象方法2;
抽象方法3;
}

使用interface代替了原來的class,其他步驟與定義類相同:

  介面中的方法均為公共訪問的抽象方法

  介面中無法定義普通的成員變數

1.3 類實現介面

類與介面的關係為實現關係,即類實現介面。實現的動作類似繼承,只是關鍵字不同,實現使用implements。

其他類(實現類)實現介面後,就相當於聲明:”我應該具備這個介面中的功能”。實現類仍然需要重寫方法以實現具體的功能。

格式:

classimplements 介面 {
    重寫介面中方法
} 

在類實現介面後,該類就會將介面中的抽象方法繼承過來,此時該類需要重寫該抽象方法,完成具體的邏輯。

  介面中定義功能,當需要具有該功能時,可以讓類實現該介面,只聲明瞭應該具備該方法,是功能的聲明。

  在具體實現類中重寫方法,實現功能,是方法的具體實現。

於是,通過以上兩個動作將功能的聲明與實現便分開了。(此時請重新思考:類是現實事物的描述,介面是功能的集合。)

1.4 介面中成員的特點

1、介面中可以定義變數,但是變數必須有固定的修飾符修飾,public static final 所以介面中的變數也稱之為常量,其值不能改變。後面我們會講解static與final關鍵字

2、介面中可以定義方法,方法也有固定的修飾符,public abstract

3、介面不可以創建對象。

4、子類必須覆蓋掉介面中所有的抽象方法後,子類才可以實例化。否則子類是一個抽象類。

interface Demo { ///定義一個名稱為Demo的介面。
    public static final int NUM = 3;// NUM的值不能改變
    public abstract void show1();
    public abstract void show2();
}

//定義子類去覆蓋介面中的方法。類與介面之間的關係是 實現。通過 關鍵字 implements
class DemoImpl implements Demo { //子類實現Demo介面。
    //重寫介面中的方法。
    public void show1(){}
    public void show2(){}
}

1.5 介面的多實現

瞭解了介面的特點後,那麼想想為什麼要定義介面,使用抽象類描述也沒有問題,介面到底有啥用呢?

介面最重要的體現:解決多繼承的弊端。將多繼承這種機制在java中通過多實現完成了。

interface Fu1
{
    void show1();
}
interface Fu2
{
    void show2();
}
class Zi implements Fu1,Fu2// 多實現。同時實現多個介面。
{
    public void show1(){}
    public void show2(){}
}

怎麼解決多繼承的弊端呢?

弊端:多繼承時,當多個父類中有相同功能時,子類調用會產生不確定性。

其實核心原因就是在於多繼承父類中功能有主體,而導致調用運行時,不確定運行哪個主體內容。

為什麼多實現能解決了呢?

因為介面中的功能都沒有方法體,由子類來明確。

1.6 類繼承類同時實現介面

介面和類之間可以通過實現產生關係,同時也學習了類與類之間可以通過繼承產生關係。當一個類已經繼承了一個父類,它又需要擴展額外的功能,這時介面就派上用場了。

子類通過繼承父類擴展功能,通過繼承擴展的功能都是子類應該具備的基礎功能。如果子類想要繼續擴展其他類中的功能呢?這時通過實現介面來完成。

class Fu {
    public void show(){}
}
interface Inter {
    pulbic abstract void show1();
}
class Zi extends Fu implements Inter {
    public void show1() {
    }
}

介面的出現避免了單繼承的局限性。父類中定義的事物的基本功能。介面中定義的事物的擴展功能。

1.7 介面的多繼承

學習類的時候,知道類與類之間可以通過繼承產生關係,介面和類之間可以通過實現產生關係,那麼介面與介面之間會有什麼關係。

多個介面之間可以使用extends進行繼承。

interface Fu1{
    void show();
}
interface Fu2{
    void show1();
}
interface Fu3{
    void show2();
}
interface Zi extends Fu1,Fu2,Fu3{
    void show3();
}

在開發中如果多個介面中存在相同方法,這時若有個類實現了這些介面,那麼就要實現介面中的方法,由於介面中的方法是抽象方法,子類實現後也不會發生調用的不確定性。

1.8 介面的思想

前面學習了介面的代碼體現,現在來學習介面的思想,接下里從生活中的例子進行說明。

舉例:我們都知道電腦上留有很多個插口,而這些插口可以插入相應的設備,這些設備為什麼能插在上面呢?主要原因是這些設備在生產的時候符合了這個插口的使用規則,否則將無法插入介面中,更無法使用。發現這個插口的出現讓我們使用更多的設備。

總結:介面在開發中的它好處

1、介面的出現擴展了功能。

2、介面其實就是暴漏出來的規則。

3、介面的出現降低了耦合性,即設備與設備之間實現瞭解耦。

 

介面的出現方便後期使用和維護,一方是在使用介面(如電腦),一方在實現介面(插在插口上的設備)。例如:筆記本使用這個規則(介面),電腦外圍設備實現這個規則(介面)。

1.9 介面和抽象的區別

 

明白了介面思想和介面的用法後,介面和抽象類的區別是什麼呢?介面在生活體現也基本掌握,那在程式中介面是如何體現的呢?

通過實例進行分析和代碼演示抽象類和介面的用法。

1、舉例:

犬:

  行為:

    吼叫;

    吃飯;

緝毒犬:

  行為:

    吼叫;

    吃飯;

    緝毒;

 

2、思考:

由於犬分為很多種類,他們吼叫和吃飯的方式不一樣,在描述的時候不能具體化,也就是吼叫和吃飯的行為不能明確。當描述行為時,行為的具體動作不能明確,這時,可以將這個行為寫為抽象行為,那麼這個類也就是抽象類。

可是當緝毒犬有其他額外功能時,而這個功能並不在這個事物的體系中。這時可以讓緝毒犬具備犬科自身特點的同時也有其他額外功能,可以將這個額外功能定義介面中。

         如下代碼演示:

interface 緝毒{
    public abstract void 緝毒();
}
//定義犬科的這個提醒的共性功能
abstract class 犬科{
public abstract void 吃飯();
public abstract void 吼叫();
}
// 緝毒犬屬於犬科一種,讓其繼承犬科,獲取的犬科的特性,
//由於緝毒犬具有緝毒功能,那麼它只要實現緝毒介面即可,這樣即保證緝毒犬具備犬科的特性,也擁有了緝毒的功能
class 緝毒犬 extends 犬科 implements 緝毒{

    public void 緝毒() {
    }
    void 吃飯() {
    }
    void 吼叫() {
    }
}
class 緝毒豬 implements 緝毒{
    public void 緝毒() {
    }
}

3、通過上面的例子總結介面和抽象類的區別:

相同點:

都位於繼承的頂端,用於被其他類實現或繼承;

都不能直接實例化對象;

都包含抽象方法,其子類都必須覆寫這些抽象方法;

區別:

抽象類為部分方法提供實現,避免子類重覆實現這些方法,提高代碼重用性;介面只能包含抽象方法;

一個類只能繼承一個直接父類(可能是抽象類),卻可以實現多個介面;(介面彌補了Java的單繼承)

抽象類是這個事物中應該具備的你內容, 繼承體系是一種 is..a關係

介面是這個事物中的額外內容,繼承體系是一種 like..a關係

 

二者的選用:

優先選用介面,儘量少用抽象類;

需要定義子類的行為,又要為子類提供共性功能時才選用抽象類;

2. 多態

2.1 多態概述

多態是繼封裝、繼承之後,面向對象的第三大特性。

現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。                                                                                                             

Java作為面向對象的語言,同樣可以描述一個事物的多種形態。如Student類繼承了Person類,一個Student的對象便既是Student,又是Person。

Java中多態的代碼體現在一個子類對象(實現類對象)既可以給這個子類(實現類對象)引用變數賦值,又可以給這個子類(實現類對象)的父類(介面)變數賦值。

如Student類可以為Person類的子類。那麼一個Student對象既可以賦值給一個Student類型的引用,也可以賦值給一個Person類型的引用。

最終多態體現為父類引用變數可以指向子類對象。

多態的前提是必須有子父類關係或者類實現介面關係,否則無法完成多態。

在使用多態後的父類引用變數調用方法時,會調用子類重寫後的方法。

2.2 多態的定義與使用格式

多態的定義格式:就是父類的引用變數指向子類對象

父類類型  變數名 = new 子類類型();
變數名.方法名();

普通類多態定義的格式

父類 變數名 = new 子類();
如:    class Fu {}
    class Zi extends Fu {}
    //類的多態使用
Fu f = new Zi();

抽象類多態定義的格式

抽象類 變數名 = new 抽象類子類();
如:    abstract class Fu {
         public abstract void method();
         }
class Zi extends Fu {
public void method(){
              System.out.println(“重寫父類抽象方法”);
}
}
//類的多態使用
Fu fu= new Zi();

介面多態定義的格式

介面 變數名 = new 介面實現類();
如: interface Fu {
             public abstract void method();
}
class Zi implements Fu {
             public void method(){
              System.out.println(“重寫介面抽象方法”);
}
}
//介面的多態使用
Fu fu = new Zi();

註意事項

同一個父類的方法會被不同的子類重寫。在調用方法時,調用的為各個子類重寫後的方法。

如 Person p1 = new Student();
   Person p2 = new Teacher();
   p1.work(); //p1會調用Student類中重寫的work方法
   p2.work(); //p2會調用Teacher類中重寫的work方法

當變數名指向不同的子類對象時,由於每個子類重寫父類方法的內容不同,所以會調用不同的方法。

2.3 多態-成員的特點

掌握了多態的基本使用後,那麼多態出現後類的成員有啥變化呢?前面學習繼承時,我們知道子父類之間成員變數有了自己的特定變化,那麼當多態出現後,成員變數在使用上有沒有變化呢?

多態出現後會導致子父類中的成員變數有微弱的變化。看如下代碼

class Fu {
    int num = 4;
}
class Zi extends Fu {
    int num = 5;
}
class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        System.out.println(f.num);
        Zi z = new Zi();
        System.out.println(z.num);
    }
}

多態成員變數

當子父類中出現同名的成員變數時,多態調用該變數時:

編譯時期:參考的是引用型變數所屬的類中是否有被調用的成員變數。沒有,編譯失敗。

運行時期:也是調用引用型變數所屬的類中的成員變數。

簡單記:編譯和運行都參考等號的左邊。編譯運行看左邊。

 

多態出現後會導致子父類中的成員方法有微弱的變化。看如下代碼

class Fu {
    int num = 4;
    void show()    {
        System.out.println("Fu show num");
    }
}
class Zi extends Fu {
    int num = 5;
    void show()    {
        System.out.println("Zi show num");
    }
}
class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        f.show();
    }
}

多態成員方法

編譯時期:參考引用變數所屬的類,如果沒有類中沒有調用的方法,編譯失敗。

運行時期:參考引用變數所指的對象所屬的類,並運行對象所屬類中的成員方法。

簡而言之:編譯看左邊,運行看右邊。

2.4 instanceof關鍵字

我們可以通過instanceof關鍵字來判斷某個對象是否屬於某種數據類型。如學生的對象屬於學生類,學生的對象也屬於人類。

使用格式:

boolean  b  = 對象  instanceof  數據類型;

Person p1 = new Student(); // 前提條件,學生類已經繼承了人類
boolean flag = p1 instanceof Student; //flag結果為true
boolean flag2 = p2 instanceof Teacher; //flag結果為false

2.5 多態-轉型

多態的轉型分為向上轉型與向下轉型兩種:

  向上轉型:當有子類對象賦值給一個父類引用時,便是向上轉型,多態本身就是向上轉型的過程。

  使用格式:

父類類型  變數名 = new 子類類型();
如:Person p = new Student();

  向下轉型:一個已經向上轉型的子類對象可以使用強制類型轉換的格式,將父類引用轉為子類引用,這個過程是向下轉型。如果是直接創建父類對象,是無法向下轉型的!

  使用格式:

子類類型 變數名 = (子類類型) 父類類型的變數;
如:Student stu = (Student) p;  //變數p 實際上指向Student對象

2.6 多態的好處與弊端

當父類的引用指向子類對象時,就發生了向上轉型,即把子類類型對象轉成了父類類型。向上轉型的好處是隱藏了子類類型,提高了代碼的擴展性。

但向上轉型也有弊端,只能使用父類共性的內容,而無法使用子類特有功能,功能有限制。看如下代碼

//描述動物類,並抽取共性eat方法
abstract class Animal {
    abstract void eat();
}
 
// 描述狗類,繼承動物類,重寫eat方法,增加lookHome方法
class Dog extends Animal {
    void eat() {
        System.out.println("啃骨頭");
    }

    void lookHome() {
        System.out.println("看家");
    }
}

// 描述貓類,繼承動物類,重寫eat方法,增加catchMouse方法
class Cat extends Animal {
    void eat() {
        System.out.println("吃魚");
    }

    void catchMouse() {
        System.out.println("抓老鼠");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal a = new Dog(); //多態形式,創建一個狗對象
        a.eat(); // 調用對象中的方法,會執行狗類中的eat方法
        // a.lookHome();//使用Dog類特有的方法,需要向下轉型,不能直接使用
        
        // 為了使用狗類的lookHome方法,需要向下轉型
// 向下轉型過程中,可能會發生類型轉換的錯誤,即ClassCastException異常
        // 那麼,在轉之前需要做健壯性判斷 
        if( !a instanceof Dog){ // 判斷當前對象是否是Dog類型
                 System.out.println("類型不匹配,不能轉換"); 
                 return; 
        } 
        Dog d = (Dog) a; //向下轉型
        d.lookHome();//調用狗類的lookHome方法
    }
}

我們來總結一下:

  什麼時候使用向上轉型:

  當不需要面對子類類型時,通過提高擴展性,或者使用父類的功能就能完成相應的操作,這時就可以使用向上轉型。

如:Animal a = new Dog();
    a.eat();

  什麼時候使用向下轉型

  當要使用子類特有功能時,就需要使用向下轉型。

如:Dog d = (Dog) a; //向下轉型
        d.lookHome();//調用狗類的lookHome方法

  向下轉型的好處:可以使用子類特有功能。

  弊端是:需要面對具體的子類對象;在向下轉型時容易發生ClassCastException類型轉換異常。在轉換之前必須做類型判斷。

如:if( !a instanceof Dog){…}

2.7 多態-舉例

我們明確多態使用,以及多態的細節問題後,接下來練習下多態的應用。

  畢老師和畢姥爺的故事

/*
描述畢老師和畢姥爺,
畢老師擁有講課和看電影功能
畢姥爺擁有講課和釣魚功能
*/
class 畢姥爺 {
    void 講課() {
        System.out.println("政治");
    }

    void 釣魚() {
        System.out.println("釣魚");
    }
}

// 畢老師繼承了畢姥爺,就有擁有了畢姥爺的講課和釣魚的功能,
// 但畢老師和畢姥爺的講課內容不一樣,因此畢老師要覆蓋畢姥爺的講課功能
class 畢老師 extends 畢姥爺 {
    void 講課() {
        System.out.println("Java");
    }

    void 看電影() {
        System.out.println("看電影");
    }
}

public class Test {
    public static void main(String[] args) {
        // 多態形式
        畢姥爺 a = new 畢老師(); // 向上轉型
        a.講課(); // 這裡表象是畢姥爺,其實真正講課的仍然是畢老師,因此調用的也是畢老師的講課功能
        a.釣魚(); // 這裡表象是畢姥爺,但對象其實是畢老師,而畢老師繼承了畢姥爺,即畢老師也具有釣魚功能

        // 當要調用畢老師特有的看電影功能時,就必須進行類型轉換
        畢老師 b = (畢老師) a; // 向下轉型
        b.看電影();
    }
}

學習到這裡,面向對象的三大特征學習完了。

總結下封裝、繼承、多態的作用:

封裝:把對象的屬性與方法的實現細節隱藏,僅對外提供一些公共的訪問方式

繼承:子類會自動擁有父類所有可繼承的屬性和方法。

多態:配合繼承與方法重寫提高了代碼的復用性與擴展性;如果沒有方法重寫,則多態同樣沒有意義。


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

-Advertisement-
Play Games
更多相關文章
  • 中介者模式: 1、定義:用一個中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用, 從而使其耦合鬆散,而且可以獨立地改變它們之間的交互 2、模型結構: (1)抽象中介者(Mediator):它是中介者的介面,提供了同事對象註冊與轉發同事對象信息的抽象方法 (2)具體中介者(Concr ...
  • 1.代碼生成器: [正反雙向](單表、主表、明細表、樹形表,快速開發利器)freemaker模版技術 ,0個代碼不用寫,生成完整的一個模塊,帶頁面、建表sql腳本、處理類、service等完整模塊2.多數據源:(支持同時連接無數個資料庫,可以不同的模塊連接不同數的據庫)支持N個數據源3.阿裡資料庫連 ...
  • 1. final關鍵字 1.1 final的概念 繼承的出現提高了代碼的復用性,並方便開發。但隨之也有問題,有些類在描述完之後,不想被繼承,或者有些類中的部分方法功能是固定的,不想讓子類重寫。可是當子類繼承了這些特殊類之後,就可以對其中的方法進行重寫,那怎麼解決呢? 要解決上述的這些問題,需要使用到 ...
  • GoLang 開山篇 1、Golang 的學習方向 Go語言,我們可以簡單的寫成Golang. 2、GoLang 的應用領域 2.1 區塊鏈的應用開發 2.2 後臺的服務應用 2.3 雲計算/雲服務後臺應用 3、學習方法的介紹 建立一個整體框架、然後細節 在實際工作中,要培養用到什麼,能夠快速學習什 ...
  • 一、遞歸 1、遞歸的定義 在一個函數內部調用函數自身。 2、遞歸的最大深度--996 修改最大深度 我們會發現,將遞歸的最大深度改成100000後,並沒有達到100000,這是由我們的電腦性能決定的。 3、遞歸的特點 (1)調用自身 (2)必須要有結束條件 4、實例 菲波那切數列:0 1 1 2 ...
  • python day4 元組/字典/集合類知識點補充 (學習資源來自老男孩教育) 2019/10/7 [TOC] 1. 元組tuple知識點補充 創建和轉換 ,比如 元組的特性 元組的特性:元組的元素不可修改,但是元素的元素可以被修改。即元組的兒子不能變,但是元組的孫子可以變。 2. 字典dict的 ...
  • 1. 構造方法 我們對封裝已經有了基本的瞭解,接下來我們來看一個新的問題,依然以Person為例,由於Person中的屬性都被private了,外界無法直接訪問屬性,必須對外提供相應的set和get方法。當創建人對象的時候,人對象一創建就要明確其姓名和年齡,那該怎麼做呢? 1.1 構造方法介紹 在開 ...
  • requests,發送http請求(用python模擬瀏覽器瀏覽網頁)requests.get("http://www.baidu.com") 示例: 1 import requests 2 response = requests.get("http://www.weather.com.cn/ada ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...