摘要:多態(tài)的作用是消除類型之間的耦合關(guān)系。編寫構(gòu)造器準(zhǔn)則用盡可能簡單的方法使對象進(jìn)入正常狀態(tài),如果可以的話,避免調(diào)用其他方法。
點(diǎn)擊進(jìn)入我的博客
在面向?qū)ο蟮某绦蛟O(shè)計(jì)語言中,多態(tài)是繼數(shù)據(jù)抽象(封裝)和繼承之后的第三種基本特征。
多態(tài)通過分離做什么和怎么做,從另一角度將接口和實(shí)現(xiàn)分離開來。
多態(tài)的作用是消除類型之間的耦合關(guān)系。
對象既可以作為它自己的本類使用,也可以作為它的基類使用。
8.1.1 忘記對象類型我們只寫一個簡單的方法,它接受基類作為參數(shù),而不是那些特殊的導(dǎo)出類。
public class Test { public static void main(String[] args) { func(new Unicycle()); func(new Bicycle()); func(new Tricycle()); } public static void func(Cycle cycle) { cycle.ride(); } } class Cycle { void ride() {} } class Unicycle extends Cycle { void ride() { System.out.println("Unicycle"); } } class Bicycle extends Cycle { void ride() { System.out.println("Bicycle"); } } class Tricycle extends Cycle { void ride() { System.out.println("Tricycle"); } }8.2 轉(zhuǎn)機(jī)
func(Cycle cycle)接受一個Cycle引用,那么編譯器怎么才能知道這個Cycle引用指的是哪個具體對象呢?實(shí)際上,編譯器并不知道。
8.2.1 方法調(diào)用綁定綁定:講一個方法調(diào)用同一個方法主體關(guān)聯(lián)起來被稱作綁定。
前期綁定:程序執(zhí)行前進(jìn)行綁定(由編譯器和連接程序?qū)崿F(xiàn))叫做前期綁定。
后期綁定(動態(tài)綁定、運(yùn)行時綁定):在運(yùn)行時根據(jù)對象的類型進(jìn)行綁定。
Java中除了static方法和final方法(private方法屬于final方法)之外,其他所有的方法都是后期綁定。
在講解final關(guān)鍵字的時候講到final關(guān)鍵字曾經(jīng)可以提高運(yùn)行效率,原因就在于它可以關(guān)閉動態(tài)綁定,必須前期綁定。
8.2.2 產(chǎn)生正確的行為在編譯時,編譯器不需要獲得任何特殊信息就能進(jìn)行正確的調(diào)用。
Cycle cycle = new Tricycle(); cycle.ride();8.2.3 可擴(kuò)展性
一個良好的OOP程序中,大多數(shù)或所有方法都會遵循基類的模型,而且只與基類接口通信。
這樣的程序是可擴(kuò)展的,因?yàn)榭梢詮耐ㄓ玫幕惱^承出新的數(shù)據(jù)類型。
多態(tài)是一項(xiàng)讓程序員“將改變的事物與未變的事物分離開來”的重要技術(shù)。
父類的私有方法子類是無法重載的,即子類的方法是一個全新的方法
只有非private的方法才能被覆蓋
下述程序調(diào)用的依然是父類的對應(yīng)方法
約定:子類中的方法不能和父類中的private方法同名,能用起個名字解決的問題不要搞得那么復(fù)雜
public class Test { public static void main(String[] args) { Test test = new TestDemo(); test.func(); // Output: Test } private void func() { System.out.println("Test"); } } class TestDemo extends Test { public void func() { System.out.println("TestDemo"); } }8.2.5 缺陷:域和靜態(tài)方法
只有普通方法的調(diào)用是多態(tài)的
當(dāng)子類對象轉(zhuǎn)型為父類對象時,任何域訪問操作都由編譯器解析,因此不是多態(tài)的
如果某個方法是靜態(tài)的,那么他就不是多態(tài)的
8.3 構(gòu)造器和多態(tài)構(gòu)造器不具有多態(tài)性,因?yàn)樗鼈円彩请[式聲明為static的
8.3.1 構(gòu)造器的調(diào)用順序基類的構(gòu)造器總是在導(dǎo)出類的構(gòu)造過程中被調(diào)用,而且按照繼承層次逐漸想和那個鏈接,以便每個基類的構(gòu)造器都能得到調(diào)用。
因?yàn)橹挥谢惖臉?gòu)造器才有恰當(dāng)?shù)姆椒ê蜋?quán)限來初始化自己的元素,所以必須令所有構(gòu)造器都得到調(diào)用,這樣才能正確的構(gòu)造對象。
沒有明確指定基類構(gòu)造器,就是調(diào)用默認(rèn)構(gòu)造器
調(diào)用基類構(gòu)造器(從根構(gòu)造器開始)
按聲明順序調(diào)用成員的初始化方法
調(diào)用導(dǎo)出類的構(gòu)造器
8.3.2 繼承與清理通過組合和繼承方法來創(chuàng)建新類時,永遠(yuǎn)不必?fù)?dān)心對象的清理問題,子對象通常會留給GC進(jìn)行處理。
如果確實(shí)遇到清理的問題,在清理方法中要先寫子類的清理邏輯,然后調(diào)用父類的清理方法;即清理順序應(yīng)該和初始化順序相反。
8.3.3 構(gòu)造器內(nèi)部的多態(tài)方法的行為如果在構(gòu)造器的內(nèi)部調(diào)用正在構(gòu)造的對象的某個動態(tài)綁定方法,會發(fā)生什么情況?
在其他任何事情發(fā)生之前,將分配給對象的存儲空間初始化成二進(jìn)制的零。
如8.3.1中那樣調(diào)用基類構(gòu)造器。因?yàn)樵诨悩?gòu)造器中調(diào)用了func(),其實(shí)是被覆蓋的func()方法。
按照聲明的順序調(diào)用成員的初始化方法。
調(diào)用導(dǎo)出類的構(gòu)造器主體。
public class Test { public static void main(String[] args) { new Child(100); } } class Child extends Parent { private int i; void func() { System.out.println("Child func, i = " + i); } public Child(int i) { this.i = i; System.out.println("Before Child constructor, i = " + i); func(); System.out.println("After Child constructor, i = " + i); } } class Parent { void func() { System.out.println("Parent func"); } public Parent() { System.out.println("Before Parent constructor"); func(); System.out.println("After Parent constructor"); } } Output: Before Parent constructor Child func, i = 0 After Parent constructor Before Child constructor, i = 100 Child func, i = 100 After Child constructor, i = 100
用盡可能簡單的方法使對象進(jìn)入正常狀態(tài),如果可以的話,避免調(diào)用其他方法。
在構(gòu)造器中唯一能夠安全調(diào)用的是基類中的final或private方法,因?yàn)檫@些方法不會被覆蓋。上述代碼中把Parent中的func()變成private的會得到不一樣的結(jié)果。
8.4 協(xié)變返回類型子類覆蓋(重寫)父類的方法時,可以返回父類返回類型的子類。
這是JSE 5之后增加的功能,如下所示。Child中的func()返回的是父類返回類型List的子類ArrayList。
class Child extends Parent { @Override ArrayList func() { return null; } } class Parent { List func() { return null; } }8.5 用繼承進(jìn)行設(shè)計(jì)
準(zhǔn)則:用繼承表達(dá)行為間的差異,用字段表達(dá)狀態(tài)上的變化。
8.5.1 純繼承與擴(kuò)展只有在基類已經(jīng)建立的方法才可以在導(dǎo)出類中被覆蓋,純粹的“is-a”的關(guān)系。
由extends關(guān)鍵詞的意思可以看出,仿佛是希望我們在基類的基礎(chǔ)上擴(kuò)展功能,即增加基類中不存在的方法,這可以稱為“”is-like-a“”的關(guān)系。
這樣的缺點(diǎn)就是擴(kuò)展部分不能被基類訪問,主要是在向上轉(zhuǎn)型的時候。
8.5.2 向下轉(zhuǎn)型與運(yùn)行時類型識別(RTTI)向上轉(zhuǎn)型是安全的,因?yàn)榛惒粫哂写笥趯?dǎo)出類的接口。
向下轉(zhuǎn)型時會有運(yùn)行時類型識別(Run-Time Type Identification)機(jī)制對類型進(jìn)行檢查,如果發(fā)現(xiàn)轉(zhuǎn)型失敗,會拋出一個運(yùn)行時異常(ClassCastException)。
RTTI的內(nèi)容不僅包括轉(zhuǎn)型處理,還可以查看對象類型。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/72199.html
摘要:當(dāng)我們對一些函數(shù)發(fā)出調(diào)用的消息時,這些函數(shù)會返回不同的執(zhí)行結(jié)果,這是多態(tài)性的一種體現(xiàn),也是很多設(shè)計(jì)模式在中可以用高階函數(shù)來代替實(shí)現(xiàn)的原因。 PS:上一篇文章發(fā)表之后,很多朋友關(guān)注了本人的思否和掘金的博客,雖然關(guān)注的朋友還有有限,但足夠讓我把自己在技術(shù)上的問題積累分享給大家,也希望大家能夠喜歡,同時能動一動手指,給一顆心(贊),博主會持續(xù)更新下去 多態(tài) 本文是《javascript設(shè)計(jì)模...
摘要:接口與類型信息關(guān)鍵字的一種重要目標(biāo)就是允許程序員隔離構(gòu)件,進(jìn)而降低耦合性。如果你編寫接口,那么就可以實(shí)現(xiàn)這一目標(biāo),但是通過類型信息,這種耦合性還是會傳播出去接口并非是對解耦的一種無懈可擊的保障。 點(diǎn)擊進(jìn)入我的博客 運(yùn)行時類型信息使得你可以在運(yùn)行時發(fā)現(xiàn)和使用類型信息,主要有兩種方式: 傳統(tǒng)的RTTI,它假定我們在編譯時已經(jīng)知道了所有的類型; 反射機(jī)制,它允許我們在運(yùn)行時發(fā)現(xiàn)和使用類的...
摘要:很多情況下,通常一個人類,即創(chuàng)建了一個具體的對象。對象就是數(shù)據(jù),對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍(lán)圖或原型。在中,對象通過對類的實(shí)體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實(shí)例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創(chuàng)建了一個具體的對象。對象就是數(shù)據(jù),對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍(lán)圖或原型。在中,對象通過對類的實(shí)體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實(shí)例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創(chuàng)建了一個具體的對象。對象就是數(shù)據(jù),對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍(lán)圖或原型。在中,對象通過對類的實(shí)體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實(shí)例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
閱讀 5394·2023-04-25 19:30
閱讀 2260·2023-04-25 15:09
閱讀 2696·2021-11-16 11:45
閱讀 2275·2021-11-15 18:07
閱讀 1526·2021-11-11 17:22
閱讀 2187·2021-11-04 16:06
閱讀 3640·2021-10-20 13:47
閱讀 3090·2021-09-22 16:03