java學習day10——接口、多態

接口

接口的定義基本格式

接口就是多個類的公共規範
接口是一種引用數據類型,最重要的內容就是其中的抽象方法
如何定義一個接口的格式:

public interface 接口名稱{
//接口內容
}

備註:換成了關鍵字interface之後,編譯生成的字節碼文件仍然是:.java --> .class
如果是java7,那麼接口中包含的內容有:

  1. 常量
  2. 抽象方法

如果是java8,還可以額外包含有:

  1. 默認方法
  2. 靜態方法

如果是java9,還可以額外包含有:

  1. 私有方法

接口的抽象方法定義

在任何版本的java中,接口都能定義抽象方法
接口和抽象類的區別
注意事項:

  1. 接口當中的抽象方法,修飾符必須是兩個固定的關鍵字:public abstract
  2. 這兩個關鍵字修飾符,可以選擇性地省略
  3. 方法的三要素,可以隨意定義
public interface MyInterfaceAbstract {
    // 這是一個抽象方法
    public abstract void methodAbs1();
    // 這也是抽象方法
    abstract void methodAbs2();
    // 這也是抽象方法
    public void methodAbs3();
    // 這也是抽象方法
    void methodAbs4();
}

接口的抽象方法使用

接口使用步驟:

  1. 接口不能直接使用,必須有一個“實現類”來“實現”該接口。
    格式:
    public class 實現類名稱 implements 接口名稱 {
    // …
    }
  2. 接口的實現類必須覆蓋重寫(實現)接口中所有的抽象方法
    去掉abstract關鍵字,加上方法體大括號
  3. 創建實現類的對象,進行使用
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {
    @Override
    public void methodAbs1() {
        System.out.println("這是第一個方法");
    }

    @Override
    public void methodAbs2() {
        System.out.println("這是第二個方法");
    }

    @Override
    public void methodAbs3() {
        System.out.println("這是第三個方法");
    }

    @Override
    public void methodAbs4() {
        System.out.println("這是第四個方法");
    }
}

注意事項:
如果實現類並沒有覆蓋重寫接口中所有抽象方法,那麼這個實現類自己就必須是抽象類

public class Demo01Interface {
    public static void main(String[] args) {
        //創建實現類的對象使用
        MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
        impl.methodAbs1();
        impl.methodAbs2();
    }
}

接口的默認方法使用

從java8開始,接口裏允許定義默認方法。
格式:
public default 返回值類型 方法名稱(參數列表){
方法體
}
備註:接口當中的默認方法可以解決接口升級的問題

public interface MyInterfaceDefault {
    // 抽象方法
    public abstract void methodAbs();
    // 新添加了一個抽象方法,會對實現類造成影響
//    public abstract void methodAbs2();
    // 新添加的方法,改成默認方法
    public default void methodDefault(){
        System.out.println("這是新添加的默認方法");
    }
}

兩個實現類

public class MyInterfaceDefaultA implements MyInterfaceDefault {
    @Override
    public void methodAbs() {
        System.out.println("實現了抽象方法,AAA");
    }
}
public class MyInterfaceDefaultB implements MyInterfaceDefault {
    @Override
    public void methodAbs() {
        System.out.println("實現了抽象方法,BBB");
    }

    @Override
    public void methodDefault() {
        System.out.println("實現類覆蓋重寫了接口的default方法");
    }
}
  1. 接口的默認方法,可以通過接口的實現類對象直接調用
  2. 接口的默認方法,也可以被接口實現類進行覆蓋重寫
public class Demo02Interface {
    public static void main(String[] args) {
        MyInterfaceDefaultA a = new MyInterfaceDefaultA();
        a.methodAbs(); // 調用抽象方法,實際運行的是右側實現類
        //調用默認方法,如果實現類中沒有,會向上找接口
        a.methodDefault();

        MyInterfaceDefaultB b = new MyInterfaceDefaultB();
        b.methodAbs();
        b.methodDefault();//實現類B覆蓋重寫了接口的默認方法
    }
}

結果在這裏插入圖片描述

接口的靜態方法定義

從java8開始,接口當中允許定義靜態方法
格式:

public static 返回值類型 方法名稱(參數列表){
方法體
}

提示:就是將abstract或者default換成static即可,帶上方法體

public interface MyInterfaceStatic {
    public static void methodStatic(){
        System.out.println("這是接口的靜態方法");
    }
}

實現類

public class MyInterfaceStaticImpl implements MyInterfaceStatic {
    // 沒有抽象方法,不報錯
}

靜態方法的調用

public class Demo03Interface {
    public static void main(String[] args) {
        MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
        //錯誤寫法!
//        impl.methodStatic();
        // 直接通過接口名稱調用靜態方法
        MyInterfaceStatic.methodStatic();
    }
}

注意:不能通過接口實現類的對象來調用接口當中的靜態方法,靜態跟對象沒關係
正確用法:通過接口名稱,直接調用其中的靜態方法
格式:
接口名稱.靜態方法名(參數);

