項目中常常會用到java多態這個特性,以前只知道一些皮毛,如今發現本身對它並無一個系統的認識,想重新梳理下本身的基礎庫。java
看了java編程思想中對象導論,關於繼承的描述:java中的類型不只僅只是描述了做用於一個對象集合上的約束條件,同時還有與其餘類型的之間的關係。能夠建立一個基類來表示系統中某些對象的核心概念,從基類的基礎上導出其餘的類型,用以表示此核心能夠被實現的不一樣方式。所以咱們知道了類與類之間還有繼承的關係。編程
問題:那什麼是多態呢?多態跟繼承有什麼關係?函數
假設我編寫了一個基類,它有多個導出類, 每次因咱們調用導出類的方法而建立本身對象時,n多個導出類有可能建立n個導出類對象。編碼
java中多態給咱們帶來的好處是能夠不用建立具體的導出類對象,而是基類的對象,去調用各自的方法。這種情形在書中描述的是:在處理類型層次結構時,常常想把一個對象不當作它所屬的特定類型對待,而是當作基類對象對待。spa
例如:SAO是一個基類,AliSAO和WeixinSAO是其導出類,都有一個相同的hello方法設計
按照本身以前老套路就是:對象
AliSAO aliSAO = new AliSAO();blog
aliSAO .hello();繼承
... ...接口
若是有n個導出類,就會有n個不一樣類型的對象
... ...
如今把導出類的類型改爲基類,交由java這種多態,使得咱們能夠編寫出不依賴特定類型的編碼。
SAO aliSAO = new AliSAO();
SAO weixinSAO = new WeixinSAO();
aliSAO.hello()和weixinSAO.hello()
aliSAO和weixinSAO對象能夠統一用 SAO類型
這樣的代碼是受新添加代碼影響較小。
可是這樣子會引出一個問題:把導出類對象當作泛化的基類類型對象,此對象自己怎麼肯定去執行正確的方法呢?
一個非面向對象編程的編譯器產生的函數調用會引發所謂的前期綁定,編譯器將產生一個具體的函數名稱的調用,運行時這個調用會解析到具體代碼的絕對地址上.而不會出現像java這種泛化對象的不肯定性方法調用。
若是是private、static、final 方法或者是構造器,則編譯器明確地知道要調用哪兒個方法,這種調用方式成爲「靜態調用」.動態綁定只是針對對象的非private,static,final方法.
而面向對象編程中,這種情形在編譯期間是沒法肯定具體調用那個方法的,SAO類型對象(aliSAO和weixinSAO )在沒有運行時不能肯定它具體類型(有人以爲SAO aliSAO = new AliSAO();不就能知道是AliSAO類型泛化的,此時是在編譯期間,對象都沒有建立,虛擬機根本不知道,比如你本身內心想的本身很清楚,你想讓別人知道,首先你得說出來,這個說出來就是運行),hello()方法都不知道調用本身的SAO對象是基類仍是導出類。
爲了解決這個問題面向對象程序設計語言提出了後期綁定的概念:java使用一小段代碼代替了絕對地址調用,這段代碼使用了對象中存儲的信息來計算方法的地址.
這樣一來,每一個對象根據這段代碼的內容,能夠具備不一樣的行爲表現,當向一個對象發送消息時,該對象就可以作出相應的應答.
上面說的都是實例方法,若是是類的靜態屬性和靜態方法可否繼承?
答案是能夠的.只是這種繼承有個特性,叫隱藏.當子類屬性和方法跟父類相同時,會出現隱藏現象.以下圖:
java的多態之因此能現實是依賴於父子類繼承,接口實現,重寫和重載.有了繼承,能夠經過子類對象指向父類引用,有了重寫,能夠經過父類引用的子類對象訪問子類重寫的方法,而不是父類的方法,這是由於「重寫」後子類的方法優先級要高於父類的優先級.而隱藏卻沒有這個屬性,所以在如圖中經過Parent ps = new Son();ps.getName()是非靜態方法,調用的是子類重寫的方法,ps.getAge()是靜態方法,只能調用父類的方法!
切記上面說的導出類繼承基類方法的覆蓋後,導出類的非靜態的方法優先級要高,若是是屬性呢?
若是注意到了:
Parent ps = new Son();
System.out.println(ps.age);// 返回父類的age=30
System.out.println(ps.money);// 返回父類的money=500
System.err.println(ps.name);// 'jack'
其中age和money在基類和導出類中都是靜態變量.而name是非靜態變量.
就會知道基類與導出類的屬性覆蓋是沒有優先級的,獲取的都是基類的屬性值.它跟具體對象類型是不要緊的,只跟對象引用類型有關!
重寫只是發生在父子類繼承的的方法中,屬性是沒有重寫這一說法的.當導出類有和基類相同 名稱的屬性時(甚至類型均可以不一樣)基類的屬性會被隱藏
對於子類來講,父類的屬性是不能被子類對象引用訪問到的,而須要經過其父類對象的引用訪問;一般來講,咱們不建議隱藏屬性,由於這會使代碼不易閱讀;
從以上定義能夠看出,成員屬性不能像方法那樣被重寫,當子類定義了一個和其父類相同名字的成員屬性,子類僅僅是聲明瞭一個新的屬性,而其父類的屬性被隱藏起來,這不是重寫,實際上用super.屬性名,仍是能夠獲得父類的非private屬性,因此不能以多態的形式訪問。
用一句話總結:在Java中,屬性綁定到類型,方法綁定到對象!
用本身的話理解隱藏就是:當導出類中有與基類中屬性相同名稱或者private,static,final或構造方法等簽名(方法名稱+方法參數)相同時,注意前提條件是二者有繼承關係且基類引用是由導出類對象實例化,若是不是有導出類對象實例化基類類型,則不會出現此狀況.隱藏至關於導出類的實例對象去掉了基類的屬性和方法,以下圖,即對象不具有這種能力了,只能去訪問基類對應的屬性或方法!
效果相似:
向上轉型:將導出類當作是基類的過程就是向上轉型,前面提到的SAO aliSAO = new AliSAO();便是向上轉型,其實際對象是AliSAO導出類的對象,引用的倒是是基類.
只能導出類對象向上轉型基類對象,反之則不能(若是基類對象能轉型成導出類對象,會出現該對象丟失導出類的行爲或屬性,畢竟導出類的屬性和方法要等於或多於基類).