摘要:多態的作用是消除類型之間的耦合關系。編寫構造器準則用盡可能簡單的方法使對象進入正常狀態,如果可以的話,避免調用其他方法。
點擊進入我的博客
在面向對象的程序設計語言中,多態是繼數據抽象(封裝)和繼承之后的第三種基本特征。
多態通過分離做什么和怎么做,從另一角度將接口和實現分離開來。
多態的作用是消除類型之間的耦合關系。
對象既可以作為它自己的本類使用,也可以作為它的基類使用。
8.1.1 忘記對象類型我們只寫一個簡單的方法,它接受基類作為參數,而不是那些特殊的導出類。
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 轉機
func(Cycle cycle)接受一個Cycle引用,那么編譯器怎么才能知道這個Cycle引用指的是哪個具體對象呢?實際上,編譯器并不知道。
8.2.1 方法調用綁定綁定:講一個方法調用同一個方法主體關聯起來被稱作綁定。
前期綁定:程序執行前進行綁定(由編譯器和連接程序實現)叫做前期綁定。
后期綁定(動態綁定、運行時綁定):在運行時根據對象的類型進行綁定。
Java中除了static方法和final方法(private方法屬于final方法)之外,其他所有的方法都是后期綁定。
在講解final關鍵字的時候講到final關鍵字曾經可以提高運行效率,原因就在于它可以關閉動態綁定,必須前期綁定。
8.2.2 產生正確的行為在編譯時,編譯器不需要獲得任何特殊信息就能進行正確的調用。
Cycle cycle = new Tricycle(); cycle.ride();8.2.3 可擴展性
一個良好的OOP程序中,大多數或所有方法都會遵循基類的模型,而且只與基類接口通信。
這樣的程序是可擴展的,因為可以從通用的基類繼承出新的數據類型。
多態是一項讓程序員“將改變的事物與未變的事物分離開來”的重要技術。
父類的私有方法子類是無法重載的,即子類的方法是一個全新的方法
只有非private的方法才能被覆蓋
下述程序調用的依然是父類的對應方法
約定:子類中的方法不能和父類中的private方法同名,能用起個名字解決的問題不要搞得那么復雜
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 缺陷:域和靜態方法
只有普通方法的調用是多態的
當子類對象轉型為父類對象時,任何域訪問操作都由編譯器解析,因此不是多態的
如果某個方法是靜態的,那么他就不是多態的
8.3 構造器和多態構造器不具有多態性,因為它們也是隱式聲明為static的
8.3.1 構造器的調用順序基類的構造器總是在導出類的構造過程中被調用,而且按照繼承層次逐漸想和那個鏈接,以便每個基類的構造器都能得到調用。
因為只有基類的構造器才有恰當的方法和權限來初始化自己的元素,所以必須令所有構造器都得到調用,這樣才能正確的構造對象。
沒有明確指定基類構造器,就是調用默認構造器
調用基類構造器(從根構造器開始)
按聲明順序調用成員的初始化方法
調用導出類的構造器
8.3.2 繼承與清理通過組合和繼承方法來創建新類時,永遠不必擔心對象的清理問題,子對象通常會留給GC進行處理。
如果確實遇到清理的問題,在清理方法中要先寫子類的清理邏輯,然后調用父類的清理方法;即清理順序應該和初始化順序相反。
8.3.3 構造器內部的多態方法的行為如果在構造器的內部調用正在構造的對象的某個動態綁定方法,會發生什么情況?
在其他任何事情發生之前,將分配給對象的存儲空間初始化成二進制的零。
如8.3.1中那樣調用基類構造器。因為在基類構造器中調用了func(),其實是被覆蓋的func()方法。
按照聲明的順序調用成員的初始化方法。
調用導出類的構造器主體。
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
用盡可能簡單的方法使對象進入正常狀態,如果可以的話,避免調用其他方法。
在構造器中唯一能夠安全調用的是基類中的final或private方法,因為這些方法不會被覆蓋。上述代碼中把Parent中的func()變成private的會得到不一樣的結果。
8.4 協變返回類型子類覆蓋(重寫)父類的方法時,可以返回父類返回類型的子類。
這是JSE 5之后增加的功能,如下所示。Child中的func()返回的是父類返回類型List的子類ArrayList。
class Child extends Parent { @Override ArrayList func() { return null; } } class Parent { List func() { return null; } }8.5 用繼承進行設計
準則:用繼承表達行為間的差異,用字段表達狀態上的變化。
8.5.1 純繼承與擴展只有在基類已經建立的方法才可以在導出類中被覆蓋,純粹的“is-a”的關系。
由extends關鍵詞的意思可以看出,仿佛是希望我們在基類的基礎上擴展功能,即增加基類中不存在的方法,這可以稱為“”is-like-a“”的關系。
這樣的缺點就是擴展部分不能被基類訪問,主要是在向上轉型的時候。
8.5.2 向下轉型與運行時類型識別(RTTI)向上轉型是安全的,因為基類不會具有大于導出類的接口。
向下轉型時會有運行時類型識別(Run-Time Type Identification)機制對類型進行檢查,如果發現轉型失敗,會拋出一個運行時異常(ClassCastException)。
RTTI的內容不僅包括轉型處理,還可以查看對象類型。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72199.html
摘要:當我們對一些函數發出調用的消息時,這些函數會返回不同的執行結果,這是多態性的一種體現,也是很多設計模式在中可以用高階函數來代替實現的原因。 PS:上一篇文章發表之后,很多朋友關注了本人的思否和掘金的博客,雖然關注的朋友還有有限,但足夠讓我把自己在技術上的問題積累分享給大家,也希望大家能夠喜歡,同時能動一動手指,給一顆心(贊),博主會持續更新下去 多態 本文是《javascript設計模...
摘要:接口與類型信息關鍵字的一種重要目標就是允許程序員隔離構件,進而降低耦合性。如果你編寫接口,那么就可以實現這一目標,但是通過類型信息,這種耦合性還是會傳播出去接口并非是對解耦的一種無懈可擊的保障。 點擊進入我的博客 運行時類型信息使得你可以在運行時發現和使用類型信息,主要有兩種方式: 傳統的RTTI,它假定我們在編譯時已經知道了所有的類型; 反射機制,它允許我們在運行時發現和使用類的...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
閱讀 5075·2023-04-25 19:30
閱讀 2173·2023-04-25 15:09
閱讀 2618·2021-11-16 11:45
閱讀 2171·2021-11-15 18:07
閱讀 1458·2021-11-11 17:22
閱讀 2115·2021-11-04 16:06
閱讀 3576·2021-10-20 13:47
閱讀 3036·2021-09-22 16:03