接口的私有方法定義

問題描述:
我們需要抽取一個共有方法,用來解決兩個默認方法之間重複代碼的問題
但是這個共有方法不應該讓實現類使用,應該是私有化的

解決方案:
從java9開始,接口當中允許定義私有方法

  1. 普通私有方法:解決多個默認方法之間重複代碼問題
    private 返回值類型 方法名稱(參數列表){
    方法體
    }
  2. 靜態私有方法:解決多個靜態方法之間重複代碼問題
    private static 返回值類型 方法名稱(參數列表){
    方法體
    }

普通私有方法

public interface MyInterfacePrivateA {
    public default void methodDefault1(){
        System.out.println("默認方法1");
        methodCommon();
    }
    public default void methodDefault2(){
        System.out.println("默認方法2");
        methodCommon();
    }
    private void methodCommon(){
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}

靜態私有方法

public interface MyInterfacePrivateB {
    public static void methodStatic1(){
        System.out.println("默認方法1");
        methodStaticCommon();
    }
    public static void methodStatic2(){
        System.out.println("默認方法2");
        methodStaticCommon();
    }
    private static void methodStaticCommon(){
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}

接口的常量定義和使用

接口當中也可以定義“成員變量”,但是必須使用public static final三個關鍵字進行修飾
從效果上看,這其實就是接口的【常量】
格式:
public static final 數據類型 常量名稱 = 數據值;
備註:
一旦使用final關鍵字進行修飾,說明不可改變
注意事項:

  1. 接口當中的常量,可以省略public static final,注意:不寫也照樣是這樣
  2. 接口當中的常量,必須進行賦值,不能不賦值
  3. 接口中常量的名稱,使用完全大寫的字母,用下劃線進行分割
    定義
public interface MyInterfaceConst {
    // 這其實就是一個常量,一旦賦值,不可以修改
    public static final int NUM_OF_MY_CLASS = 10;
}

訪問

public class Demo05Interface {
    public static void main(String[] args) {
        // 訪問接口中的常量
        System.out.println(MyInterfaceConst.NUM_OF_MY_CLASS);
    }
}

接口的內容小結

  1. 成員變量其實是常量,【格式】:
    public static final 數據類型 常量名稱 = 數據值;
    【注意】:常量必須進行賦值,而且一旦賦值不能改變
    常量名稱完全大寫,用下劃線進行分隔
  2. 接口中最重要的就是抽象方法,【格式】:
    public abstract 返回值類型 方法名稱(參數列表);
    【注意】:實現類必須覆蓋重寫接口的所有抽象方法,除非實現類是抽象類
  3. 從java8開始,接口裏允許定義默認方法,【格式】:
    public default 返回值類型 方法名稱(參數列表){方法體}
    【注意】:默認方法也可以被覆蓋重寫
  4. 從java8開始,接口裏允許定義靜態方法,【格式】:
    public static 返回值類型 方法名稱(參數列表){方法體}
    【注意】:應該通過接口名稱進行調用,不能通過實現類對象調用接口靜態方法
  5. 從java9開始,接口裏允許定義私有類方法,【格式】:
    普通私有方法:private 返回值類型 方法名稱(參數列表){方法體}
    靜態私有方法:private static 返回值類型 方法名稱(參數列表){方法體}
    【注意】:private的方法只有接口自己才能調用,不能被實現類或別人使用。

繼承父類並實現多個接口

使用接口時,需注意:

  1. 接口是沒有靜態代碼塊或構造方法的
  2. 一個類的直接父類是唯一的,但是一個類可以同時實現多個接口
    格式:
    public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
    //覆蓋重寫所有抽象方法
    }
  3. 如果實現類所實現的多個接口當中存在重複的抽象方法,那麼只需要覆蓋重寫一次即可
  4. 如果實現類沒有覆蓋重寫所有接口當中的所有抽象方法,那麼實現類就必須是一個抽象類
  5. 如果實現類所實現的多個接口當中,存在重複的默認方法,那麼實現類一定要對沖突的默認方法進行覆蓋重寫
  6. 一個類如果直接父類當中的方法和接口當中的默認方法產生了衝突,優先用父類當中的方法

接口之間的多繼承

