Java 中的繼承和多態(深入版)

2021-03-02 Java後端

面向對象的三大特性:封裝、繼承、多態。在這三個特性中,如果沒有封裝和繼承,也不會有多態。
那麼多態實現的途徑和必要條件是什麼呢?以及多態中的重寫和重載在JVM中的表現是怎麼樣?在Java中是如何展現繼承的特性呢?對於子類繼承於父類時,又有什麼限制呢?本文系基礎,深入淺出過一遍 Java 中的多態和繼承。多態是同一個行為具有多個不同表現形式或形態的能力。舉個慄子,一隻雞可以做成白切雞、豉油雞、吊燒雞、茶油雞、鹽焗雞、蔥油雞、手撕雞、清蒸雞、叫花雞、啤酒雞、口水雞、香菇滑雞、鹽水雞、啫啫滑雞、雞公煲等等。

class Chicken{
    public void live(){
        System.out.println("這是一隻雞");
    }
}

1. 子類必須繼承父類對於子類必須繼承父類,小編個人認為,是因為按照面向對象的五大基本原則所說的中的依賴倒置原則:抽象不依賴於具體,具體依賴於抽象。既然要實現多態,那麼必定有一個作為"抽象"類來定義「行為」,以及若干個作為"具體"類來呈現不同的行為形式或形態。

class BaiqieChicken extends Chicken{ }

但僅是定義一個白切雞類是不夠的,因為在此我們只能做到復用父類的屬性和行為,而沒有呈現出行為上的不同的形式或形態2. 必須有重寫重寫,簡單地理解就是重新定義的父類方法,使得父類和子類對同一行為的表現形式各不相同。我們用白切雞類來舉個慄子。

class BaiqieChicken extends Chicken{
    public void live(){
        System.out.println("這是一隻會被做成白切雞的雞");
    }
}

這樣就實現了重寫,雞類跟白切雞類在live()方法中定義的行為不同,雞類是一隻命運有著無限可能的雞,而白切雞類的命運就是做成一隻白切雞。但是為什麼還要有「父類引用指向子類對象」這個條件呢?3. 父類引用指向子類對象其實這個條件是面向對象的五大基本原則裡面的裡氏替換原則,簡單說就是父類可以引用子類,但不能反過來。當一隻雞被選擇做白切雞的時候,它的命運就不是它能掌控的。

Chicken c = new BaiqieChicken();
c.live();

為什麼要有這個原則?因為父類對於子類來說,是屬於「抽象」的層面,子類是「具體」的層面。「抽象」可以提供接口給「具體」實現,但是「具體」憑什麼來引用「抽象」呢?而且「子類引用指向父類對象」是不符合「依賴倒置原則」的。當一隻白切雞想回頭重新選擇自己的命運,抱歉,它已經在鍋裡,逃不出去了。

BaiqieChicken bc = new Chicken();
bc.live();

多態的實現途徑多態的實現途徑有三種:重寫、重載、接口實現,雖然它們的實現方式不一樣,但是核心都是:同一行為的不同表現形式1. 重寫重寫,指的是子類對父類方法的重新定義,但是子類方法的參數列表和返回值類型,必須與父類方法一致!所以可以簡單的理解,重寫就是子類對父類方法的核心進行重新定義。

class Chicken{
    public void live(String lastword){
        System.out.println(lastword);
    }
}
class BaiqieChicken extends Chicken{
    public void live(String lastword){
        System.out.println("這隻白切雞說:");
        System.out.println(lastword);
    }
}

這裡白切雞類重寫了雞類的live()方法,為什麼說是重寫呢?因為白切雞類中live()方法的參數列表和返回值與父類一樣,但方法體不一樣了。2. 重載重載,指的是在一個類中有若干個方法名相同,但參數列表不同的情況,返回值可以相同也可以不同的方法定義場景。也可以簡單理解成,同一行為(方法)的不同表現形式。

class BaiqieChicken extends Chicken{
    public void live(){
        System.out.println("這是一隻會被做成白切雞的雞");
    }
    public void live(String lastword){
        System.out.println("這隻白切雞說:");
        System.out.println(lastword);
    }
}

這裡的白切雞類中的兩個live()方法,一個無參一個有參,它們對於白切雞類的live()方法的描述各不相同,但它們的方法名都是live。通俗講,它們對於白切雞雞生的表現形式不同。3. 接口實現接口,是一種無法被實例化,但可以被實現的抽象類型,是抽象方法的集合,多用作定義方法集合,而方法的具體實現則交給繼承接口的具體類來定義。所以,接口定義方法,方法的實現在繼承接口的具體類中定義,也是對同一行為的不同表現形式

interface Chicken{
    public void live();
}
class BaiqieChicken implements Chicken{
    public void live(){
        System.out.println("這是一隻會被做成白切雞的雞");
    }
}
class ShousiChicken implements Chicken{
    public void live(){
        System.out.println("這是一隻會被做成手撕雞的雞");
    }
}

從上面我們可以看到,對於雞接口中的live()方法,白切雞類和手撕雞類都有自己對這個方法的獨特的定義。在虛擬機中多態如何表現前文我們知道,java文件在經過javac編譯後,生成class文件之後在JVM中再進行編譯後生成對應平臺的機器碼。而JVM的編譯過程中體現多態的過程,在於選擇出正確的方法執行,這一過程稱為方法調用方法調用的唯一任務是確定被調用方法的版本,暫時還不涉及方法內部的具體運行過程。(註:方法調用不等於方法執行)在介紹多態的重載和重寫在JVM的實現之前,我們先簡單了解JVM提供的5條方法調用字節碼指令:invokestatic:調用靜態方法。
invokespecial:調用實例構造器方法、私有方法和父類方法。
invokevirtual:調用所有的虛方法(這裡的虛方法泛指除了invokestatic、invokespecial指令調用的方法,以及final方法)。
invokeinterface:調用接口方法,會在運行時再確定一個實現此接口的對象。
invokedynamic:先在運行時動態解析出調用點限定符所應用的方法(說人話就是用於動態指定運行的方法)。而方法調用過程中,在編譯期就能確定下來調用方法版本的靜態方法、實例構造器方法、私有方法、父類方法和final方法(雖是由invokevirtual指令調用)在編譯期就已經完成了運行方法版本的確定,這是一個靜態的過程,也稱為解析調用。而分派調用則有可能是靜態的也可能是動態的,可能會在編譯期發生或者運行期才確定運行方法的版本。而分派調用的過程與多態的實現有著緊密聯繫,所以我們先了解一下兩個概念:靜態分派:所有依賴靜態類型來定位方法執行版本的分派動作。
動態分派:根據運行期實際類型來定位方法執行版本的分派動作。1. 重載

public class StaticDispatch {
    static abstract class Human{ }
    static class Man extends Human{}
    static class Woman extends Human{}
    
    public void sayHello(Human guy){
        System.out.println("hello, guy!");
    }
    public void sayHello(Man guy){
        System.out.println("hello, gentleman!");
    }
    public void sayHello(Woman guy){
        System.out.println("hello, lady!");
    }

    public static void main(String[] args){
        Human man = new Man();
        Human woman = new Woman();
        StaticDispatch sr = new StaticDispatch();
        sr.sayHello(man);
        sr.sayHello(woman);
    }
}

想想以上代碼的運行結果是什麼?3,2,1,運行結果如下:根據裡氏替換原則,子類必須能夠替換其基類,也就是說子類相對於父類是「具體類」,而父類是處於「奠定」子類的基本功能的地位。所以,我們把上面代碼中的「Human」稱為變量man的靜態類型(Static Type),而後面的"Man"稱為變量的實際類型(Actual Type),二者的區別在於,靜態類型是在編譯期可知的;而實際類型的結果在運行期才能確定,編譯期在編譯程序時並不知道一個對象的實際類型是什麼。在了解了這兩個概念之後,我們來看看字節碼文件是怎麼說的:

javac -verbose StaticDispatch.class


我們看到,圖中的黃色框的invokespecial指令以及標籤,我們可以知道這三個是指令是在調用實例構造器方法。同理,下面兩個紅色框的invokevirtual指令告訴我們,這裡是採用分派調用的調用虛方法,而且入參都是「Human」。因為在分派調用的時候,使用哪個重載版本完全取決於傳入參數的數量和數據類型。而且,虛擬機(準確說是編譯期)在重載時是通過參數的靜態類型而不是實際類型作為判斷依據,並且靜態類型是編譯期可知的。所以,在編譯階段,Javac編譯期就會根據參數的靜態類型決定使用哪個重載版本。重載是靜態分派的經典應用。2. 重寫

public class StaticDispatch {
    static abstract class Human{
        protected abstract void sayHello();
    }
    static class Man extends Human{
        @Override
        protected void sayHello() {
            System.out.println("man say hello");
        }
    }
    static class Woman extends Human{
        @Override
        protected void sayHello() {
            System.out.println("woman say hello");
        }
    }
    public static void main(String[] args){
        Human man = new Man();
        Human woman = new Woman();
        man.sayHello();
        woman.sayHello();
    }
}

man say hello
woman say hello

相信你看到這裡也會會心一笑,這一看就很明顯嘛,重寫是按照實際類型來選擇方法調用的版本嘛。先別急,我們來看看它的字節碼:

嘶…這好像跟靜態分派的字節碼一樣啊,但是從運行結果看,這兩句指令最終執行的目方法並不相同啊,那原因就得從invokevirtual指令的多態查找過程開始找起。我們來看看invokevirtual指令的運行時解析過程的步驟:

找到操作數棧頂的第一個元素所指向的對象的實際類型,記作C。

如果在在類型C中找到與常量中的描述符和簡單名稱都相符的方法,則進行訪問權限校驗,如果通過則返回這個方法的直接引用,查找過程結束;如果不通過,則返回java.lang.IllegalAccessError異常。

否則,按照繼承關係從下往上依次對C的各個父類進行第2步的搜索和驗證過程。

如果始終沒有找到合適的方法,則拋出java.lang.AbstractMethodError異常。

我們可以看到,由於invokevirtual指令在執行的第一步就是在運行期確定接收者的實際類型,所以字節碼中會出現invokevirtual指令把常量池中的類方法符號引用解析到了不同的直接引用上,這個就是Java重寫的本質。總結一下,重載的本質是在編譯期就會根據參數的靜態類型來決定重載方法的版本,而重寫的本質在運行期確定接收者的實際類型繼承


假如我們有兩個類:生物類、貓類。

生物類:

class Animal{
  private String name;
  public void setName(String name){
    this.name = name;
  }
  public String getName(){
    return this.name;
  }
}

class Cat{
  private String name;
    private String sound;
  public void setName(String name){
    this.name = name;
  }
    public void setSound(String sound){
        this.sound = sound;
    }
  public String getName(){
    return this.name;
  }
    public String getSound(){
        return this.sound;
    }
}

我們知道,貓也是屬於生物中的一種,生物有的屬性和行為,貓按理來說也是有的。但此時沒有繼承的概念,那麼代碼就得不到復用,長期發展,代碼冗餘、維護困難且開發者的工作量也非常大。繼承的概念繼承就是子類繼承父類的特徵和行為,使得子類對象(實例)具有父類的實例域和方法,或子類從父類繼承方法,使得子類具有父類相同的行為。簡單來說,子類能吸收父類已有的屬性和行為。除此之外,子類還可以擴展自身功能。子類又被稱為派生類,父類被稱為超類。在 Java中,如果要實現繼承的關係,可以使用如下語法:繼承的基本實現

class Animal{
  private String name;
  public void setName(String name){
    this.name = name;
  }
  public String getName(){
    return this.name;
  }
}
class Cat extends Animal{}

public class Test{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.setName("貓");
        System.out.println(cat.getName());
    }
}

我們可以看出,子類可以在不擴展操作的情況下,使用父類的屬性和功能子類擴充父類

class Animal{
  private String name;
  public void setName(String name){
    this.name = name;
  }
  public String getName(){
    return this.name;
  }
}
class Cat extends Animal{
    private String sound;
    public void setSound(String sound){
    this.sound = sound;
  }
  public String getSound(){
    return this.sound;
  }
}

public class Test{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.setName("NYfor2020")
        cat.setSound("我不是你最愛的小甜甜了嗎?");
        System.out.println(cat.getName()+":"+cat.getSound());
    }
}

我們可以看出,子類在父類的基礎上進行了擴展,而且對於父類來說,子類定義的範圍更為具體。也就是說,子類是將父類具體化的一種手段。總結一下,Java中的繼承利用子類和父類的關係,可以實現代碼復用,子類還可以根據需求擴展功能。繼承的限制1. 子類只能繼承一個父類

class ACat{
  public void mewo(){...}
}
class BCat{
  public void mewo(){...}
}
class CCat extends ACat, BCat{
  @Override
  public void mewo(){...?} //提問:這裡的mewo()是繼承自哪個類?
}

class ACat{}
class BCat extends ACat{}
class CCat extends BCat{}

這樣,BCat就繼承了ACat所有的方法,而CCat繼承了ACat、BCat所有的方法,實際上CCat是ACat的子(孫)類,是BCat的子類。總結一下,子類雖然不支持多重繼承,只能單繼承,但是可以多層繼承2. private修飾不可直接訪問,final修飾不可修改private修飾對於子類來說,父類中用private修飾的屬性對其隱藏的,但如果提供了這個變量的setter/getter接口,還是能夠訪問和修改這個變量的。

class ACat {
    private String sound = "meow";
    public String getSound() {
        return sound;
    }
    public void setSound(String sound) {
        this.sound = sound;
    }
}

class BCat extends ACat {
}

public class Test {
    public static void main(String[] args) {
        BCat b = new BCat();
        b.setSound("我不是你最愛的小甜甜了嗎?");
        System.out.println(b.getSound());
    }
}

final修飾父類已經定義好的final修飾變量(方法也一樣),子類可以訪問這個屬性(或方法),但是不能對其進行更改

class ACat {
    final String sound = "你是我最愛的小甜甜";
    public String getSound() {
        return sound;
    }
    public void setSound(String sound){
        this.sound = sound; //這句執行不了,會報錯的
    }
}

class BCat extends ACat {
}

總結一下,用private修飾的變量可以通過getter/setter接口來操作,final修飾的變量就只能訪問,不能更改3. 實例化子類時默認先調用父類的構造方法在實例化子類對象時,會調用父類的構造方法對屬性進行初始化,之後再調用子類的構造方法。

class A {
    public A(){
        System.out.println("我不是你最愛的小甜甜了嗎?");
    }
    public A(String q){
        System.out.println(q);
    }
}

class B extends A {
    public B(){
        System.out.println("你是個好姑娘");
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
    }
}

從結果我們可以知道,在實例化子類時,會默認先調用父類中無參構造方法,然後再調動子類的構造方法。那麼怎麼調用父類帶參的構造方法呢?只要在子類構造方法的第一行調用super()方法就好。

class A {
    public A(String q){
        System.out.println(q);
    }
}

class B extends A {
    public B(){
        super("我是你的小甜甜?");
        System.out.println("你是個好姑娘");
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
    }
}

在子類實例化時,默認調用的是父類的無參構造方法,而如果沒有父類無參構造方法,則子類必須通過super()來調用父類的有參構造方法,且super()方法必須在子類構造方法的首行。總結一下,Java繼承中有三種繼承限制,分別是子類只能單繼承、父類中private修飾的變量不能顯式訪問和final修飾的變量不能改變,以及實例化子類必定會先調用父類的構造方法,之後才調用子類的構造方法。類是怎麼加載的?(此處只是粗略介紹類加載的過程,想了解更多可參考《深入理解Java虛擬機》)這三個步驟的開始時間仍然保持著固定的先後順序,但是進行和完成的進度就不一定是這樣的順序了。

1. 加載:虛擬機通過這個類的全限定名來獲取這個類的二進位字節流,然後在字節流中提取出這個類的結構數據,並轉換成這個類在方法區(存儲類結構)的運行時數據結構;2. 驗證:先驗證這字節流是否符合Class文件格式的規範,然後檢查這個類的其父類中數據是否存在衝突(如這個類的父類是否繼承被final修飾的類),接著對這個類內的方法體進行檢查,如果都沒問題了,那就把之前的符號引用換成直接引用;3. 準備:為類變量(static修飾的變量)分配內存(方法區)並設置類變量初始值,而這裡的初始值是指這個數據類型的零值,如int的初始值是0;4. 解析:在Class文件加載過程中,會將Class文件中的標識方法、接口的常量放進常量池中,而這些常量對於虛擬機來說,就是符號引用。此階段就是針對類、接口、欄位等7類符號引用,轉換成直接指向目標的句柄——直接引用。5. 初始化:這階段是執行static代碼塊和類構造器的過程,有小夥伴可能會疑惑類構造器不是默認static的嗎?詳情請看這個博客: https://www.cnblogs.com/dolphin0520/p/10651845.html總結一下,類加載的過程中,首先會對Class文件中的類提取並轉換成運行時數據結構,然後對類的父類和這個類的數據信息進行檢驗之後,為類中的類變量分配內存並且設置初始值,接著將Class文件中與這個類有關的符號引用轉換成直接引用,最後再執行類構造器。而且我們可以從第二步看出,在加載類的時候,會先去檢查這個類的父類的信息,然後再檢查這個類的方法體,也就是說,在加載類的時候,會先去加載它的父類。初學Java的時候知道這些概念,但只是淺嘗而止。現在跟著Hollis大佬的《Java 工程師成神之路!》,重新回顧這些知識的時候,發現如果自己只是像以前一樣片面了解,那豈不是沒有成長?所以在寫文章的過程中,嘗試寫得更加深入且儘量易懂。當然,本人水平有限,如有不正之處,歡迎指正。堅持寫技術文章的確是一件不容易的事情。現在技術更新越來越快,但是依然想把基礎再打牢一點。https://www.cnblogs.com/jack204/archive/2012/10/29/2745150.html從虛擬機指令執行的角度分析JAVA中多態的實現原理https://www.cnblogs.com/hapjin/p/9248525.htmlhttps://blog.csdn.net/wei_zhi/article/details/52780026

 

相關焦點

  • C中的繼承和多態
    有了繼承和多態,我們可以完成代碼重用。在C中有許多技巧可以實現多態。本文的目的就是演示一種簡單和容易的技術,在C中應用繼承和多態。通過創建一個VTable(virtual table)和在基類和派生類對象之間提供正確的訪問,我們能在C中實現繼承和多態。VTable能通過維護一張函數表指針表來實現。為了提供基類和派生類對象之間的訪問,我們可以在基類中維護派生類的引用和在派生類中維護基類的引用。
  • Java中使用接口實現多繼承和多態的方法
    1.JAVA裡沒有多繼承,一個類之能有一個父類。  而繼承的表現就是多態。一個父類可以有多個子類,而在子類裡可以重寫父類的方法(例如方法print()),這樣每個子類裡重寫的代碼不一樣,自然表現形式就不一樣。
  • 繼承、接口與多態的相關問題
    Java中所有的類都是通過直接或間接地繼程java.lang.Object類得到的。繼承而得到的類稱為子類,被繼承的類稱為父類。子類不能繼承父類中訪問權限為private的成員變量和方法。子類可以重寫父類的方法,及命名與父類同名的成員變量。但Java不支持多重繼承,即一個類從多個超類派生的能力。
  • java多態
    多態在設計一個方法時,通常希望該方法具備一定的通用性。例如要實現一個動物叫的方法,由於每種動物的叫聲是不同的,因此可以在方法中接收一個動物類型的參數,當傳入貓類對象時就發出貓類的叫聲,傳入犬類對象時就發出犬類的叫聲。在同一個方法中,這種由於參數類型不同而導致執行效果各異的現象就是多態。繼承是多態得以實現的基礎。
  • 【編程基礎】如何理解java中的多態
    大家都知道Java面向對象有幾大特徵:抽象、封裝、繼承和多態,Java的這些特性讓Java變得很強大,可以很輕鬆的勝任比較複雜的項目開發。今天重點給大家說說多態這個特性。多態總結起來發生的場景就是兩類:1、對象運行時確定是子類還是父類;2、方法運行時確定調用同名的哪個方法;也就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。
  • Java多態性:Java什麼是多態?
    多態性是面向對象編程的又一個重要特徵,它是指在父類中定義的屬性和方法被子類繼承之後,可以具有不同的數據類型或表現出不同的行為,這使得同一個屬性或方法在父類及其各個子類中具有不同的含義
  • 淺析java繼承與多態,抽象類與接口的區別
    面向對象的設計可以更好的解決系統維護、擴展、和代碼重用的需求,代碼過多後過程式的邏輯組織會變得異常複雜,所以就發展出了面向對象式編程。而面向對象的基本特性就是封裝、繼承、與多態。一、繼承extends 關鍵字表示繼承某個已存在的類,已存在的類稱為「超類」、「基類」、或「父類」,而新繼承的類稱為「子類」、「派生類」。子類可以方法父類中的所有非 private 域,也即子類通過繼承獲得了父類具有的特性和能力。
  • python中類的繼承和多態
    繼承是為了代碼復用和設計復用而設計的,是面向對象程序設計的重要特徵之一。當我們設計一個新類時,如果可以繼承一個已有的設計良好的類然後進行二次開發,無疑會大幅度減少開發工作量。1.類的繼承類繼承語法:Class 派生類名(基類名): #基類名寫在括號裡派生類成員在繼承關係中,已有的,設計好的類稱為父類或基類,新設計的類稱為子類或派生類。派生類可以繼承父類的公有成員,但是不能繼承其私有成員。
  • C語言實現繼承和多態
    背景 寫這篇文章的是因為前幾天看了一個火狐的郵箱代碼,純C寫的,很佩服他們能用C寫出多態、繼承的形式來
  • C語言中的面向對象(1)-類模擬和多態,繼承
    正在閱讀:C語言中的面向對象(1)-類模擬和多態,繼承C語言中的面向對象(1)-類模擬和多態,繼承2005-01-12 10:04出處:作者:liyuming1978所以說類包含了兩個範疇:數據和操作。而C語言中的struct僅僅是數據的集合。
  • Java面向對象中的封裝和訪問控制符詳解
    封裝面向對象編程有三大概念:封裝、繼承、多態,今天我們就先從封裝開始講起。封裝(Encapsulation)是面向對象方法的重要原則,就是把對象的屬性和操作(或服務)結合為一個獨立的整體,並儘可能隱藏對象的內部實現細節。封裝是把過程和數據包圍起來,對數據的訪問只能通過已定義的接口。
  • 電腦程式的思維邏輯 (15) - 初識繼承和多態
    在繼承關係中,有父類和子類,比如動物類Animal和狗類Dog,Animal是父類,Dog是子類。父類也叫基類,子類也叫派生類,父類子類是相對的,一個類B可能是類A的子類,是類C的父類。之所以叫繼承是因為,子類繼承了父類的屬性和行為,父類有的屬性和行為,子類都有。但子類可以增加子類特有的屬性和行為,某些父類有的行為,子類的實現方式可能與父類也不完全一樣。
  • JAVA繼承總結
    今天來講講JAVA繼承總結:       Java中的繼承作為Java面向對象三大特性之一,在知識點上而言,還是很多的,而且java的繼承與覆蓋基本是java筆試中常出的題,也比較繞。下面小編來介紹下JAVA繼承的相關知識點:       繼承在本職上是特殊一般的關係,即常說的is-a關係。
  • Java多態的實現機制是什麼,寫得非常好!
    三、多態的用途多態最大的用途我認為在於對設計和架構的復用,更進一步來說,《設計模式》中提倡的針對接口編程而不是針對實現編程就是充分利用多態的典型例子。定義功能和組件時定義接口,實現可以留到之後的流程中。
  • Java基礎知識學習:Java三大特效之多態!
    1.1 多態的形式多態是繼封裝、繼承之後,面向對象的第三大特性。多態是出現在繼承或者實現關係中的。Cat繼承了Animal,Cat對象也是Animal類型,自然可以賦值給父類類型的變量。1.2 多態的案例演示當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫後方法。如果子類沒有重寫該方法,就會調用父類的該方法。
  • 黑馬程式設計師:Python封裝、繼承和多態以及類方法等習題
    習題總結:本文的習題繼續面向對象編程的知識點,包括面向對象的三大特徵,圍繞類屬性和實例屬性,還有類的方法和靜態方法。通過本章內容的學習複習,大家對面向對象應該會有比較深入的了解了。為以後的開發奠定好紮實的面向對象編程思想。如果有需要答案的小夥伴依舊是轉發後評論獲取。
  • Java中的多態有哪些具體表現形式
    學習Java語言的過程中,對於多態的理解是非常關鍵的,理解了多態也就意味著打開了理解Java各種「抽象」的大門。所謂的「多態」,簡單的理解就是對象在不同情況下的不同表現,具體體現在定義和功能兩個方面,簡單的總結一下,多態可以用「三個定義和兩個方法」來總結。三個定義分別是父類定義子類構建、接口定義實現類構建和抽象類定義實體類構建,而兩個方法分別是方法重載和方法重寫。
  • 淺析 Python 的類、繼承和多態
    繼承舉一個教科書中最常見的例子。Circle 和 Rectangle 繼承自 Shape,不同的圖形,面積(area)計算方式不同。Python 以最少的新的語法和語義實現了類機制,這一點確實讓人驚嘆,但是也讓 C++ / Java 程式設計師感到頗為不適。多態如前所述,Python 沒有覆寫(override)的概念。嚴格來講,Python 並不支持「多態」。
  • Java面試總結之Java基礎
    封裝表面上看就把描述一個對象的屬性和行為封裝成一個類,把業務邏輯封裝成方法,封裝的另一層含義在於通過修飾符控制類的訪問屬性(公有,私有等)。繼承主要是實現了代碼的復用,所有子類公用的行為和屬性可以抽取為一個父類,所有子類繼承了父類的屬性和行為,java中繼承是單一性的。
  • c++是如何實現多繼承帶來的多態問題?
    多態是指同樣的消息被不同類型的對象接收時導致完全不同的的行為。有虛函數的類才能叫多態類型的類,可以從探索虛函數是如何實現動態綁定的來了解如何實現多繼承中的多態。單繼承時虛函數動態綁定的實現原理每個類各有一個虛表(虛函數表),虛表的內容是由編譯器安排的。