天天看點

3-4 面向對象的程式設計OOP

本次課程的目的

▪ 将抽象資料類型的接口與其實作分離,并使用Java接口類型強制分離。

▪ 用接口定義adt,并編寫實作接口的類。

大綱

▪ 基本概念:對象、類、屬性、方法、接口和枚舉

▪ 面向對象的封裝與資訊隐藏的顯著特征

繼承與重寫

多态,子類型與重載、靜态與動态排程

▪ Java中一些重要的對象方法

▪ 設計好的課程、

▪ 面向對象的曆史

▪ 摘要

1基本概念:對象、類、屬性和方法

對象

▪ 現實世界中的物體有兩個共同的特征:它們都有狀态和行為。

▪ 識别現實世界對象的狀态和行為是從面向對象的角度開始思考的一個好方法。

–狗有狀态(名稱、顔色、品種、饑餓)和行為(吠叫、抓取、搖尾巴)。

–自行車有狀态(目前檔位、目前踏闆節拍、目前速度)和行為(換檔、更改踏闆節拍、踩刹車)。

▪ 對于你看到的每一個物體,問自己兩個問題,這些真實世界的觀察結果都轉化為面向對象程式設計的世界:這個物體可能處于什麼樣的狀态?

–此對象可以執行哪些可能的行為?

對象是狀态和行為的集合

▪ State–對象中包含的資料。–在Java中,這些是對象的字段

▪ 行為

——對象支援的動作

——在Java中,這些被稱為方法

——方法隻是面向對象的函數

——調用方法=調用函數

Class

▪ 每個對象都有一個類

——一個類定義方法和字段

——方法和字段統稱為成員

▪ 類同時定義類型和實作-類型≈可以使用對象的位置-實作≈對象如何做事情

▪ 松散地說,類的方法是其應用程式程式設計接口(API)——定義使用者如何與執行個體互動

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

類的靜态與執行個體變量/方法

▪ 類變量:與類關聯而不是與類的執行個體關聯的變量。您還可以将方法與類(class methods)關聯。

–若要引用類變量和方法,請将類的名稱、類方法或類變量的名稱與句點(’.’)連接配接起來。

▪ 非類方法或類變量的方法和變量稱為執行個體方法和執行個體變量。

–要引用執行個體方法和變量,必須引用類執行個體中的方法和變量

▪ 摘要:

–類變量和類方法與類關聯,并且每個類發生一次。使用它們不需要建立對象。

–執行個體方法和變量在類的每個執行個體中出現一次。

類的靜态與執行個體變量/方法

3-4 面向對象的程式設計OOP

類的靜态與執行個體變量/方法

▪ 靜态方法不與類的任何特定執行個體相關聯,而必須對特定對象調用執行個體方法(聲明時不使用Static關鍵字)。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

2接口和枚舉

接口

▪ Java的接口是設計和表達ADT的一種有用的語言機制,它的實作是實作該接口的類。

–Java中的接口是方法簽名清單,但沒有方法體。

–如果類在其implements子句中聲明接口,則它實作接口,并為接口的所有方法提供方法體。

–一個接口可以擴充一個或多個其他接口

–一個類可以實作多個接口

-一個接口可以有多個實作類

3-4 面向對象的程式設計OOP

與示例類一起使用的接口

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

修改類以使用接口

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

接口允許多種實作

3-4 面向對象的程式設計OOP

接口将客戶機與實作分離

3-4 面向對象的程式設計OOP

Java接口和類

▪ 接口與類-接口:指定期望

-類:按期望傳遞(實作)

▪ 類确實定義了類型

——公共類方法可以像接口方法那樣使用

——公共字段可以直接從其他類通路

▪ 但是更喜歡使用接口

——除非你知道一個實作就足夠了,否則對變量和參數使用接口類型。

–支援實作的更改;

–防止對實作細節的依賴

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

為什麼要多個實作?

▪ 不同的性能—選擇最适合使用的實作

▪ 不同的行為-選擇您想要的實作-行為必須符合接口規範(“契約”)

▪ 通常性能和行為都會有所不同–提供功能–性能權衡–例如:HashSet、TreeSet

使用MyString及其實作

▪ 問題:打破了抽象障礙

——客戶機必須知道具體表示類的名稱。

–因為Java中的接口不能包含構造函數,是以它們必須直接調用一個具體類的構造函數。

–該構造函數的規範不會出現在接口中的任何地方,是以無法靜态保證不同的實作甚至會提供相同的構造函數。

使用靜态工廠而不是構造函數

3-4 面向對象的程式設計OOP

枚舉

▪ 有時類型有一個小的、有限的不可變值集,例如:–一年中的幾個月:一月、二月、…、十一月、十二月

–一周中的幾天:星期一、星期二、…、星期六、星期日–指南針點:北、南、東、西

▪ 當值集很小且有限時,将所有值定義為命名常量(稱為枚舉)是有意義的。Java具有enum構造。

3-4 面向對象的程式設計OOP

向枚舉添加資料和行為

3-4 面向對象的程式設計OOP

4封裝和資訊隐藏

資訊隐藏

▪ 區分一個設計良好的子產品和一個糟糕的子產品的一個最重要的因素是它對其他子產品隐藏内部資料和其他實作細節的程度

▪ 精心設計的代碼隐藏了所有的實作細節

——清晰地将API與實作分離

——子產品隻通過API進行通信

——它們對彼此的内部工作不敏感

▪ 被稱為資訊隐藏或封裝,是軟體設計的基本原則。

資訊隐藏的好處

▪ 分離組成系統的類-允許它們單獨開發、測試、優化、使用、了解和修改

▪ 加快系統開發——類可以并行開發

▪ 減輕了維護的負擔——類可以更快速地了解和調試,而不必擔心會損害其他子產品

▪ 實作有效的性能優化-Hot class可以單獨優化

▪ 增加軟體重用——松散耦合的類通常在其他上下文中被證明是有用的

帶接口的資訊隐藏

▪ 使用接口類型聲明變量

▪ 用戶端隻能使用接口方法

▪ 無法從用戶端代碼通路字段

▪ 但這隻需要我們到目前為止-用戶端可以直接通路非接口成員-本質上,這是自願的資訊隐藏

成員的可見性修改器

▪ private–隻能從聲明類通路

▪ 受保護–可從聲明類的子類(以及包内)通路

▪ 公共-可從任何地方通路

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

資訊隐藏的最佳實踐

▪ 仔細設計你的API

▪ 隻提供用戶端所需的功能,所有其他成員都應是私有的

可以在不妨礙調用者的情況下,在後期将私有變為 公有

不能進行相反操作,會破壞調用者的使用

5繼承和重寫

繼承

▪ 繼承用于代碼重用-隻寫一次代碼-在子類中隐式提供超類特性(公共的,受保護的)

3-4 面向對象的程式設計OOP

(1) Overriding

可重寫方法和嚴格繼承

▪ 可重寫方法:允許重新實作的方法。

–在Java中,方法在預設情況下是可重寫的,即沒有特殊的關鍵字。

▪ 嚴格繼承(Strict inheritance)

——子類隻能向超類添加新方法,不能覆寫它們

——如果在Java程式中不能覆寫某個方法,則必須以關鍵字final作為字首。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

final

▪ final field:防止在初始化後重新配置設定到該字段

▪ final method:防止重寫該方法

▪ final類:防止擴充該類

——例如,public final類CheckingAccountImpl{。。。}

覆寫(覆寫/重寫)

▪ 方法重寫是一種語言特性,它允許子類或子類提供方法的特定實作,該方法已由其父類之一提供。

–相同的名稱、相同的參數或簽名以及相同的傳回類型。

–執行的方法的版本将由用于調用它的對象确定。

。如果使用父類的對象來調用該方法,則将執行父類中的版本;如果使用子類的對象來調用該方法,則将執行子類中的版本。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

當子類包含重寫超類方法的方法時,它還可以使用關鍵字super調用超類方法。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

重寫方法的錯誤使用

▪ 用全新的含義覆寫超類的操作。

3-4 面向對象的程式設計OOP

該重寫将加法定義為了減法,減法定義為了加法

重寫方法的提示

▪ 如果要重寫方法:

–確定簽名比對–使用@override以便編譯器支援

–複制和粘貼聲明(或讓IDE為您執行此操作)

–可見設定可以保持不變或增加,但不能降低。

3-4 面向對象的程式設計OOP

(2) 抽象類

抽象方法和抽象類

▪ 抽象方法:

–有簽名但沒有實作的方法(也稱為抽象操作)

–由關鍵字Abstract定義

▪ 抽象類:

–包含至少一個抽象方法的類稱為抽象類

–它不能被執行個體化!

–在執行個體化從抽象類派生的類之前,其父類的所有抽象方法都必須由派生鍊中的某個類實作

3-4 面向對象的程式設計OOP

抽象方法和抽象類

▪ 接口:隻有抽象方法的抽象類,接口主要用于系統或子系統的規範。實作由子類或其他機制提供。

▪ 具體類→抽象類→接口

3-4 面向對象的程式設計OOP

