-
OOP定義
1 一切都是對象。
2 計算通過對象間互相通信,請求其他對象執行動作來實作。對象間通過發送和接收消息來通信。
3 每個對象都有自己的記憶體,其中可能包括了其他的對象。
4 每一個對象都是某個某個類的執行個體。類就是一組相似的對象。
5 類是對象相關行為的存儲庫。也就是說,同一個類的所有對象都能執行同樣的動作。
6 類被組織成有單個根節點的樹狀結構,被稱為繼承層次結構。與類執行個體相關的記憶體和行為都會被樹結構中的後代自動繼承。
-
面向對象基本概念
1 任何事物都是對象,對象有屬性和方法。複雜對象可以由相對簡單的對象以某種方式構成。
2 通過類比發現對象間的相似性,即對象間的共同屬性,是構成對象類的依據。
3 對象見的互相聯系是通過傳遞“消息”來完成的。通過對象之間的消息通信驅動對象執行一系列的操作進而完成某一任務。
- 面向對象主要特點:類、對象、繼承、封裝、聚合、關聯、消息、多态
對象
-
對象是獨立存在的客觀事物,它由一組屬性和一組操作構成。
屬性和操作是對象的兩大要素。屬性是對象靜态特征的描述,操作是對象動态特征的描述。
屬性一般隻能通過執行對象的操作來改變。
操作又稱為方法或服務,它描述了對象執行的功能。通過消息傳遞,還可以為其他對象使用。
-
對象是指一個實體:能夠儲存一個狀态(又稱資訊或資料);能提供一系列操作(或稱行為),這些操作或能檢查或影響對象的狀态
能夠表示現實或抽象的事物:具有良好定義的責任和良好定義的行為;具有良好定義的接口
- 對象的性質:封裝性、自治性、通信性、暫存性、持久性
-
對象辨別(OID)
是将一個對象和其他對象加以差別的辨別符
一個對象辨別和對象永久結合在一起,不管這個對象狀态如何變化,一直到該對象消亡為止
用變量名充當辨別
可尋址性和辨別這兩個概念做了混合
強類型變量,像C++,Java Employ emp = new Employ();
非強類型的變量 var emp =new Employ()
直接辨別就是變量的值即為要辨別的對象
間接辨別指變量的值不是要辨別的對象而是該對象的指針
-
複合對象
指一個對象的一個屬性或多個屬性引用了其他對象
委托:是複合對象的一個特例,在委托方式下可有兩個對象參與處理的一個請求,接受請求的對象将責任委托給它的代理者
組合:一個對象可以由其他對象構造
聚合:描述對象間具有互相關系的另一種方式
組合和聚合的差別:
-
組合:
語義規則: a-part-of
整體負責部分
每個部分對象也僅與一個整體對象聯系
是更強形式的聚合
-
聚合:
同質的
語義規則:has-a
部分可以脫離整體存在,例如攝影協會和會員的關系
-
-
對象持久化
要長久儲存的對象,也就是持久對象。持久對象不随着建立它的程序結束而消亡,在外村中存貯
不需要長期儲存的,被稱為暫存對象
類
-
類就是這些具有相同或相似行為或資料結構的對象的共同描述
類是若幹對象的模闆,并且能夠描述這些對象内部的構造
屬于同一個類的對象具有相同資料結構及行為
-
類與對象的關系:
對象按照不同的性質劃分為不同的類
同類對象在資料和操作性質方面具有共性
把一組對象的共同特性加以抽象并存貯在一個類中
類是對象之上的抽象,有了類之後,對象則是類的具體化,是類的執行個體(類是對象的抽象,對象是類的執行個體)
類是靜态概念,對象是動态概念
-
類的性質:
類的名辨別一個類
在同一個系統環境中,類的名能夠唯一辨別一個類
類必須有一個成員集合:屬性、方法、方法的操作接口
類的屬性的域:基本類、使用者定義的類
支援資訊隐藏
-
類的執行個體
一個執行個體是從一個類建立而來的對象
屬于某個類的對象稱為該類的一個執行個體,每個執行個體具有一個對象辨別
類和對象間有instance-of關系
類描述了這個執行個體的行為(方法)及結構(屬性)
每個執行個體可由該類上定義的操作(方法)來操縱
-
類及執行個體的特征
同一個類的不同執行個體:
- 具有相同的值
-
承受的是同一方法集合所定義的操作,因而具有相同的行為
同一個類的不同執行個體可以持有不同的值,因而可以有不同的狀态
執行個體的初始狀态(初值)可以在執行個體化中确定
-
類的資料字段/類屬性:被一個類的所有執行個體共享的公共資料字段
靜态資料字段的初始化實在加載類時,執行靜态塊完成
靜态成員函數
- 不能通路非靜态成員
-
無this/不能使用this引用
構造和析構函數不能為靜态成員
Java中的類方法和類變量
- Java同時也包含無對象(objectless)變量及無對象方法,稱為類變量和類方法
- 類變量的典型使用方式是定義常量
- 類方法可以被視為可獨立于某類的所有對象而進行調用的方法,而非傳遞給該類的對象的消息
-
類的使用
類的使用有兩種形式:允引、繼承
-
對象的建立
對象數組的建立:數組的配置設定和建立、數組所包含對象的配置設定和建立
Java:new僅建立數組,數組包含的對象必須獨立建立
-
構造函數【問老師】
初始化新建立對象
優點:確定初始化之前不會被使用,防多次調用
Java/C++:名稱,傳回值
構造函數重載
-
const和final差別
const 常量,不允許改變
final 僅斷言相關變量不會賦予新值、并不能阻止在對象内部對變量值進行改變(如set方法)
-
析構函數
C++,在記憶體中開始釋放對象時自動調用
-
記憶體回收
使用new建立–堆記憶體
- 堆記憶體沒有綁定在過程的入口和出口處
-
記憶體有限
記憶體回收的方式:
1 在程式中顯式指定不再使用的對象,将其使用記憶體回收
C++:delete
Object Pascal:free
2 垃圾回收機制(Java\C#\Smaltalk)
時刻監控對象的操作,對象不再使用時,自動回收其所占記憶體
通常在記憶體将要耗盡時工作:方法:1確定動态配置設定的記憶體對象都有一個指定的屬主; 2 引用計數:引用共享對象的指針的計數值
-
對象結構
簡單類型的域與引用類型的域(????)
對象同一:具有相同的辨別
對象相等:兩個對象的辨別不同,但具有相同的值
-
克隆(感覺應該是很重要的)
記憶體布局(三種)
1.最小靜态空間配置設定:隻配置設定基類所需的存儲空間
2.最大靜态空間配置設定:無論是基類還是子類,都配置設定可以用于所有合法地數值的最大的存儲空間
3.動态記憶體配置設定: 隻配置設定用于儲存一個指針所需的存儲空間(棧),在運作時通過堆來配置設定數值所需的存儲空間同時将指針設為相應的合适值(存儲空間的位址)
指派(兩種語義):複制、指針
複制和克隆:
當指向其他對象的變量值進行複制時,有兩種可能的方案:
1.淺複制: 與原來變量共享執行個體變量,即原有變量和複制産生的變量引用相同的變量值
2.深複制:建立執行個體變量的新的副本
Java中的克隆clone():一是拷貝對象傳回的是一個新對象,而不是一個引用;二是拷貝對象與用new操作符傳回的新對象的差別就是這個拷貝已經包含了一些對象的原始資訊而不是對象的初始資訊(對于非基本類型變量,儲存的僅僅是對象的引用,clone後的和原始對象的相應變量指向的是同一個對象,需要注意)
Java預設的clone方法是淺克隆,隻克隆對象的非引用類型成員。
如果要改成深克隆需要:①讓非基本類型變量的類實作Cloneable接口,重載clone()方法②外層為該非基本類型變量執行 clone()方法
-
類對象
類本身也是一個對象,這個特殊的對象也有其屬性和方法,稱之為類屬性和類方法,普通對象的屬性和方法稱作實力屬性和執行個體方法。
-
元類(metaclass)
描述類的類,如果将類看作一個對象,該類必定是另一個特殊類的執行個體,将這個特殊類稱之為元類
每個類一定是某個元類的執行個體,例如對于類A,它是元類A class 的執行個體
引入元類的優點: 隻用對象這一概念便可描述系統中的所有成分;使類成為運作時刻一部分,有助于改善程式設計環境;繼承的規範化,類與元類的繼承采用雙軌制
Class是特殊的類(類的類),具有以下行為:建立執行個體,傳回類名稱,傳回類執行個體大小,傳回類執行個體可識别消息清單
類對象:反射工具都開始于一個對象,該對象是關于一個類的動态(運作時)展現;類對象是更一般的類(Class類)的執行個體;類對象通常都包括類名稱、類執行個體所占用記憶體的大小以及建立新執行個體的能力
getClass()——擷取類對象;getSuperclass()——擷取父類;getName()、toString()——字元串類名稱;instanceof——檢測對象類
ClassLoader 類加載器,主要用于加載類檔案,利用反射(newInstance())生成類執行個體,如:
ClassLoader cl = this.getClass.getClassLoader();//獲得ClassLoader
Class cls = cl.loadClass(“com.rain.B);//使用第一步得到的ClassLoader來載入B
B b = (B)cls.newInstance();//有B的類得到一個B的執行個體
反射:反射支援一個組建動态地加載和查詢自身資訊,是以反射為許多基于組建的程式設計工具建立了基礎。如得到某個對象的屬性、靜态屬性、執行某對象的方法、靜态方法、建立執行個體、判斷是否為某個類的執行個體、得到數組中的某個元素等等
-
消息傳遞
在兩個實體間通信,其必要條件是在它們之間至少存在一條通道,并且遵守同一種通信協定
發送一條消息時,應指明信道或給出信道的決定方法,最常用的是用接收方的辨別(如名字)來命名信道:Send To
發送消息的流程:對象A向對象B發送消息,也可以看作對象A向對象B請求服務。對象A要明确知道對象B提供什麼樣的服務;根據請求服務的不同,對象A可能需要給對象B一些額外的資訊,以使對象B明确知道如何處理該服務;對象B也應該知道對象A是否希望它将最終的執行結果以報告形式回報回去
消息傳遞文法:消息->接收器;響應行為随接收器不同而不同
aGame.displayCard(aCard,42,27)
receiver selector arguments
僞變量this
this使用時好像在使用同類的執行個體,this隐含指向調用成員函數的對象,也可作為參數傳遞
類和繼承
-
繼承
繼承是一種使使用者得以在一個類的基礎上建立新的類的技術
新的類自動繼承舊類的屬性和行為特征,并可具備某些附加的特征或某些限制
新類稱作舊類的子類,舊類稱作新類的超類
繼承機制的強有力之處還在于它允許程式設計人員可重用一個未必完全符合要求的類,允許對該類進行修改而不至于在該類的其他部分引起有害的副作用
是其他語言所沒有的
- 繼承:在已有的類的基礎上建立新的類的方法。重複使用和擴充已有的、經過測試的類,實作重用;可以增強處理的一緻性,是一種規範和限制
-
作用:代碼複用;概念複用,共享方法的定義
可以用“Is-A”檢驗兩個概念是否為繼承關系
超類(superclass)和子類(subclass):已在的類通常稱作超類,新的類通常稱作子類,子類不僅可以繼承超類的方法,也可以繼承超類的屬性,如果超類中的某些方法不适合于子類,則可以重置這些方法
繼承有傳遞性
單繼承:如果一個類隻有一個直接超類則被稱為單繼承。單繼承構成類之間的關系是一棵樹
多繼承:如果一個類有多餘一個的直接超類則被稱為多繼承,多繼承構成的類之間的關系是一個網格(需要防止産生二義性如用es.Employee::print()和es.Studnet::Print()來區分兩個方法)
C++規定多繼承時直接超類的構造函數的調用次序是:①抽象超類。若有多個抽象超類,按繼承說明次序從左到右。②非抽象超類:若有多個非抽象超類,按繼承順序從左到右
-
泛化和特化:
泛化:通過抽取及共享共同特征,将這些共性抽取出作為超類放在繼承層次的上端,抽取出的超類叫做抽象類(抽象類沒有執行個體,不能建立執行個體,Bind)。
特化:新類作為舊類的子類
接口和抽象類:與類一樣,接口可以繼承于其他接口,甚至可以繼承于多個父接口。
- 抽象方法:介于類和接口之間的概念,定義方法但不實作。在建立執行個體前,子類必須實作父類的抽象方法
- 繼承的形式
- 特化子類化(子類型化) 新類是父類的一種特殊形式,但符合父類的各種規定,顯然支援替換原則
- 規範子類化 父類(抽象規範類)僅僅對行為進行定義,卻沒有對其實作,而由子類來實作這些行為
- 構造子類化 需要對一些對應于接口的方法名稱進行改變,或者以一種特定的方式對方法參數進行修改,經常違反替換原則。
- 泛化子類化 子類将父類的行為進行擴充,建立了一種更泛化的對象,通常用于打算基于已存在的類來建立一種不想修改或不能修改的類
- 擴充子類化 對對象增加新功能。和泛化子類化的差別在于,後者必須至少改寫來自父類的一個方法,且子類的功能需要與父類緊密聯系,而擴充子類化隻是對父類增加新的方法,且子類的功能與父類的聯系不那麼緊密
- 限制子類化 用于子類的行為少于或限制于弗雷。如将雙向隊列改為棧。限制子類化違反替換原則,通過它建立的子類不是子類型,應當盡量避免使用
- 變體子類化 在兩個或多個類需要實作類似的功能,但它們的抽象概念之間似乎不存在層次關系的情況下,可以使用變體子類化。
- 結合子類化 需要一個子類,可以表示兩個或更多的父類的特征的結合。(多重繼承)
-
替換原則:在靜态類型語言中父類和子類資料類型的關系
1.子類執行個體必須擁有父類的所有資料成員
2.子類的執行個體必須至少通過繼承實作父類所定義的所有功能。
3.這樣,在某種條件下,如果用子類執行個體來替換父類執行個體,那麼将會發現子類執行個體可以完全模拟父類的行為,二者毫無差異
在替換原則下,對于類A和類B,若B是A的子類,那麼在任何情況下都可以用類B來替換類A
可替換性:變量聲明時指定的類型不必與它所容納的值類型相一緻(傳統的程式設計語言不允許,OO的卻經常出現)
-
重置/改寫(見後面)
子類通過改寫,來避免繼承父類的行為
文法上,改寫需要子類定義一個與父類有相同名稱且類型簽名相同的方法
運作時,變量聲明為一個類,它所包含的值來自于子類,與給定消息相對應的方法同時出現于父類和子類。
改寫與替換結合時,想要執行的一般都是子類的方法
(重定義:操作的表示和操作的實作都将改變)
重置(改寫)時子類不改變超類中的已有接口定義,重置機制是基于動态綁定(動态聯編)的
-
Java中的實作繼承
實作繼承是面向對象程式設計的最重要特性之一(也被稱為子類化)。這個特性顯著地增強了類的可重用性并且減少了代碼的備援。
缺點:讀者要追溯執行流程變得非常困難,尤其是在一個具有很多世代的深度繼承樹之中,其樹底層的方法的代碼分散在高層的祖先當中;另一個問題就是所有子類都是和父類緊密聯系的。
如果某類的子類要正确地實作,就需要将該類的實作公開給子類的設計人員,然而這種公開并不總能得到滿足
指導原則:如果某個類具有一些隻對類的部分對象适用的行為,那麼不妨考慮将該類分裂為兩個互相關聯的類,兩個類之間要麼直接通過繼承關聯,要麼通過一個公共的抽象類或接口關聯。
可以用繼承、多态和動态方法調用來避免不優雅的條件判斷
-
軟體複用機制:繼承、組合
組合:提供了一種利用已存在的軟體元件來建立新的應用程式的方法。委托、非替換、語義A has-a B
繼承:通過繼承,新的類可以聲明為已存在的類的子類。通過這種方式,與初始類相關的所有資料字段和函數都自動地與新的資料抽象建立聯系。語義:A is-a B
組合和繼承的比較:
繼承:is-a關系
- 優點:子類可以重寫父類的方法來實作對父類的擴充;代碼簡潔性
-
缺點:①父類的内部細節對子類是可見的;②子類從父類繼承的方法在編譯時便已确定,是以無法再運作期間改變從父類繼承的方法的行為;③子類與父類是一種高耦合,如果對父類的方法做了修改的話(比如增加了一個參數),則子類的方法必須做出相應的修改。
組合:has-a 關系
設計類的時候要把組合的類的對象加入到該類中作為自己的成員變量
- 優點:目前對象隻能通過所包含的那個對象去調用其方法,所包含的對象的内部細節對目前對象不可見;目前對象與包含的對象是低耦合關系,如果修改包含對象的類中代碼不需要修改目前對象類的代碼;目前對象可以在運作時動态綁定所包含的對象
-
缺點:①容易産生過多的對象;②為了能夠組合多個對象,必須仔細對接口進行定義
由此可見,組合比繼承更具靈活性和穩定性,是以在設計的時候優先使用組合
- 接口和抽象類:
-
抽象類:在定義方法時可以隻給出方法頭,而不必給出方法體(即方法實作的細節),這樣的方法被稱為抽象方法。
抽象方法必須使用關鍵字abstract修飾,包含抽象方法的類必須聲明為抽象類。
抽象類不能被執行個體化
JAVA語言規定,子類必須實作其父類中的所有抽象方法,否則該子類也隻能聲明為抽象類
抽象類主要是通過繼承、再由其子類發揮作用的,其中作用包括兩方面:代碼重用;規劃
其他特性:
抽象類中可以不包含抽象方法;
子類中可以不全部實作抽象父類中的抽象方法,但此時子類也隻能聲明為抽象類
父類不是抽象類,但在子類可以添加抽象方法,但子類需聲明為抽象類
抽象類中可以聲明static屬性和方法
-
接口:
接口是特殊的抽象類,一個類隻包含靜态最終變量和抽象方法,或者是隻有抽象方法的時候,抽象類可以等同于一個接口。
用接口代替抽象類,是因為接口有比抽象類更好的特性:①可以被多繼承;②設計和實作完全分離;③更自然的使用多态;④更容易搭建程式架構;⑤更容易更換實作
特性:
接口不可以被執行個體化,常作為類型使用
實作類必須實作接口的所有方法(抽象類除外)
實作類可以實作多個接口(Java中的多繼承)
接口中的變量都是靜态常量(Java文法規定,接口中的變量預設自動隐含是public static final)
-
-
靜态類與動态類
變量的靜态類:指用于聲明變量的類。靜态類在編譯時就确定下來,且再也不會改變
變量的動态類:指與變量所表示的目前數值相關的類。動态類在程式執行時,對變量賦新值時可以改變
對于靜态類型面向對象程式設計語言,在編譯時消息傳遞表達式的合法性不是基于接收器的目前動态數值,而是基于接收器的靜态類來決定的
Animal pet;
pet = new Dog();
pet.bark();//這裡會報錯,因為Animal類沒有bark()這個方法
-
方法綁定:分為靜态方法綁定(在程式執行前方法已經被綁定)和動态方法(在運作時根據具體對象的類型進行綁定)綁定
響應消息時對哪個方法進行綁定是由接收器目前所包含的動态數值來決定的。
多态和重載
多态是指能夠在不同上下文中對某一事物(變量、函數或對象)賦予不同含義或用法
引入多态的概念,是為了得到更為靈活的方式使表示的形式盡可能與所表示的内容無關
-
多态的三種類型:
多态一般分為繼承多态、重載多态、模闆多态
重載多态和模闆多态是靜态多态,即多态行為是在編譯期決定的
而繼承多态是一種動态多态的形式,即多态行為可以在運作時動态改變
-
多态變量:
指可以引用多種對象類型的變量
這種變量在程式執行過程中可以包含不同類型的數值
對于動态類型語言,所有的變量都可能是多态的
對于靜态類型語言,則是替換原則的具體表現
-
四種形式的多态:
1.重載(overloading):兩種,基于類型簽名的重載和基于範疇的重載
-
函數類型簽名:是關于函數參數類型、參數順序和傳回值類型的描述,類型簽名通常不包括接收器類型
基于類型簽名的重載就是,多個方法允許共享同一名稱,且通過該過程所需的參數數目、順序和類型(對于靜态類型語言)來對他們進行區分,即使函數處于同一上下文也是合法的。唯一的要求就是每種實作方式都必須具有不同的類型簽名。
關于重載的解析,是在編譯時給予參數值的靜态類型完成的。和改寫不同,不涉及運作時機制。
-
範疇:定義了能夠使名稱有效使用的一段程式,或者能夠使名稱有效使用的方式
基于範疇的重載就是,兩個不同的函數可以使用相同名稱的本地變量,由于它們的範疇之間并不重疊,是以不會産生任何混亂。
通過繼承建立的新類将同時建立新的名稱範疇,該範疇是對父類的名稱範疇的擴充
強制、轉換和造型
-
強制是一種隐式的類型轉換,它發生在無需顯式引用的程式中。
double x = 2.8;
int i = 3;
x =i+x;//這裡i會轉換成double
-
轉換:程式員進行的顯式類型轉換,也被稱為“造型”
x = ((double)i)+x;
-
重載方法比對算法:
①找精确比對(形參實參精确比對的同一類型),找到則執行,若未找到,進入第二步
②找可行比對(符合替換原則的比對,即實參所屬類是形參所屬類的子類),若沒找到,報錯;隻找到一個可行比對,執行;如果多于1個,則轉第三步
③多個可行比對兩兩比較,逐一作出判斷:如果一個方法的類型簽名都可以指派給另一個方法,則後者(類型大者)被排除;重複此操作,直到無法排除為止。
④如果隻剩一個幸存者,執行。否則報錯
2.重置/改寫(overriding)
發生在有父類和子類關系的上下文中,與替換原則相結合
3.多态變量(指派多态):聲明和包含不同
Parent p = new Child();
多态變量是指可以引用多種對象類型的變量,這種變量在程式執行過程中可以包含不同類型的數值,對動态類型語言,所有的變量都可能是多态的,而對于靜态類型語言,多态變量是替換原則的具體表現。
多态方法(純多态),執行個體:StringBuffer中的append方法,一個定義,多種結果。
4.泛型(模闆)
Template T max(T left,T right)
{//詳細的代碼
}
名稱T是一個參數,但是它是不同于函數的兩個參數,在函數的代碼體中,T可以用于任何合适的類型
為什麼需要泛型?泛型的主要目的之一就是用來指定容器要持有什麼類型的對象,而且由編譯器來保證類型的正确性,與其使用Object,不如暫時不指定類型,而是稍後再決定具體使用什麼類型。
-
-
重載、重定義、改寫的差別:
重載:有兩種,見上文
重定義:當子類定義了一個與父類具有相同的名稱但簽名類型不同的方法時,發生重定義。類型簽名的變化是重定義差別于改寫的主要依據。
改寫:如果子類的方法具有與父類的方法相同的名稱和類型簽名,我們就說子類的方法改寫了父類的方法
重載與改寫:改寫可以看成重載的一種特殊情況,但還存在着以下差異:
1.對于改寫。方法所在的類必須符合繼承關系,而重載無此要求
2.如果發生改寫,兩個方法的類型簽名必須比對
3.重載方法總是獨立的,而對于改寫的兩個方法,有時會結合起來一起實作某行為
4.重載通常實在編譯時解析的,而改寫則是一種運作時機制。
-
動态綁定
綁定是把一個過程調用和相應這個調用而需要執行的代碼加以結合的過程
在編譯時進行的叫靜态綁定
在運作時進行的叫動态綁定。是以,一個給定的過程調用和代碼的結合直到調用發生時才得以進行,因而也叫作延遲綁定
方法
定義于某一特定類上的操作與規則
具有同類的對象才可為該類的方法所操作
這組方法表達了該類對象的動态性質,而對于其他類的對象可能無意義,乃至非法
規則,說明了對象的其他特征之間是怎樣聯系的,或者對象在什麼條件下是可行的
方法也稱作行為
消息
對另一個對象的操作在于選擇一個對象并通知它要做什麼
該對象“決定”如何完成這一任務,在其所屬類的方法集合中選擇合适的方法作用于其身
“操作一個對象“并不意味着直接将某個程式作用于該對象,而是利用傳遞消息,通知對象自己去執行這一操作,接收到消息的對象經過解釋,然後予以相應
發送消息的對象不需要知道接收消息的對象如何對請求予以相應
封裝
所有資訊(資料及行為)都封裝在對象中
影響對象的唯一方式是執行它所屬的類的方法即執行作用于其上的操作
資訊隐藏
當使用對象時,不必知道對象的屬性及行為在内部是如何表示和實作的,隻需知道它提供了哪些方法即可。
目的:1.避免重複的代碼;2.保護類受到不必要的修改