面向對象開發有三大特性(特點 / 特征) : 封裝, 繼承, 多態。我們今天主要討論封裝和繼承,多態會在下篇中討論。 一、封裝: 所謂封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。封裝是面向對象的特征之一,是對象和類概念的主要特性 ...
面向對象開發有三大特性(特點 / 特征) : 封裝, 繼承, 多態。我們今天主要討論封裝和繼承,多態會在下篇中討論。
一、封裝:
所謂封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。封裝是面向對象的特征之一,是對象和類概念的主要特性。簡單的說,一個類就是一個封裝了數據以及操作這些數據的代碼的邏輯實體。在一個對象內部,某些代碼或某些數據可以是私有的,不能被外界訪問。通過這種方式,對象對內部數據提供了不同級別的保護,以防止程式中無關的部分意外的改變或錯誤的使用了對象的私有部分。
1.1、封裝五種訪問修飾符
A.public [公開訪問]
公開的訪問許可權。
當前類, 子類, 實例對象, 都可以訪問到。
B.private [私有訪問]
私有的訪問許可權。
只能在當前類內部進行訪問使用; 子類, 實例對象, 都訪問不到。
C.protected [保護訪問]
受保護的訪問許可權。
只能在當前類的內部,以及該類的子類中訪問;實例對象訪問不到。
也就是說受保護的,不讓外部的實例去訪問和改變。
D.internal [內部訪問]
只能在當前程式集(項目)中訪問;
(程式集可以理解為一個項目,一個項目也就是一個程式集。從設計的角度來說,也可以看成是一個完整的模塊(Module),或者稱為是包(Package)。因此,一個程式集也可以體現為一個dll文件,或者exe文件。一個解決方案下可以用很多項目,項目名和命名空間名可以分別設置,但預設情況下是一致的,而對應到項目的具體文件夾就是“項目的命名空間.文件夾名
在同一個項目中 internal 和 public 的訪問許可權是一樣的。
一個解決方案裡面,可以有多個項目!
E.protected internal [內部保護訪問]
protected + internal 的訪問許可權。
用的不多,我是沒用過的。
1.2、使用場合
A.修飾類
能夠修飾類的訪問修飾符只有兩個, public 和 internal;
類的預設訪問修飾符是 internal。預設類就是那種前面沒有修飾符的class
B.修飾類成員
五種訪問修飾符都可以修飾類中的成員;
類中的成員預設訪問修飾符是 private。
C.類視圖
當前項目上右鍵-->視圖-->查看類圖
可以看到類的繼承關係圖, 以及類中的成員概況。
各位留意一下欄位,屬性,方法各自特有的圖標。
欄位是小磚塊,還上鎖了。屬性是小扳手。方法是空的小盒子。
下麵是類視圖。
二、繼承:
將一堆類中的一些共有的“成員”單獨抽取出來,作為一個父類,然後這一堆類繼承這個父類,共用父類的資源, 這就叫做繼承。
以為人為例,我們每個人都會有一些共同的特征,都要做一些相同的事情。比如:人都有一個腦袋,二只胳膊,二條腳,這是共同的特征。都要吃飯,喝水,這是都要做的相同的事情。那麼如果我們現在要聲明很多個人,每個人都會有這些特征,那我不是要寫很多重覆的代碼?所以我們可以先建一個人的父類,這個父類不代表一個具體的人,只是一個擁有人所有共同特性的虛擬的人。下次我 們要實例化一個具體的人的時候,只需要將這個人繼承上面這個“虛擬的人”,那麼他就擁有了人的所有共同特性。這樣,這些重覆的代碼我們就可以不寫了。
當然,繼承的目標不僅僅是為了節省代碼,還有實現後面的多態的功能。初學者只需要瞭解繼承可以少寫很多代碼就好了,餘下的需要在項目中慢慢理解。
書面的解釋如下:對象的繼承代表一種"is-a"的關係,假如兩個對象A和B,如果可以描述為"B就是A",那麼則表示B可以繼承自A。
註意: 如果A繼承了B,那麼A不僅擁有了B除私有的特性外的所有特性,A還可以擁有自己獨特的特性。比如上面人的這個例子,一個繼承了 “虛擬的人”,那麼他除了有一個腦袋,二只胳膊,二條腳,要吃飯喝水外,他可能還會編程。編程就是他獨特的特性了,因為不是每個人都會編程的。
2.1、繼承的好處
①優化代碼結構, 讓類與類之間產生關係(人不是孤立存在的, 類也一樣);
②提高代碼的復用性, 便於閱讀(寫更少的代碼, 乾更多的活);
③為“ 多態” 提供前提(多態是面向對象最複雜的一個特性, 後面重點講解)。
2.2、生活中的例子
以電視劇舉例, 見圖。
2.3、方法
(1)、延續:父類存在,子類沒有重寫但可以使用;
(2)、新增:父類沒有,子類新增加的
(3)、重寫:父類子類都存在,但是父類不滿足要求,子類對其進行從新定義;
2.4、實例化:
(1)、先父類後子類,先靜態後成員;
實例化的時候先調用父類的靜態構造快,在調用父類的構造方法,然後子類的構造塊,在調用子類的構造方法;
(有繼承關係的幾個類中,構造函數是由上至下調用的,即首先調用基類的構造函數。父親會的,兒子也會,不先把父親弄出來,兒子何來會?
(2)、預設調用父類空構造;
(3)、那麼子類如何繼承父類的構造方法呢,要按如下寫法
編寫父類的構造方法
演示: 在 Hero 類中創建構造方法, 用於初始化父類中的成員。
public Hero(string m_heroName, string m_heroInfo, int m_attack, int m_defense, int m_speed, string m_nickName) { this.heroName = m_heroName; this.heroInfo = m_heroInfo; this.attack = m_attack; this.defense = m_defense; this.speed = m_speed; this.nickName = m_nickName; }
編寫子類的構造方法
演示: 在各個子類中編寫各自的構造方法, 使用 base 關鍵字傳值給父類。
關鍵字 base,代表父類;
關鍵字 this,代表當前類。
public LuKaShi() { } public LuKaShi(string m_heroName, string m_heroInfo, int m_attack, int m_defense, int m_speed, string m_nickName) : base(m_heroName, m_heroInfo, m_attack, m_defense, m_speed, m_nickName) { }
這裡他不做任何處理,直接扔給父類。
(4)、繼承之成員繼承:構造方法
構造方法可以使用 private 和 public 進行修飾。但 99%的時候使用 public 修飾, 只有在使用單例模式的時候才使用 private 修飾。
private 修飾的構造方法, 我們在子類中訪問不到;
public 修飾的構造方法, 我們在子類中可以訪問到, 使用 base()。
2.5、繼承的堆棧關係
父類在記憶體是實際存在的。
2.6、重寫override
2.6.1 重寫與重載:
重寫override:繼承的子類中,方法簽名相同( 方法名+形參個數 類型 順序 )
重載overload:同一個類方法名相同,形參個數 類型 順序 不同(只要有一項不同,即可構成重載)
2.6.2 重寫規則:在子類中簽名與父類中相同,在合理範圍內提高子類可見性;
這裡沒總結好,先學再總結下
A、父類必須有公共(public)或受保護(protected)的虛方法(virtual);
返回類型:基本類型和void必須相同;引用類型要<=父類的返回類
在派生類里使用override關鍵字來重寫父類的虛方法。
B、抽象方法,介面,標記為virtual的方法可以被重寫(override),實方法不可以。
C、可見性:要大於或等於父類中被重寫的方法(重寫方法的訪問修飾符一定要大於被重寫方法的訪問修飾符(public>protected>default>private))
D、重寫和覆蓋的區別