簡體   English   中英

了解Java中的繼承和抽象類

[英]Understanding inheritance and abstract classes in Java

好的,所以我一直在通過Google搜索來尋找文檔,但是我還沒有找到任何能真正描述我要回答的內容,所以在這里我問你們。

因此,我得到了繼承及其運作方式。 我遇到的問題是有時我看到一個對象最初定義為一種類型,並設置為另一種類型,但我不完全了解發生了什么。 這是一個例子:

假設我有動物課,而貓和狗課又有動物課。 貓,動物和狗都有方法speak(),用於貓打印“喵”,用於狗打印“ woof”和用於動物“不會說話”。

好了,所以這終於是我的問題了。 如果讓一只貓(c)然后運行Animal a = c;會發生什么? 如果我運行a.speak();會發生什么? 叫哪種說話方法? 當我改變這樣的類型時到底發生了什么? 我會有任何真正的理由使用它嗎?

就抽象方法而言,我的問題是擁有它們的真正意義是什么? 在示例中,我已經看到它們已放入超類中,並且其下的類定義了確切的行為。 通過將一個抽象方法放在一個超類中,是不是要求其下的所有類都可以實現它呢?

感謝你的幫助!

如果讓一只貓(c)然后運行Animal a = c;會發生什么? 如果我運行a.speak();會發生什么? 叫哪種說話方法? 當我改變這樣的類型時到底發生了什么? 我會有任何真正的理由使用它嗎?

始終是真實類的方法,例如,在這種情況下,是貓的speak()方法。

就抽象方法而言,我的問題是擁有它們的真正意義是什么?

他們確保例如每個動物都有一個可以調用每個動物的方法walk() 保證書上寫着“每個Animal物體都有這種方法,您不必在意”。

在示例中,我已經看到它們已放入超類中,並且其下的類定義了確切的行為。 通過將一個抽象方法放在一個超類中,是不是要求其下的所有類都可以實現它呢?

是的,也要實現它或使其抽象化。

Cat c = new Cat(); 動物a = c; a.speak()將打印出喵聲。

請檢查出Java多態性

關於抽象類:

當抽象類被子類化時,該子類通常為其父類中的所有抽象方法提供實現。 但是,如果沒有,則子類也必須聲明為抽象。

JLS的5.2節解釋了為什么Cat可分配給Animal (請注意,由於Cat是“更特定的”種類,所以不能將Animal隱式分配給CatCatAnimal的子類型,而AnimalCat的超類型)

如下檢查將編譯時參考類型S(源)的值分配給編譯時參考類型T(目標)的變量:

  • 如果S是類類型:
    • 如果T為類類型,則S必須與T屬於同一類,或者S必須為T的子類,否則會發生編譯時錯誤。
    • 如果T是接口類型,則S必須實現接口T,否則會發生編譯時錯誤。
    • 如果T是數組類型,則發生編譯時錯誤。
  • 如果S是接口類型:
    • 如果T是類類型,則T必須是Object ,否則會發生編譯時錯誤。
    • 如果T是接口類型,則T必須是與S相同的接口或S的超接口,否則會發生編譯時錯誤。
    • 如果T是數組類型,則發生編譯時錯誤。
  • 如果S是數組類型SC [],即,數組類型為SC的組件:

[為簡潔起見,省略]

假設我有動物課,而貓和狗課又有動物課。 貓,動物和狗都有方法speak(),用於貓打印“喵”,用於狗打印“ woof”和用於動物“不會說話”。

好了,所以這終於是我的問題了。 如果先讓一只貓( c )然后運行Animal a = c;該怎么辦? 如果我運行a.speak();會怎樣? 哪個調用了speak()方法? 當我改變這樣的類型時到底發生了什么? 我會有任何真正的理由使用它嗎?

Java中的對象完全知道創建對象的類型。 它實際上是一個隱藏字段(您可以使用Object.getClass()方法進行檢索)。 此外,所有非靜態方法解析都從最特定類的方法定義開始,並朝着最通用類( Object )進行; 由於Java中實現的唯一繼承,因此這是一個簡單的搜索。 Cat知道這是Animal的子類型,這是Object的子類型,而c知道它是Cat ,與變量的類型無關。

進行分配時, 編譯器會檢查要分配的值的已知類型是分配給的類型還是其子類型之一 如果是這樣,分配工作。 如果不是這樣,您將需要一個顯式的強制轉換(在運行時進行適當的類型檢查;強制轉換不能破壞Java的類型系統,它們只能使其難看)。 它不會改變以下事實:方法查找仍然是動態完成的,並且對象仍然知道它實際上是什么類型。 該程序正在做的所有工作都在忽略一些信息。 如果您了解C ++,則可以將Java視為僅具有虛擬方法(和靜態方法),並且將查詢處理到vtable中非常簡單,因為繼承菱形或其他有害情況沒有問題。

使用接口的實現時,幾乎是一樣的,只是執​​行了更復雜的查找(即,首先像以前一樣先查找vtable中的索引,然后再進行操作)。 但是,擁有一個實現接口的對象意味着必須有一些可以完全實現該接口的類,到那時,這又都相對簡單了。 記住,所有真正復雜的工作都是在編譯時完成的。 在運行時,在所有情況下事情都是相對簡單的。

那么,您會利用其中的任何一個嗎? 好吧,您應該 (而且您實際上會發現在真實代碼中很難避免)。 從接口或超類定義合同的角度考慮是一種很好的方式,其中子類型遵守合同,而調用者不必知道詳細信息。 Java庫非常頻繁地使用它,特別是因為合同類型細節及其實現的可見性可能不同。 所有客戶端代碼都知道對象遵守合同,屬於給定類型。

根據您的背景,C ++或Java會變得非常混亂。

在C ++中,存在虛函數的概念,該虛函數在運行時進行查找,以確定該函數所屬的實際類。 還有一些非虛擬函數將根據變量類型被調用。

在Java中,盡管所有方法本質上都是虛擬的,這意味着Java方法總是在運行時查找,這個過程稱為“ 運行時多態”

這樣的好處是這樣的

class Animal{
    public void type(){
        System.out.println("animal");

    }
}

class Dog extends Animal{
    public void type(){
        System.out.println("dog");
    }
}

class Cat extends Animal{
    public void type(){
        System.out.println("cat");
    }
}

public class Driver{
    public static void main(String[] args){
        Animal[] animals = new Animal[3];
        animals[0] = new Animal();
        animals[1] = new Dog();
        animals[2] = new Cat();

        for(Animal animal: animals){
            animal.type();
        }
    }
}

這將輸出

animal
dog
cat

據我了解,您使用接口來解耦代碼。 您要針對接口而不是實現進行編程: “編程為接口”是什么意思? 您將使用抽象類來實現功能,這對於所有實現類都是微不足道的,因此您無需在所有實現類中都編寫它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM