JavaRush /Java Blog /Random-TW /Java 中的多態性

Java 中的多態性

在 Random-TW 群組發布
關於 OOP 的問題是 IT 公司 Java 開發人員技術面試中不可或缺的一部分。在這篇文章中我們將討論OOP的原則之一—多態性。我們將重點放在面試中經常被問到的方面,並提供一些小例子以方便理解。

什麼是多態性?

多態性是程式能夠以相同的介面相同地使用對象,而無需了解該對象的特定類型。如果您以這種方式回答什麼是多態性這個問題,您很可能會被要求解釋您的意思。再次,不要問一堆額外的問題,為面試官安排好一切。

Java 多態性面試 - 1
我們可以從以下事實開始:OOP 方法涉及基於類別物件的交互來建立 Java 程式。類別是預先編寫的圖形(模板),程式中的物件將根據這些圖形建立。此外,一個類別總是有一個特定的類型,透過良好的程式設計風格,它可以透過名稱「講述」其用途。另外,要注意的是,由於Java是強型別語言,程式碼在宣告變數時總是需要指明物件的類型。除此之外,嚴格類型化可以提高程式碼安全性和程式可靠性,並允許您在編譯階段防止類型不相容錯誤(例如,嘗試將字串除以數字)。當然,編譯器必須「知道」聲明的類型 - 這可以是 JDK 中的類,也可以是我們自己創建的類。請面試官注意,在使用程式碼時,我們不僅可以使用聲明時分配的類型的對象,還可以使用其後代。這是很重要的一點:我們可以將多種類型視為一種類型(只要這些類型是從基底類型派生的)。這也意味著,在聲明了超類別類型的變數後,我們可以將其後代之一的值賦給它。如果你舉個例子,面試官會喜歡的。選擇一組物件可以通用(基礎)的某個對象,並從中繼承幾個類別。基底類:
public class Dancer {
    private String name;
    private int age;

    public Dancer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void dance() {
        System.out.println(toString() + "I dance like everyone else.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. " ;
    }
}
在後代中,重寫基類方法:
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance() {
        System.out.println( toString() + "I dance electric boogie!");
    }
}

public class BreakDankDancer extends Dancer{

    public BreakDankDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance(){
        System.out.println(toString() + "I'm breakdancing!");
    }
}
Java 中的多態性與程式中物件的使用的範例:
public class Main {

    public static void main(String[] args) {
        Dancer dancer = new Dancer("Anton", 18);

        Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);// upcast to base type
        Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20); // upcast to base type

        List<Dancer> discotheque = Arrays.asList(dancer, breakDanceDancer, electricBoogieDancer);
        for (Dancer d : discotheque) {
            d.dance();// polymorphic method call
        }
    }
}
在方法程式碼中main顯示 以下行中的內容:
Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20);
我們聲明了一個超類別類型變量,並將其後代之一的值賦給它。最有可能的是,您會被問到為什麼編譯器不會抱怨賦值符號左側和右側聲明的類型之間的不匹配,因為 Java 具有嚴格的類型。解釋一下向上類型轉換在這裡起作用——對物件的引用被解釋為對基底類別的引用。此外,編譯器在程式碼中遇到這種構造時,會自動且隱式地執行此操作。根據範例程式碼可以看出,賦值符號左側聲明的類別類型具有Dancer在右側聲明的多種形式(類型)BreakDankDancerElectricBoogieDancer對於超類別方法中定義的通用功能,每種形式都可以有自己獨特的行為dance。也就是說,在超類別中聲明的方法可以在其後代中以不同的方式實現。在這種情況下,我們正在處理方法重寫,這正是創建各種形式(行為)的原因。你可以透過執行main方法程式碼來執行: 程式輸出 我是Anton,我今年18歲。我和其他人一樣跳舞。我是阿列克謝,今年 19 歲。我跳霹靂舞!我是伊戈爾,今年 20 歲。我跳電動布吉舞! 如果我們不在後代中使用重寫,那麼我們將不會得到不同的行為。BreakDankDancer例如,如果我們ElectricBoogieDancer註解掉我們的類別的方法dance,程式的輸出將是這樣的: 我是 Anton,我 18 歲。我和其他人一樣跳舞。我是阿列克謝,今年 19 歲。我和其他人一樣跳舞。我是伊戈爾,今年 20 歲。我和其他人一樣跳舞。 這意味著創建新類別根本沒有意義 BreakDankDancerJava多態性的原理到底是什麼?在程式中使用一個物件而不知道它的具體類型,它隱藏在哪裡?在我們的範例中,這是對類型的物件的方法呼叫。Java多態性意味著程式不需要知道物件或物件將是什麼類型。最主要的是,它是階級的後裔。而如果我們談論後​​代,需要注意的是,Java 中的繼承不僅是,而且是。現在是時候記住 Java 不支援多重繼承 - 每種類型都可以有一個父類別(超類別)和無限數量的後代(子類別)。因此,介面用於向類別添加多種功能。與繼承相比,介面減少了物件與父物件的耦合,並且使用非常廣泛。在Java中,介面是參考類型,因此程式可以將該類型宣告為介面類型的變數。現在是舉例的好時機。讓我們創建介面: ElectricBoogieDancerd.dance()dDancerBreakDankDancerElectricBoogieDancerDancerextendsimplements
public interface Swim {
    void swim();
}
為了清楚起見,讓我們採用不同且不相關的物件並在其中實作一個介面:
public class Human implements Swim {
    private String name;
    private int age;

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void swim() {
        System.out.println(toString()+"I swim with an inflatable ring.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. ";
    }

}

public class Fish implements Swim{
    private String name;