抽象類缺少一個或多個方法的實作

受保護的元素在子類中可見

抽象方法留在子類中實作

3-4 面向對象的程式設計OOP

6多态、子類型和重載

(1) 三種多态性

三種多态性(多态)

▪ 特殊多态性:當一個函數表示不同的、潛在的異構實作時,取決于個别指定的類型群組合的有限範圍。許多使用函數重載(function overloading)的語言都支援特殊多态性。一個方法可以有多個同名的實作(方法重載)

▪ 參數多态性(Parametric polymorphics):當編寫代碼時沒有提到任何特定類型,是以可以透明地與任何數量的新類型一起使用。在面向對象程式設計社群中,這通常被稱為泛型或泛型程式設計。一個類型名字可以代表多個類型(泛型程式設計)

▪ 子類型(也稱為子類型多态性或包含多态性):當一個名稱表示由某個公共超類關聯的許多不同類的執行個體時

(2) 特殊多态性和重載

特殊多态性

▪ 當一個函數在幾個不同的類型上工作(可能沒有顯示出一個公共的結構)并且可能以不相關的方式對每種類型進行操作時,就可以獲得特殊的多态性

3-4 面向對象的程式設計OOP

重載

▪ 重載方法允許您在類中重用相同的方法名,但使用不同的參數(也可以選擇使用不同的傳回類型)。

▪ 重載方法通常意味着您對那些調用您的方法的人稍微好一點,因為您的代碼承擔了處理不同參數類型的負擔,而不是在調用您的方法之前強制調用方進行轉換。

重載

▪ 函數重載是使用不同的實作建立多個同名方法的能力。

–對重載函數的調用将運作該函數的特定實作,該實作适合于調用的上下文,允許一個函數調用根據上下文執行不同的任務。

▪ 重載是一種靜态多态性——使用“最佳比對技術”解析函數調用,即根據參數清單解析函數。

–函數調用中的靜态類型檢查–在編譯時解析這些方法中使用哪種方法的确定。

在編譯階段時決定要具體執行哪個方法 (static type checking) – 與之相反,overridden methods則是在run-time進行dynamic checking

重載規則

▪ 函數重載中的規則:重載的函數必須根據arity或資料類型而有所不同

-必須更改參數清單。

–可以更改傳回類型。

–可以更改通路修飾符。public/private/protected

–可以聲明新的或更廣泛的檢查異常。

–方法可以在同一個類或子類中重載

3-4 面向對象的程式設計OOP

要調用的方法的哪個重載版本在運作時根據對象類型決定,但要調用的方法的哪個重載版本基于編譯時傳遞的參數的引用類型。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

重寫與重載

▪ 不要将重寫派生類(baiquity)中的方法與重載方法名混淆

–重寫方法時,派生類中給出的新方法定義的參數數量和類型與基類中的完全相同

-當派生類中的方法與基類中的方法具有不同的簽名(即重載)時

-請注意,當派生類重載原始方法時,它仍然繼承原始方法也來自基層

3-4 面向對象的程式設計OOP

(3) 參數多态性與泛型程式設計

參數多态性

▪ 參數多态性是當一個函數在一系列類型上一緻工作時獲得的;這些類型通常表現出一些共同的結構。

–它能夠以泛型方式定義函數和類型,以便它基于運作時傳遞的參數工作,即允許靜态類型檢查,而不完全指定類型。

參數多态性

▪ 泛型程式設計是一種程式設計方式,在這種程式設計方式中,資料類型和函數是按照要在以後指定的類型編寫的,然後在需要時對作為參數提供的特定類型進行執行個體化

泛型程式設計的核心思想是從具體的、有效的算法中抽象出泛型算法,進而獲得可以與不同資料表示相結合的泛型算法,進而生成各種有用的軟體。

Java中的泛型

▪ 類型變量(type variable)是不合格(unqualified)辨別符。

–它們由泛型類聲明、泛型接口聲明、泛型方法聲明引入

▪ 一個classis泛型,如果它聲明一個或多個類型變量。

–這些類型變量稱為類的類型參數(baiquity)。

–它定義一個或多個用作參數的類型變量。

–泛型類聲明定義一組參數化類型,每個類型參數部分的可能調用都有一個參數化類型。

–所有這些參數化類型在運作時共享同一個類。

使用菱形運算符<>,幫助聲明類型變量

3-4 面向對象的程式設計OOP

Java中的泛型

▪ 如果接口聲明一個或多個類型變量,則它是泛型的。

–這些類型變量稱為接口的類型參數。

–它定義一個或多個用作參數的類型變量。

–泛型接口聲明定義一組類型,每個類型參數部分的可能調用都有一個類型。

–所有參數化類型在運作時共享同一接口。

3-4 面向對象的程式設計OOP

另一個例子:Java集

▪ 集合是某些其他類型E的有限元集合的ADT。

▪ Set是泛型類型的一個示例:一種類型,它的規範是按照稍後要填寫的占位符類型。

▪ 我們沒有為Set、Set等編寫單獨的規範和實作,而是設計并實作了一個Set。

3-4 面向對象的程式設計OOP

通用接口

▪ 假設我們要實作泛型Set接口。

–方法1:通用接口,非通用實作:實作特定類型E的Set

3-4 面向對象的程式設計OOP

方法2:通用接口,通用實作。

–我們還可以實作泛型Set接口,而不必為E選擇類型。

–在這種情況下,我們編寫的代碼與客戶機為E選擇的實際類型無關。

–Java的HashSet對Set執行此操作。

3-4 面向對象的程式設計OOP

Java中的泛型方法

▪ 如果方法聲明一個或多個類型變量,則該方法是泛型的。

–這些類型變量稱為方法的形式類型參數。

–形式類型參數清單的形式與類或接口的類型參數清單相同。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

(4) 亞型多态性

繼承和子類型:層次結構的一瞥

▪ Java集合API

3-4 面向對象的程式設計OOP

▪ 繼承/子類型的好處:代碼重用、模組化靈活性

▪ 在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的任何所需屬性。

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

instanceof()

▪ 測試對象是否屬于給定類的運算符

3-4 面向對象的程式設計OOP

▪ 建議:盡可能避免instanceof(),永遠不要(?)在超類中使用instanceof()檢查子類的類型

▪ 有時候你想要一個不同的類型

double pi = 3.14; int indianaPi = (int) pi;

▪ 如果您知道您有一個更具體的子類型,則很有用:

Account acct = …; CheckingAccount checkingAcct = (CheckingAccount) acct; long fee = checkingAcct.getFee();

▪ 但如果類型不相容,它将獲得ClassCastException▪ 建議:避免貶低類型為什麼?–從不(?)在超類中向下轉換為子類的原因

Java中的一些重要對象方法

重寫對象方法

▪ equals()–如果兩個對象“相等”,則為true

▪ hash code()–用于哈希映射的哈希代碼

▪ toString()–可列印的字元串表示

▪ toString()——醜陋且不具資訊性

——你知道你的對象是什麼,是以你可以做得更好

——總是重寫toString(),除非你知道不會調用該方法

▪ equals&hashCode–辨別語義

–如果需要比值,則必須重寫,否則不需要

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

替代哈希碼重寫

▪ 效率較低,但在其他方面同樣好!

3-4 面向對象的程式設計OOP

![在這裡插入圖檔描述](https://img-blog.csdnimg.cn/20200401104031186.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzc4NDUzNw==,size_16,color_FFFFFF,t_70

3-4 面向對象的程式設計OOP

▪ Name重寫了hashCode

,但沒重寫Equals!是以,這兩個名稱執行個體是不相等的。

3-4 面向對象的程式設計OOP

11設計好的class

不可變類的優點

▪ 簡單

▪ 固有的線程安全性

▪ 可以自由分享

▪ 不需要防禦副本

▪ 優秀的building blocks

如何編寫不可變類

▪ 不提供任何突變物

▪ 確定不能重寫任何方法

▪ 使所有字段成為最終字段

▪ 将所有字段設為私有

▪ 確定任何可變元件的安全性(避免重複暴露)

▪ 實作toString()、hashCode()、clone()、equals()等

3-4 面向對象的程式設計OOP
3-4 面向對象的程式設計OOP

何時使類不可變

▪ 總是,除非有充分的理由不

▪ 總是讓小的“值類”不可變!–例如:顔色、電話号碼、機關

何時使類可變

▪ 類表示其狀态更改的實體-現實世界-銀行帳戶、TrafficLight-抽象-疊代器、比對器、集合-程序類-線程、計時器

▪ 如果類必須是可變的,則最小化可變性-

構造函數應完全初始化執行個體

-避免重新初始化方法

12面向對象程式設計的曆史

3-4 面向對象的程式設計OOP

摘要總結

▪ 物體定向标準

▪ 基本概念:對象、類、屬性、方法和接口

▪ 面向對象封裝和資訊隐藏的顯著特點繼承和重寫多态性、子類型和重載靜态和動态排程

▪ Java中一些重要的對象方法

▪ 編寫不可變類

▪ 面向對象的曆史

繼續閱讀