摘要:抽象數據類型的多個不同表示可以共存于同一個程序中,作為實現接口的不同類。封裝和信息隱藏信息隱藏將精心設計的模塊與不好的模塊區分開來的唯一最重要的因素是其隱藏內部數據和其他模塊的其他實施細節的程度。
大綱
面向對象的標準
基本概念:對象,類,屬性,方法和接口
OOP的獨特功能
封裝和信息隱藏 繼承和重寫 多態性,子類型和重載 靜態與動態分派
Java中一些重要的Object方法
設計好的類
面向對象的歷史
總結
面向對象的編程方法/語言應該具有類的概念作為中心概念。
語言應該能夠為類和它的特征提供斷言(即規范:前置條件,后置條件和不變量)和異常處理,依靠工具生成這些斷言中的文檔,并且可選地在運行時監視它們 時間。
他們幫助生產可靠的軟件;
他們提供系統文件;
它們是測試和調試面向對象軟件的核心工具。
靜態類型:一個定義良好的類型系統應該通過強制執行一些類型聲明和兼容性規則來保證它接受的系統的運行時類型安全。
泛型(Genericity):用于“準備改變”和“為/重用設計”:應該可以編寫具有表示任意類型的正式泛型參數的類。
繼承(Inheritance):應該可以將一個類定義為從另一個繼承,以控制潛在的復雜性。
多態(Polymorphism):在基于繼承的類型系統的控制下,應該可以將實體(表示運行時對象的軟件文本中的名稱)附加到各種可能類型的運行時對象。
動態分派/綁定(Dynamic dispatch / binding):在一個實體上調用一個特性應該總是觸發與所附加的運行時對象的類型相對應的特性,這在調用的不同執行過程中不一定是相同的。
基本概念:對象,類,屬性和方法對象
真實世界的物體有兩個特征:它們都有狀態和行為。
識別真實世界對象的狀態和行為是從OOP角度開始思考的好方法。
狗有狀態(名稱,顏色,品種,饑餓)和行為(吠叫,取出,搖尾巴)。
自行車具有狀態(當前檔位,當前踏板節奏,當前速度)和行為(更換檔位,改變踏板節奏,應用制動器)。
對于你看到的每個對象,問自己兩個問題,這些現實世界的觀察都轉化為OOP的世界:
這個物體有什么可能的狀態?
這個對象有什么可能的行為?
一個對象是一組狀態和行為
狀態 - 包含在對象中的數據。
在Java中,這些是對象的字段
行為 - 對象支持的操作
在Java中,這些被稱為方法
方法只是面向對象的功能
調用一個方法=調用一個函數
類
每個對象都有一個類
一個類定義方法和字段
統稱為成員的方法和領域
類定義了類型和實現
類型≈可以使用對象的位置
執行≈對象如何做事情
松散地說,類的方法是它的應用程序編程接口(API)
定義用戶如何與實例進行交互
靜態與實例變量/類的方法
類成員變量:與類相關聯的變量,而不是類的實例。 您還可以將方法與類關聯 - 類方法。
要引用類變量和方法,需要將類的名稱和類方法或類變量的名稱連同句點(".")一起加入。
不是類方法或類變量的方法和變量稱為實例方法和實例成員變量。
要引用實例方法和變量,必須引用類實例中的方法和變量
總結:
類變量和類方法與類相關聯,并且每個類都會出現一次。 使用它們不需要創建對象。
實例方法和變量在類的每個實例中出現一次。
靜態方法不與任何特定的類實例關聯,而實例方法(不帶static關鍵字聲明)必須在特定對象上調用。
接口Java的接口是一種用于設計和表達ADT的有用語言機制,其實現方式是實現該接口的類。
Java中的接口是方法簽名的列表,但沒有方法體。
如果一個類在其implements子句中聲明接口并為所有接口的方法提供方法體,則該類將實現一個接口。
一個接口可以擴展一個或多個其他接口
一個類可以實現多個接口
接口和類:定義和實現ADT
接口之間可以繼承
一個類可以實現多個接口
接口和實現
API的多個實現可以共存
多個類可以實現相同的API
他們在性能和行為方面可能有所不同
在Java中,API由接口或類指定
接口只提供一個API
一個接口定義但不實現API
類提供了一個API和一個實現
一個類可以實現多個接口
一個接口可以有多種實現
Java接口和類
接口與類
接口:確定ADT規約
類:實現ADT
類確實定義了類型
類似接口方法的公共類方法
可從其他課程直接訪問的公共字段
但更喜歡使用接口
除非你知道一個實現就足夠,否則使用變量和參數的接口類型。
支持更改實施;
防止依賴于實施細節
問題:打破抽象邊界
客戶必須知道具體表示類的名稱。
因為Java中的接口不能包含構造函數,所以它們必須直接調用其中一個具體類的構造函數。
構造函數的規范不會出現在接口的任何位置,所以沒有任何靜態的保證,即使不同的實現甚至會提供相同的構造函數。
接口的優點
接口指定了客戶端的契約,僅此而已。
接口是客戶程序員需要閱讀才能理解ADT的全部內容。
客戶端不能在ADT的表示上創建無意的依賴關系,因為實例變量根本無法放入接口。
實現完全保持完全分離,完全不同。 抽象數據類型的多個不同表示可以共存于同一個程序中,作為實現接口的不同類。
當一個抽象數據類型被表示為一個多帶帶的類時,如果沒有接口,就很難擁有多個表示。
為什么有多個實現
不同的表現
選擇最適合您使用的實現
不同的行為
選擇你想要的實現
行為必須符合接口規范(“契約”)
性能和行為往往不盡相同
提供功能
性能權衡
示例:HashSet,TreeSet
接口總結
編譯器和人員的文檔
允許性能權衡
可選的方法
有意識欠定規格的方法
一個類的多個視圖
越來越不值得信賴的實現
減少錯誤保證安全
ADT由它的操作定義,而接口就是這樣做的。
當客戶端使用接口類型時,靜態檢查確保它們只使用接口定義的方法。
如果實現類暴露其他方法 - 或更糟糕的是,具有可見的表示 - 客戶端不會意外地看到或依賴它們。
當我們有一個數據類型的多個實現時,接口提供方法簽名的靜態檢查。
容易明白
客戶和維護人員確切知道在哪里查找ADT的規范。
由于接口不包含實例字段或實例方法的實現,因此更容易將實現的細節保留在規范之外。
準備好改變
我們可以通過添加實現接口的類輕松地添加新類型的實現。
如果我們避免使用靜態工廠方法的構造函數,客戶端將只能看到該接口。
這意味著我們可以切換客戶端正在使用的實現類,而無需更改其代碼。
封裝和信息隱藏信息隱藏
將精心設計的模塊與不好的模塊區分開來的唯一最重要的因素是其隱藏內部數據和其他模塊的其他實施細節的程度。
設計良好的代碼隱藏了所有的實現細節
將API與實施完全分開
模塊只通過API進行通信
對彼此的內在運作無知
被稱為信息隱藏或封裝,是軟件設計的基本原則。
信息隱藏的好處
將構成系統的類分開
允許它們獨立開發,測試,優化,使用,理解和修改
加速系統開發
類可以并行開發
減輕了維護的負擔
可以更快速地理解類并調試,而不必害怕損害其他模塊
啟用有效的性能調整
“熱”類可以多帶帶優化
增加軟件重用
松散耦合的類通常在其他情況下證明是有用的
通過接口隱藏信息
使用接口類型聲明變量
客戶端只能使用接口方法
客戶端代碼無法訪問的字段
但我們到目前為止
客戶端可以直接訪問非接口成員
實質上,它是自愿的信息隱藏
成員的可見性修飾符
private - 只能從聲明類訪問
protected - 可以從聲明類的子類(以及包內)
public - 從任何地方訪問
信息隱藏的最佳實踐
仔細設計你的API
只提供客戶需要的功能,其他所有成員應該是私人的
您可以隨時在不破壞客戶的情況下讓私人成員公開
但反之亦然!
繼承和重寫(1)重寫
可重寫的方法和嚴格的繼承
可重寫方法:允許重新實現的方法。
在Java方法中默認是可重寫的,即沒有特殊的關鍵字。
嚴格的繼承
子類只能向超類添加新的方法,它不能覆蓋它們
如果某個方法不能在Java程序中被覆蓋,則必須以關鍵字final為前綴。
final
final字段:防止初始化后重新分配給字段
final方法:防止重寫該方法
final類:阻止繼承類
重寫
方法重寫是一種語言功能,它允許子類或子類提供已由其超類或父類之一提供的方法的特定實現。
相同的名稱,相同的參數或簽名,以及相同的返回類型。
執行的方法的版本將由用于調用它的對象決定。實際執行時調用哪個方法,運行時決定。
如果父類的對象用于調用該方法,則會執行父類中的版本;
如果使用子類的對象來調用該方法,則會執行子類中的版本。
當子類包含一個覆蓋超類方法的方法時,它也可以使用關鍵字super調用超類方法。
重寫的時候,不要改變原方法的本意
(2)抽象類
抽象方法和抽象類
抽象方法:
具有簽名但沒有實現的方法(也稱為抽象操作)
由關鍵字abstract定義
抽象類:
一個包含至少一個抽象方法的類被稱為抽象類
接口:只有抽象方法的抽象類
接口主要用于系統或子系統的規范。 該實現由子類或其他機制提供。
具體類?抽象類?接口
多態性,子類型和重載(1)三種多態性
特殊多態(Ad hoc polymorphism):當一個函數表示不同且可能不同種類的實現時,取決于多帶帶指定類型和組合的有限范圍。 使用函數重載(function overloading)在許多語言中支持特設多態。
參數化多態(parametric polymorphism):當代碼被寫入時沒有提及任何特定類型,因此可以透明地使用任何數量的新類型。 在面向對象的編程社區中,這通常被稱為泛型或泛型編程。
子類型多態(也稱為子類型多態或包含多態):當名稱表示由一些公共超類相關的許多不同類的實例。
(2)特殊多態和重載
當函數適用于幾種不同的類型(可能不會顯示公共結構)并且可能以不相關的方式表現每種類型時,可以獲得特殊多態。
(3)重載
重載的方法允許您在類中重復使用相同的方法名稱,但使用不同的參數(以及可選的不同的返回類型)。
重載方法通常意味著對于那些調用方法的人來說會更好一些,因為代碼承擔了應對不同參數類型的負擔,而不是在調用方法之前強制調用方執行轉換。
函數重載可以在不同的實現中創建同名的多個方法。
對重載函數的調用將運行適合于調用上下文的該函數的特定實現,允許一個函數調用根據上下文執行不同的任務。
重載是一種靜態多態
函數調用使用“最佳匹配技術”解決,即根據參數列表解析函數。
函數調用中的靜態類型檢查
在編譯時確定使用這些方法中的哪一個。(靜態類型檢查)
與之相反,重寫方法則是在運行時進行動態檢查!
重載規則
函數重載中的規則:重載函數必須因參數或數據類型而有所不同
必須改變參數列表。
可以改變返回類型。
可以改變訪問修飾符。
可以聲明新的或更廣泛的檢查異常。
一個方法可以在同一個類或子類中重載。
重寫與重載
不要混淆覆蓋派生類中的方法和重載方法名稱
當方法被覆蓋時,派生類中給出的新方法定義與基類中的參數數量和類型完全相同
當派生類中的方法與基類中的方法有不同的簽名時,即重載
請注意,當派生類重載原始方法時,它仍然繼承基類中的原始方法
(3)參數多態性和泛型編程
當一個函數在一系列類型上統一工作時獲得參數多態性; 這些類型通常具有一些共同的結構。
它能夠以通用的方式定義函數和類型,以便它可以在運行時傳遞參數的基礎上工作,即允許在沒有完全指定類型的情況下進行靜態類型檢查。
這是Java中所謂的“泛型(Generic)”。
泛型編程是一種編程風格,其中數據類型和函數是根據待指定的類型編寫的,隨后在需要時作為參數提供的特定類型實例化。
泛型編程圍繞從具體,高效的算法中抽象出來以獲得可與不同數據表示形式結合的泛型算法來生成各種各樣有用軟件的想法相關。
Java中的泛型
類型變量是一個不合格的標識符。
它們由泛型類聲明,泛型接口聲明,泛型方法聲明和泛型構造函數聲明引入。
如果一個類聲明一個或多個類型變量,則該類是通用的。
泛型類:其定義中包含了類型變量
這些類型的變量被稱為類的類型參數。
它定義了一個或多個作為參數的類型變量。
泛型類聲明定義了一組參數化類型,每個類型參數部分的每個可能的調用都有一個類型聲明。
所有這些參數化類型在運行時共享相同的類。
如果聲明了類型變量,則interface是通用的。
這些類型變量被稱為接口的類型參數。
它定義了一個或多個作為參數的類型變量。
通用接口聲明定義了一組類型,每種類型參數部分的每個可能的調用都有一個類型。
所有參數化類型在運行時共享相同的接口。
如果聲明類型變量,則方法是通用的。
這些類型的變量被稱為方法的形式類型參數。
形式類型參數列表的形式與類或接口的類型參數列表相同。
類型變量
使用菱形運算符<>來幫助聲明類型變量。
一些Java泛型細節
可以有多個類型參數
例如Map
Wildcards通配符,只在使用泛型的時候出現,不能在定義中出現
List <?> list = new ArrayList
List extends Animal>
List super Animal>
通用類型信息被擦除(即僅編譯時)
不能使用instanceof()來檢查通用類型運行時泛型消失了!
無法創建通用數組
Pair
(4)子類多態性
子類
一個類型是一組值。
Java List類型由接口定義。
如果我們考慮所有可能的List值,它們都不是List對象:我們不能創建接口的實例。
相反,這些值都是ArrayList對象或LinkedList對象,或者是實現List的另一個類的對象。
子類型只是超類型的一個子集
ArrayList和LinkedList是List的子類型。
繼承/子類型的好處:重用代碼,建模靈活性
在Java中:每個類只能直接繼承一個父類; 一個類可以實現多個接口。
“B是A的子類型”意思是“每個B都是A.”
在規格方面:“每個B都符合A的規格”。
如果B的規格至少和A的規格一樣強,B只是A的一個子類型。 - 當我們聲明一個實現接口的類時,Java編譯器會自動執行這個需求的一部分:它確保A中的每個方法出現在B中,并帶有一個兼容的類型簽名。
B類不能實現接口A,而不實現A中聲明的所有方法。
靜態檢查子類型
但編譯器無法檢查我們是否以其他方式削弱了規范:
加強對某種方法的某些投入的先決條件
弱化后置條件
弱化接口抽象類型通告客戶端的保證。
如果你在Java中聲明了一個子類型(例如,實現一個接口),那么你必須確保子類型的規范至少和超類型一樣強。
子類型的規約不能弱化超類型的規約。
子類型多態
子類型多態:不同類型的對象可以被客戶代碼統一處理子類型多態:不同類型的對象可以統一的處理而無需區分
每個對象根據其類型行為(例如,如果添加新類型的帳戶,客戶端代碼不會更改)從而隔離了“變化”
Liskov替換原則(LSP):
如果S是T的子類型,那么類型T的對象可以用類型S的對象替換(即,類型T的對象可以用子類型S的任何對象代替)而不改變T的任何期望屬性。
instanceof
測試某個對象是否為給定類的運算符
建議:如果可能,避免使用instanceof(),并且從不在超類中使用instanceof()來檢查針對子類的類型。
類型轉換
有時你想要一種不同于你已有的類型
如果你知道你有一個更具體的子類型,很有用
但是如果類型不兼容,它會得到一個ClassCastException
建議:
避免向下轉換類型
從不在超類內向某個子類下降
動態分派動態分派是選擇在運行時調用多態操作的哪個實現的過程。
面向對象的系統將一個問題建模為一組交互對象,這些對象執行按名稱引用的操作。
多態現象是這樣一種現象,即有些可互換的對象每個都暴露同一名稱的操作,但行為可能不同。
確定在運行時要調用哪種方法,即對重寫或多態方法的調用可在運行時解決
作為示例,File對象和Database對象都有一個StoreRecord方法,可用于將人員記錄寫入存儲。 他們的實現不同。
一個程序持有一個對象的引用,該對象可能是一個File對象或一個數據庫對象。 它可能是由運行時間設置決定的,在這個階段,程序可能不知道或關心哪一個。
當程序在對象上調用StoreRecord時,需要確定哪些行為被執行。
該程序將StoreRecord消息發送給未知類型的對象,并將其留給運行時支持系統以將消息分派給正確的對象。 該對象實現它實現的任何行為。
動態分派與靜態分派形成對比,其中在編譯時選擇多態操作的實現。
動態分派的目的是為了支持在編譯時無法確定多態操作的適當實現的情況,因為它依賴于操作的一個或多個實際參數的運行時類型。
靜態分派:編譯階段即可確定要執行哪個具體操作。
重載的方法使用靜態分派,而重寫的方法在運行時使用動態分派。
動態分派不同于動態綁定(也稱為動態綁定)。
選擇操作時,綁定會將名稱與操作關聯。
在決定了名稱引用的操作后,分派會為操作選擇一個實現。
綁定:將調用的名字與實際的方法名字聯系起來(可能很多個);
分派:具體執行哪個方法(提前綁定→靜態分派)
通過動態分派,該名稱可以在編譯時綁定到多態操作,但不會在運行時間之前選擇實現。動態分派:編譯階段可能綁定到多態操作,運行階段決定具體執行哪個(覆蓋和過載均是如此);
雖然動態分派并不意味著動態綁定,但動態綁定確實意味著動態分派,因為綁定決定了可用分派的集合。
提前/靜態綁定
每當發生靜態,私有和最終方法的綁定時,類的類型由編譯器在編譯時確定,并且綁定在那里和那里發生。
推遲/動態綁定
重寫父類和子類都有相同的方法,在這種情況下,對象的類型決定了要執行哪種方法。 對象的類型在運行時確定。
動態方法分派
1.(編譯時)確定要查找哪個類
2.(編譯時)確定要執行的方法簽名
查找所有可訪問的適用方法
選擇最具體的匹配方法
3.(運行時)確定接收器的動態類別
4.(運行時)從動態類中,找到要調用的方法
在第2步中找到具有相同簽名的方法
否則在超類中搜索等。
10 Java中的一些重要的Object方法
equals() - 如果兩個對象“相等”,則為true
hashCode() - 用于哈希映射的哈希代碼
toString() - 可打印的字符串表示形式
toString() - 丑陋而無信息
你知道你的目標是什么,所以你可以做得更好
總是覆蓋,除非你知道不會被調用
equals&hashCode - 身份語義
如果你想要價值語義,你必須重寫
否則不要
設計好的類不可變類的優點
簡單
本質上是線程安全的
可以自由分享
不需要防御式拷貝
優秀的積木
如何編寫一個不可變的類
不要提供任何變值器
確保沒有方法可能被覆蓋
使所有的領域最終
使所有字段保密
確保任何可變組件的安全性(避免重復曝光)
實現toString(),hashCode(),clone(),equals()等。
何時讓類不可變
總是,除非有充分的理由不這樣做
總是讓小型“價值類”永恒不變!
何時讓類可變
類表示狀態改變的實體
真實世界 - 銀行賬戶,紅綠燈
抽象 - 迭代器,匹配器,集合
進程類 - 線程,計時器
如果類必須是可變的,則最小化可變性
構造函數應該完全初始化實例
避免重新初始化方法
OOP的歷史仿真和面向對象編程的起源
20世紀60年代:Simula 67是第一個由Kristin Nygaardand Ole-Johan Dahl在挪威計算中心開發的面向對象語言,用于支持離散事件模擬。 (類,對象,繼承等)
“面向對象編程(OOP)”這個術語最早是由施樂PARC在他們的Smalltalk語言中使用的。
20世紀80年代:面向對象已經變得非常突出,而其中的主要因素是C ++。
NiklausWirth用于Oberon和Modula-2的模塊化編程和數據抽象;
埃菲爾和Java
面向對象的標準
基本概念:對象,類,屬性,方法和接口
OOP的獨特功能
封裝和信息隱藏 繼承和重寫 多態性,子類型和重載 靜態和動態調度
Java中一些重要的Object方法
編寫一個不可變的類
OOP的歷史
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71327.html
摘要:抽象函數引發的關系是等價關系。所以當且僅當通過調用抽象數據類型的任何操作不能區分它們時,兩個對象是相等的。必須為每個抽象數據類型適當地定義操作。一般來說,在面向對象編程中使用是一種陋習。 大綱 什么是等價性?為什么要討論等價性?三種等價性的方式==與equals()不可變類型的等價性對象契約可變類型的等價性自動包裝和等價性 什么是等價性?為什么要討論等價性? ADT上的相等操作 ADT...
摘要:程序失敗時,很難確定錯誤的位置。它保護客戶免受單位工作細節的影響。將前提條件放在中,并將后置條件放入和。涉及可變對象的契約現在取決于每個引用可變對象的每個人的良好行為。設計規約按規約分類比較規約它是如何確定性的。 大綱 1.編程語言中的功能/方法2.規約:便于交流的編程,為什么需要規約 行為等同規約結構:前提條件和后條件測試和驗證規約3.設計規約分類規約圖表規約質量規約4.總結 編程...
摘要:所有變量的類型在編譯時已知在程序運行之前,因此編譯器也可以推導出所有表達式的類型。像變量的類型一樣,這些聲明是重要的文檔,對代碼讀者很有用,并由編譯器進行靜態檢查。對象類型的值對象類型的值是由其類型標記的圓。 大綱 1.編程語言中的數據類型2.靜態與動態數據類型3.類型檢查4.易變性和不變性5.快照圖6.復雜的數據類型:數組和集合7.有用的不可變類型8.空引用9.總結 編程語言中的數據...
摘要:二面向對象概述面向過程的設計思想和面向對象的設計思想我要吃魚香肉絲蓋飯面向過程我買菜我洗菜我切菜我放水我點火我做飯我炒菜。。。。 大家好,上次我們講過了樂字節Java編程之方法、調用、重載、遞歸,接下來我們將會進入到Java封裝的內容。Java編程語言發展,面向對象和類。 一、編程語言的發展 機器語言——直接由計算機的指令組成,指令、數據、地址都以0和1組成:可以被計算機直接識別并執行...
閱讀 1095·2021-11-15 18:00
閱讀 2808·2021-09-22 15:18
閱讀 1970·2021-09-04 16:45
閱讀 753·2019-08-30 15:55
閱讀 3865·2019-08-30 13:10
閱讀 1340·2019-08-30 11:06
閱讀 1988·2019-08-29 12:51
閱讀 2296·2019-08-26 13:55