    public Fish(String name) {
        this.name = name;
    }

    @Override
    public void swim() {
        System.out.println("I'm a fish " + name + ". I swim by moving my fins.");

    }

public class UBoat implements Swim {

    private int speed;

    public UBoat(int speed) {
        this.speed = speed;
    }

    @Override
    public void swim() {
        System.out.println("The submarine is sailing, rotating the propellers, at a speed" + speed + " knots.");
    }
}
方法main
public class Main {

    public static void main(String[] args) {
        Swim human = new Human("Anton", 6);
        Swim fish = new Fish("whale");
        Swim boat = new UBoat(25);

        List<Swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
執行介面中定義的多態方法的結果使我們能夠看到實現該介面的類型的行為差異。它們存在於方法執行的不同結果中swim。研究完我們的例子後,面試官可能會問為什麼,當執行程式碼時main
for (Swim s : swimmers) {
            s.swim();
}
這些類別中定義的方法是否為我們的物件呼叫?執行程式時如何選擇所需的方法實作?為了回答這些問題,我們需要談談後期(動態)綁定。透過綁定,我們的意思是在方法呼叫與其在類別中的特定實作之間建立連接。本質上,程式碼決定將執行類別中定義的三個方法中的哪一個。Java 預設使用後期綁定(在執行時而不是在編譯時,就像早期綁定的情況一樣)。這意味著編譯程式碼時
for (Swim s : swimmers) {
            s.swim();
}
編譯器還不知道程式碼來自哪個類HumanFish也不知道Uboat它是否會在swim. 這只有在程式執行時才能確定,這要歸功於動態分派機制——在程式執行期間​​檢查物件的類型並為該類型選擇所需的方法實作。如果你被問到這是如何實現的,你可以回答說,當載入和初始化物件時,JVM 在記憶體中建立表,並在表中將變數與其值關聯起來,將物件與其方法關聯起來。此外,如果一個物件被繼承或實作一個接口,則首先檢查其類別中是否存在重寫方法。如果有,則它們與此類型相關聯,如果沒有,則搜尋在上一級類別(在父級中)中定義的方法,依此類推,直至多層次結構中的根。談到 OOP 中的多態性及其在程式碼中的實現,我們注意到使用抽象描述和介面來定義基底類別是一種很好的做法。這種實踐基於抽象的使用 - 隔離常見的行為和屬性並將它們封裝在抽象類別中,或僅隔離常見的行為 - 在這種情況下我們建立一個介面。建構和設計基於介面和類別繼承的物件層次結構是實現 OOP 多態性原則的先決條件。關於Java中的多態性和創新問題,我們可以提到,在建立抽象類別和介面時,從Java 8開始,可以使用關鍵字在基底類別中編寫抽象方法的預設實作default。例如:
public interface Swim {
    default void swim() {
        System.out.println("Just floating");
    }
}
有時他們可能會詢問在基類中聲明方法的要求,以便不違反多態性原則。這裡一切都很簡單:這些方法不應該是staticprivateFinalPrivate使該方法僅在類別中可用,並且不能在後代中重寫它。靜態使方法成為類別的屬性,而不是物件的屬性,因此超類別方法將始終被呼叫。Final將使該方法不可變並且對其繼承者隱藏。

多態性在Java中為我們帶來了什麼?

使用多態性帶給我們什麼的問題很可能也會出現。在這裡你可以簡單地回答,而不需要太深入:
  1. 允許您替換物件實作。這就是測試的基礎。
  2. 提供程式可擴展性 - 為未來創建基礎變得更加容易。基於現有類型新增類型是擴展 OOP 風格編寫的程式功能的最常見方法。
  3. 允許您將具有常見類型或行為的物件組合到一個集合或陣列中,並統一操作它們(如我們的範例中,讓每個人跳舞 - 一種方法dance或游泳 - 一種方法swim)。
  4. 建立新類型時的靈活性:您可以選擇從父級實作方法或在子級中覆寫它。

旅途的臨別贈言

多型原理是一個非常重要且廣泛的議題。它涵蓋了Java 的 OOP的近一半以及該語言基礎知識的很大一部分。你不可能在面試中定義這個原則。對此的無知或誤解很可能會導致面試結束。因此,在考試前不要偷懶檢查自己的知識,必要時刷新一下。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION