天天看點

oop面向對象【接口、多态】

 今日内容

1.接口

2.三大特征——多态

3.引用類型轉換

教學目标

1.寫出定義接口的格式

2.寫出實作接口的格式

3.說出接口中成員的特點

4.能夠說出使用多态的前提條件

5.了解多态的向上轉型

6.了解多态的向下轉型

7.能夠完成筆記本電腦案例(方法參數為接口)

第一章 接口

1.1  概述

接口,是Java語言中一種引用類型,是方法的集合,如果說類的内部封裝了成員變量、構造方法和成員方法,那麼

接口的内部主要就是封裝了方法,包含抽象方法(JDK 7及以前),預設方法和靜态方法(JDK 8),私有方法(JDK 9)。

接口的定義,它與定義類方式相似,但是使用  interface  關鍵字。它也會被編譯成.class檔案,但一定要明确它并

不是類,而是另外一種引用資料類型。

引用資料類型:數組,類,接口。

接口的使用,它不能建立對象,但是可以被實作( implements  ,類似于被繼承)。一個實作接口的類(可以看做

是接口的子類),需要實作接口中所有的抽象方法,建立該類對象,就可以調用方法了,否則它必須是一個抽象

類。

1.2  定義格式

public interface 接口名稱 {
    //  抽象方法
    //  預設方法
    //  靜态方法
    //  私有方法
}      

含有抽象方法

抽象方法:使用 abstract  關鍵字修飾,可以省略,沒有方法體。該方法供子類實作使用。

代碼如下:

public interface InterFaceName {
    public abstract void method();
}      

含有預設方法和靜态方法

預設方法:使用  default  修飾,不可省略,供子類調用或者子類重寫。

靜态方法:使用  static  修飾,供接口直接調用。

public interface InterFaceName {
    public
    default void method() {
        //  執行語句
    }
    public static void method2() {
        //  執行語句
    }
}      

含有私有方法和私有靜态方法

私有方法:使用  private  修飾,供接口中的預設方法或者靜态方法調用。

public interface InterFaceName {
    private void method() {
        //  執行語句
    }
}      

1.3  基本的實作

實作的概述

類與接口的關系為實作關系,即類實作接口,該類可以稱為接口的實作類,也可以稱為接口的子類。實作的動作類

似繼承,格式相仿,隻是關鍵字不同,實作使用  implements   關鍵字。

非抽象子類實作接口:

1. 必須重寫接口中所有抽象方法。

2. 繼承了接口的預設方法,即可以直接調用,也可以重寫。

實作格式:

class 類名 implements 接口名 {
    //  重寫接口中抽象方法【必須】
    //  重寫接口中預設方法【可選】
}      

抽象方法的使用

必須全部實作,代碼如下:

定義接口:

public interface LiveAble {
    //  定義抽象方法
    public abstract void eat();
    public abstract void sleep();
}      

定義實作類:

public class Animal implements LiveAble {
    @Override
    public void eat() {
        System.out.println("吃東西");
    }
      
    @Override
    public void sleep() {
        System.out.println("晚上睡");
    }
}      

定義測試類:

public class InterfaceDemo {
    public static void main(String[] args) {
        //  建立子類對象
        Animal a = new Animal();
        //  調用實作後的方法
        a.eat();
        a.sleep();
    }
}

輸出結果:
吃東西
晚上睡      

預設方法的使用

可以繼承,可以重寫,二選一,但是隻能通過實作類的對象來調用。

1. 繼承預設方法,代碼如下:

public interface LiveAble {
    public
    default void fly() {
        System.out.println("天上飛");
    }
}      
public class Animal implements LiveAble {
    //  繼承,什麼都不用寫,直接調用
}      
public class InterfaceDemo {
    public static void main(String[] args) {
        //  建立子類對象
        Animal a = new Animal();
        //  調用預設方法
        a.fly();
    }
}

輸出結果:
天上飛      

2. 重寫預設方法,代碼如下:

public interface LiveAble {
    public
    default void fly() {
        System.out.println("天上飛");
    }
}      
public class Animal implements LiveAble {
    @Override
    public void fly() {
        System.out.println("自由自在的飛");
    }
}      
public class InterfaceDemo {
    public static void main(String[] args) {
        //  建立子類對象
        Animal a = new Animal();
        //  調用重寫方法
        a.fly();
    }
}

輸出結果:
自由自在的飛      

靜态方法的使用

靜态與.class 檔案相關,隻能使用接口名調用,不可以通過實作類的類名或者實作類的對象調用,代碼如下:

public interface LiveAble {
    public static void run() {
        System.out.println("跑起來~~~");
    }
}      
public class Animal implements LiveAble {
    //  無法重寫靜态方法
}      
public class InterfaceDemo {
    public static void main(String[] args) {
        //  Animal.run();  // 【錯誤】無法繼承方法,也無法調用
        LiveAble.run(); //
    }
}
輸出結果:
跑起來~~~      

私有方法的使用

私有方法:隻有預設方法可以調用。

私有靜态方法:預設方法和靜态方法可以調用。

如果一個接口中有多個預設方法,并且方法中有重複的内容,那麼可以抽取出來,封裝到私有方法中,供預設方法

去調用。從設計的角度講,私有的方法是對預設方法和靜态方法的輔助。同學們在已學技術的基礎上,可以自行測試。

public interface LiveAble {
    default void func() {
        func1();
        func2();
    }
    private void func1() {
        System.out.println("跑起來~~~");
    }
    private void func2() {
        System.out.println("跑起來~~~");
    }
}      

1.4  接口的多實作

之前學過,在繼承體系中,一個類隻能繼承一個父類。而對于接口而言,一個類是可以實作多個接口的,這叫做接

口的多實作。并且,一個類能繼承一個父類,同時實作多個接口。

class 類名[extends 父類名] implements 接口名1, 接口名2, 接口名3... {
    //  重寫接口中抽象方法【必須】
    //  重寫接口中預設方法【不重名時可選】
}      

[ ]: 表示可選操作。

抽象方法

接口中,有多個抽象方法時,實作類必須重寫所有抽象方法。如果抽象方法有重名的,隻需要重寫一次。代碼如

下:

定義多個接口:

interface A {
    public abstract void showA();
    public abstract void show();
}
interface B {
    public abstract void showB();
    public abstract void show();
}      
public class C implements A, B {
    @Override
    public void showA() {
        System.out.println("showA");
    }
    @Override
    public void showB() {
        System.out.println("showB");
    }
    @Override
    public void show() {
        System.out.println("show");
    }
}      

預設方法

接口中,有多個預設方法時,實作類都可繼承使用。如果預設方法有重名的,必須重寫一次。代碼如下:

interface A {
    public
    default void methodA() {}
    public
    default void method() {}
}
interface B {
    public
    default void methodB() {}
    public
    default void method() {}
}      
public class C implements A, B {
    @Override
    public void method() {
        System.out.println("method");
    }
}      

靜态方法

接口中,存在同名的靜态方法并不會沖突,原因是隻能通過各自接口名通路靜态方法。

優先級的問題

當一個類,既繼承一個父類,又實作若幹個接口時,父類中的成員方法與接口中的預設方法重名,子類就近選擇執

行父類的成員方法。代碼如下:

interface A {
    public
    default void methodA() {
        System.out.println("AAAAAAAAAAAA");
    }
}      

定義父類:

class D {
    public void methodA() {
        System.out.println("DDDDDDDDDDDD");
    }
}      

定義子類:

class C extends D implements A {
    //  未重寫methodA方法
}      
public class Test {
    public static void main(String[] args) {
        C c = new C();
        c.methodA();
    }
}

輸出結果: DDDDDDDDDDDD      

1.5  接口的多繼承【了解】

一個接口能繼承另一個或者多個接口,這和類之間的繼承比較相似。接口的繼承使用  extends  關鍵字,子接口繼

承父接口的方法。如果父接口中的預設方法有重名的,那麼子接口需要重寫一次。代碼如下:

定義父接口:

interface A {
    public
    default void method() {
        System.out.println("AAAAAAAAAAAAAAAAAAA");
    }
}
interface B {
    public
    default void method() {
        System.out.println("BBBBBBBBBBBBBBBBBBB");
    }
}      

定義子接口:

interface D extends A, B {
    @Override
    public default void method() {
        System.out.println("DDDDDDDDDDDDDD");
    }
}      

小貼士:

子接口重寫預設方法時,default關鍵字可以保留。

子類重寫預設方法時,default關鍵字不可以保留。

1.6  其他成員特點

接口中,無法定義成員變量,但是可以定義常量,其值不可以改變,預設使用public static final修飾。

接口中,沒有構造方法,不能建立對象。

接口中,沒有靜态代碼塊。

第二章 多态

2.1  概述

引入

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

生活中,比如跑的動作,小貓、小狗和大象,跑起來是不一樣的。再比如飛的動作,昆蟲、鳥類和飛機,飛起來也