  1. 類與類之間是單繼承的,直接父類只有一個
  2. 類與接口之間是多實現的,一個類可以實現多個接口
  3. 接口與接口之間是多繼承的

多態

extends繼承或者implements實現,是多態性的前提
代碼當中體現多態性,其實就是一句話,父類引用指向子類對象
格式:
父類名稱 對象名 = new 子類名稱();
或者:
接口名稱 對象名 = new 實現類名稱();

多態中成員方法的使用特點

在多態的代碼中,成員方法的訪問規則是:
看new的是誰,就優先用誰,沒有則向上找

public class Fu {
    int num = 10;
    public void method(){
        System.out.println("父類方法");
    }
    public void methodFu(){
        System.out.println("父類特有方法");
    }
}
public class Zi extends Fu {
    int num = 20;
    @Override
    public void method() {
        System.out.println("子類方法");
    }
    public void methodZi(){
        System.out.println("子類特有方法");
    }
}
public class Demo01Multi {
    public static void main(String[] args) {
        // 使用多態的寫法
        //左側父類的引用,指向了右側子類的對象
        Fu obj = new Zi();
        obj.method(); //父子都有,優先用子
        obj.methodFu(); //子類沒有,父類有,向上找到父類
    }
}

【注意】編譯看左邊,運行看右邊
在這裏插入圖片描述

多態的好處

在這裏插入圖片描述

對象的向上轉型

其實就是多態寫法:

父類名稱 對象名 = new 子類名稱();

eg: Animal animial = new Cat();
含義:右側創建一個子類對象,把它當作父類來看待使用
注意事項:向上轉型一定是安全的,從小範圍轉向了大範圍

對象的向下轉型

向上轉型有一個弊端:對象一旦向上轉型爲父類,那麼就無法調用子類原本特有的內容

解決方案:用對象的向下轉型【還原】
格式:

子類名稱 對象名 = (子類名稱)父類對象;

含義:將父類對象,【還原】成爲本來的子類對象
類似於: 基本數據類型的強制數據轉換
父類

public abstract class Animal {
    public abstract void eat();
}

貓——子類

public class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("貓喫魚");
    }
    public void catchMouse(){
        System.out.println("貓抓老鼠");
    }
}

狗——子類

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗喫屎");
    }
    public void watchHouse(){
        System.out.println("狗看家");
    }
}

main方法

public class down {
    public static void main(String[] args) {
        Animal animal = new Cat();//本來是貓,向上轉型爲動物
        animal.eat();//貓喫魚
        /*
        注意事項:
        1. 必須保證對象本來創建的時候,就是貓,才能夠向下轉型爲貓。
        2. 如果對象創建的時候本來不是貓,現在非要向下轉型爲貓,就會報錯(ClassCastException)。
         */
        // 向下轉型,進行【還原】動作
        Cat cat = (Cat) animal;//本來是貓,意見被當作動物了,還原回來成爲本來的貓
        cat.catchMouse();//貓抓老鼠

        //本來是貓,現在非要當成狗c
        Dog dog = (Dog) animal;//錯誤寫法!編譯不會報錯,但是會出現異常 java.lang.ClassCastException
    }
}

instanceof關鍵字

知道一個父類引用的對象本來是什麼子類
格式:

對象 instanceof 類名稱

這將會得到一個boolean值結果,也就是判斷前面的對象能不能當作後面類型的實例

public class Demo02Instanceof {
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.eat();

        if(animal instanceof Dog){
            Dog dog =(Dog) animal;
            dog.watchHouse();
        }
        if(animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
    // instanceof 的作用,不知道傳入參數的類型時使用
    public static void giveMeAPat(Animal animal){
        if(animal instanceof Dog){
            Dog dog =(Dog) animal;
            dog.watchHouse();
        }
        if(animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
}

案例分析

在這裏插入圖片描述
USB接口

public abstract interface USB {
    public abstract void open();//打開設備
    public abstract void close();//關閉設備
}

筆記本類

public class Computer {
    public void powerOn() {
        System.out.println("筆記本電腦開機");
    }

    public void powerOff() {
        System.out.println("筆記本電腦關機");
    }

    // 使用USB設備的方法
    public void useDevice(USB usb) {
        usb.open();
        usb.close();
        if (usb instanceof Keyboard) {
            Keyboard keyboard = (Keyboard) usb;
            keyboard.type();
        } else if (usb instanceof Mouse) {
            Mouse mouse = (Mouse) usb;
            mouse.click();
        }
    }
}

鼠標類

public class Mouse implements USB {
    @Override
    public void open() {
        System.out.println("打開鼠標");
    }

    @Override
    public void close() {
        System.out.println("關閉鼠標");
    }
    public void click(){
        System.out.println("點擊鼠標");
    }
}

鍵盤類

public class Keyboard implements USB {
    @Override
    public void open() {
        System.out.println("打開鍵盤");
    }

    @Override
    public void close() {
        System.out.println("關閉鍵盤");
    }
    public void type(){
        System.out.println("鍵盤輸入");
    }
}

Main方法

public class DemoMain {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();
        //準備一個鼠標,供電腦使用
        //首先進行向上轉型
        USB usbMouse = new Mouse();
        computer.useDevice(usbMouse);

        //創建一個USB鍵盤
        Keyboard keyboard = new Keyboard();
        //方法參數時USB類型,傳遞進去的是實現類對象
        computer.useDevice(keyboard);//正確寫法!也發生了向上轉型
        computer.powerOff();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章