1. 程式人生 > 實用技巧 >Java學習之介面與多型篇

Java學習之介面與多型篇

Java學習之介面與多型篇

0x00 前言

在前面寫的幾篇文章裡,講了封裝和繼承,那麼這篇文章就來講講第三大面向物件的特性多型吧,在此之前先接著上篇文章的抽象先來看看Java裡面介面的應用。

0x01 介面

介面在java裡面屬於引用資料型別,是方法的集合。如果說的類的內部封裝了成員變數、構造方法和成員方法,那麼介面內部主要封裝了方法,包括抽象方法,預設方法和靜態方法,私有方法。

介面的定義和定義類差不多,定義介面需要使用到interface關鍵字。雖然說他也會被編譯為class檔案,但是他並不是一個類,而是另一種引用資料型別。

引用資料型別:陣列、介面、類

介面的使用,他和抽象類一樣不能直接被建立,但是他可以被實現使用implements關鍵字。
一個實現介面的類可以看作是介面的子類,需要實現接口裡面所有的抽象方法,建立該類的物件,就可以呼叫方法了,否則他必須是抽象的子類。

定義格式:

public interface 介面名{
    
}

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

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

接口裡的預設方法和靜態方法

預設方法:使用default進行修飾,不能省略,提供給子類或者子類重寫使用。

靜態方法:使用static修飾,提供給介面直接呼叫。

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

接口裡的私有方法和靜態方法

私有方法和定義私有變數一樣,使用private修飾,提供給介面的預設方法和靜態方法呼叫。

pubilc interface InterfaceName{
    
}

實現介面

類和介面的關係為實現關係,這被稱為類實現介面,這類可以稱為介面的實現類,基本上和抽象類有點類似,也可以稱為介面的子類。實現的動作蕾仕於繼承,格式幾乎一致,但是是使用implements關鍵字,而不是extends。

實現介面必須重寫所有的抽象方法

集成了介面的預設方法,可以直接呼叫也可以重寫

介面程式碼:
public interface MyInterface {
    void eat();
    void sleep();

}
實現類程式碼:
public class Zi implements MyInterface {


    @Override
    public void eat() {
        System.out.println("吃");
    }

    @Override
    public void sleep() {
        System.out.println("睡");

    }
}

main方法程式碼:

public static void main(String[] args) {
        Zi zi = new Zi();
        zi.eat();
        zi.sleep();

    }

介面預設方法程式碼實現:

定義介面:

public interface MyInterface {
    default void eat() {
        System.out.println("吃");
        
    }
    public default void sleep(){
        System.out.println("睡");
    }

}
實現類程式碼:

public class Zi implements MyInterface {



}

main方法程式碼:

public static void main(String[] args) {
        Zi zi = new Zi();
        zi.sleep();
        zi.eat();

    }

這裡介面定義的第一個方法直接defalut使用這個關鍵字定義預設方法,第二個加上了public這兩種寫法都可以,前者是省略式的寫法。

實現類的任何程式碼都沒寫,只寫了一個繼承,預設方法是預設就定義好的,可以不用寫,從例項化物件直接呼叫,當然也可以在實現類裡面進行重寫。

介面靜態方法使用

靜態與.class 檔案相關,只能使用介面名呼叫,不可以通過實現類的類名或者實現類的物件呼叫。

介面定義:
public interface MyInterface {
    static void eat() {
        System.out.println("吃");
    }
}

實現類定義:

public class Zi implements MyInterface {

    
}

main方法程式碼:

public static void main(String[] args) {
        MyInterface.eat();

    }
    

這裡可以看到在main方法裡面,例項化一個物件,然後去訪問eat方法是報錯的,定義為靜態的方法必須使用介面名進行訪問。

私有方法的使用

私有方法:只有預設方法可以訪問。

私有靜態方法:只有預設方法和靜態方法可以呼叫。

如果一個介面有多個預設方法並且重複,那麼就可以提取出來封裝到私有方法裡面,供預設方法去呼叫他。

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

這種寫法是jdk9的新特性,jdk8沒有,此段描述純屬複製。

0x02 介面多實現

在前面說到因為java只支援單繼承,所以一個類只能繼承一個父類,但是對於介面來說,一個類是可以實現多個介面的,這就叫做介面的多實現,並且一個類裡面可以繼承一個父類,同時也實現多個介面。

格式:

class 類名 extends 父類名 impls 介面1,介面2{
    ...
}
或者
class 父類名 impls 介面1,介面2{
    ...
}

介面多實現多抽象方法

如果在多個介面中有重名的方法,該重名的方法只需重寫一次。

介面多實現多個預設方法

介面中多個預設方法,實現類都可以繼承使用。但是如果有重名的方法就必須重寫一次。

介面1:

public interface MyInterface {
    default void method1(){
        System.out.println("我是一個沒有感情的預設方法");
    }
    }
介面2:
public interface MyInterface2 {
    default void method1(){
        System.out.println("我是一個沒有感情的預設方法");
    }
    }
實現類:

public class Zi implements MyInterface,MyInterface1 {
    @Override
    public void method1() {
        System.out.println("沒有感情的方法");
    }
}

main方法程式碼:

    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method1();
    }

而介面中的靜態方法就不會存在重名的問他,靜態方法訪問可以直接使用介面名訪問靜態方法。

優先順序問題

一個類中如果計策了一個父類又實現了多個介面時,父類中的成員方和介面中的預設方法重名,子類就近選擇執行的父類成員方法。

注意事項:

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

介面中,沒有構造方法,不能建立物件。

介面中,沒有靜態程式碼塊。

0x03 多型

多型是面向物件的第三大特性。

多型:指同一種行為,具有不同的表現形式。

多型前提:

1.繼承或者實現

2.方法的重寫

3.父類引用指向子類[格式]

格式:

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

父類型別:指向子類物件繼承的父類型別,或者是實現的父類介面型別。

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

父類程式碼:

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


子類1程式碼:
public class Cat extends Animal {


    @Override
    public void play() {
        System.out.println("擼貓");
    }
}

子類2 程式碼:
public class Dog extends Animal{

    @Override
    public void play() {
        System.out.println("擼狗");
    }
}

main方法程式碼:

public static void main(String[] args) {
        Animal cat = new Cat();
        Animal dog = new Dog();
        cat.play();
        dog.play();
    }

0x04 多型引用型別轉換

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

我們經常用到的就是向上轉型,多型本身就是子類型別向父類型別向上轉換的過程。

當父類引用指向子類物件的時候,這就是向上轉型。
程式碼:

Antmal cat = 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("抓魚");
    }

}

main方法程式碼:

public static void main(String[] args) {
        Animal cat = new Cat();
        cat.eat();
        Cat c =(Cat)cat;
        c.catchMouse();
    }

為了避免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
}
}
}

0x05 結尾

介面與多型篇章更新完成,檢測每天一個小總結。