是不一樣的。可見,同一行為,通過不同的事物,可以展現出來的不同的形态。多态,描述的就是這樣的狀态。

定義

多态: 是指同一行為,具有多個不同表現形式。

前提【重點】

1. 繼承或者實作【二選一】

2. 方法的重寫【意義展現:不重寫,無意義】

3. 父類引用指向子類對象【格式展現】

2.2  多态的展現

多态展現的格式:

父類類型 變量名 = new 子類對象;
變量名.方法名();      

父類類型:指子類對象繼承的父類類型,或者實作的父接口類型。

Fu f = new Zi();
f.method();      

當使用多态方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫

後方法。

public abstract class Animal {
    public abstract void eat();
}      
class Cat extends Animal {
    public void eat() {
        System.out.println("吃魚");
    }
}
class Dog extends Animal {
    public void eat() {
        System.out.println("吃骨頭");
    }
}      
public class Test {
    public static void main(String[] args) {
        //  多态形式,建立對象
        Animal a1 = new Cat();
        //  調用的是  Cat 的  eat
        a1.eat();
        //  多态形式,建立對象
        Animal a2 = new Dog();
        //  調用的是  Dog 的  eat
        a2.eat();
    }
}      

2.3  多态的好處

實際開發的過程中,父類類型作為方法形式參數,傳遞子類對象給方法,進行方法的調用,更能展現出多态的擴充

性與便利。代碼如下:

public abstract class Animal {
    public abstract void eat();
}      
class Cat extends Animal {
    public void eat() {
        System.out.println("吃魚");
    }
}
class Dog extends Animal {
    public void eat() {
        System.out.println("吃骨頭");
    }
}      
public class Test {
    public static void main(String[] args) {
        //  多态形式,建立對象
        Cat c = new Cat();
        Dog d = new Dog();
       
         //  調用showCatEat
        showCatEat(c);
        
        //  調用showDogEat
        showDogEat(d);
        
        /*
        以上兩個方法,  均可以被showAnimalEat(Animal  a)方法所替代
        而執行效果一緻
        */
        showAnimalEat(c);
        showAnimalEat(d);
    }
    public static void showCatEat(Cat c) {
        c.eat();
    }
    public static void showDogEat(Dog d) {
        d.eat();
    }
    public static void showAnimalEat(Animal a) {
        a.eat();
    }
}              

由于多态特性的支援,showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,當

然可以把Cat對象和Dog對象,傳遞給方法。

當eat方法執行時,多态規定,執行的是子類重寫的方法,那麼效果自然與showCatEat、showDogEat方法一緻,

是以showAnimalEat完全可以替代以上兩方法。

不僅僅是替代,在擴充性方面,無論之後再多的子類出現,我們都不需要編寫showXxxEat方法了,直接使用

showAnimalEat都可以完成。

是以,多态的好處,展現在,可以使程式編寫的更簡單,并有良好的擴充。

2.4  引用類型轉換

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

向上轉型

向上轉型:多态本身是子類類型向父類類型向上轉換的過程,這個過程是預設的。

當父類引用指向一個子類對象時,便是向上轉型。

使用格式:

父類類型

變量名 = new 子類類型();
如: Animal a = new Cat();      

向下轉型

向下轉型:父類類型向子類類型向下轉換的過程,這個過程是強制的。

一個已經向上轉型的子類對象,将父類引用轉為子類引用,可以使用強制類型轉換的格式,便是向下轉型。

子類類型 變量名 = (子類類型) 父類變量名;
如: Cat c = (Cat) a;      

為什麼要轉型

當使用多态方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁

有,而父類沒有的方法。編譯都錯誤,更别說運作了。這也是多态給我們帶來的一點"小麻煩"。是以,想要調用子

類特有的方法,必須做向下轉型。

轉型示範,代碼如下:

定義類:

abstract class Animal {
    abstract void eat();
}
class Cat extends Animal {
    public void eat() {
        System.out.println("吃魚");
    }
    public void catchMouse() {
        System.out.println("抓老鼠");
    }
}
class Dog extends Animal {
    public void eat() {
        System.out.println("吃骨頭");
    }
    public void watchHouse() {
        System.out.println("看家");
    }
}      
public class Test {
    public static void main(String[] args) {
        //  向上轉型
        Animal a = new Cat();
        a.eat();
        //  調用的是  Cat 的  eat
        //  向下轉型
        Cat c = (Cat) a;
        c.catchMouse();
        //  調用的是  Cat 的  catchMouse
    }
}      

轉型的異常

轉型的過程中,一不小心就會遇到這樣的問題,請看如下代碼:

public class Test {
    public static void main(String[] args) {
        //  向上轉型
        Animal a = new Cat();
        a.eat();
        //  調用的是  Cat 的  eat
        //  向下轉型
        Dog d = (Dog) a;
        d.watchHouse();
        //  調用的是  Dog 的  watchHouse  【運作報錯】
    }
}      

這段代碼可以通過編譯,但是運作時,卻報出了  ClassCastException  ,類型轉換異常!這是因為,明明建立了

Cat類型對象,運作時,當然不能轉換成Dog對象的。這兩個類型并沒有任何繼承關系,不符合類型轉換的定義。

為了避免ClassCastException的發生,Java提供了  instanceof  關鍵字,給引用變量做類型的校驗,格式如下:

變量名  instanceof  資料類型
如果變量屬于該資料類型,傳回true。
如果變量不屬于該資料類型,傳回false。      

是以,轉換前,我們最好先做一個判斷,代碼如下:

public class Test {
    public static void main(String[] args) {
        //  向上轉型
        Animal a = new Cat();
        a.eat();
        //  調用的是  Cat 的  eat
        //  向下轉型
        if(a instanceof Cat) {
            Cat c = (Cat) a;
            c.catchMouse();
            //  調用的是  Cat 的  catchMouse
        } else if(a instanceof Dog) {
            Dog d = (Dog) a;
            d.watchHouse();
            //  調用的是  Dog 的  watchHouse
        }
    }
}      

第三章 接口多态的綜合案例

3.1  筆記本電腦

筆記本電腦(laptop)通常具備使用USB裝置的功能。在生産時,筆記本都預留了可以插入USB裝置的USB接口,

但具體是什麼USB裝置,筆記本廠商并不關心,隻要符合USB規格的裝置都可以。

定義USB接口,具備最基本的開啟功能和關閉功能。滑鼠和鍵盤要想能在電腦上使用,那麼滑鼠和鍵盤也必須遵守

USB規範,實作USB接口,否則滑鼠和鍵盤的生産出來也無法使用。

3.2  案例分析

進行描述筆記本類,實作筆記本使用USB滑鼠、USB鍵盤

USB接口,包含開啟功能、關閉功能

筆記本類,包含運作功能、關機功能、使用USB裝置功能

滑鼠類,要實作USB接口,并具備點選的方法

鍵盤類,要實作USB接口,具備敲擊的方法

3.3  案例實作

定義USB接口:

interface USB {
    void open(); //  開啟功能
    void close(); //  關閉功能
}      

定義滑鼠類:

class Mouse implements USB {
    public void open() {
        System.out.println("滑鼠開啟,紅燈閃一閃");
    }
    public void close() {
        System.out.println("滑鼠關閉,紅燈熄滅");
    }
    public void click() {
        System.out.println("滑鼠單擊");
    }
}      

定義鍵盤類:

class KeyBoard implements USB {
    public void open() {
        System.out.println("鍵盤開啟,綠燈閃一閃");
    }
    public void close() {
        System.out.println("鍵盤關閉,綠燈熄滅");
    }
    public void type() {
        System.out.println("鍵盤打字");
    }
}      

定義筆記本類:

class Laptop {
    //  筆記本開啟運作功能
    public void run() {
            System.out.println("筆記本運作");
    }
    //  筆記本使用usb裝置,這時當筆記本對象調用這個功能時,必須給其傳遞一個符合USB規則的USB裝置
    public void useUSB(USB usb) {
        //  判斷是否有USB裝置
        if(usb != null) {
            usb.open();
            //  類型轉換,調用特有方法
            if(usb instanceof Mouse) {
                Mouse m = (Mouse) usb;
                m.click();
            } else if(usb instanceof KeyBoard) {
                KeyBoard kb = (KeyBoard) usb;
                kb.type();
            }
            usb.close();
        }
    }
    public void shutDown() {
        System.out.println("筆記本關閉");
    }
}      

測試類,代碼如下:

public class Test {
    public static void main(String[] args) {
        //  建立筆記本實體對象
        Laptop lt = new Laptop();
        //  筆記本開啟
        lt.run();
        //  建立滑鼠實體對象
        Usb u = new Mouse();
        //  筆記本使用滑鼠
        lt.useUSB(u);
        //  建立鍵盤實體對象
        KeyBoard kb = new KeyBoard();
        //  筆記本使用鍵盤
        lt.useUSB(kb);
        //  筆記本關閉
        lt.shutDown();
    }
}      

ending...