Java程序設(shè)計基礎(chǔ) 課件 (羅剛)第7章 多態(tài)與接口、第8章 異常處理_第1頁
Java程序設(shè)計基礎(chǔ) 課件 (羅剛)第7章 多態(tài)與接口、第8章 異常處理_第2頁
Java程序設(shè)計基礎(chǔ) 課件 (羅剛)第7章 多態(tài)與接口、第8章 異常處理_第3頁
Java程序設(shè)計基礎(chǔ) 課件 (羅剛)第7章 多態(tài)與接口、第8章 異常處理_第4頁
Java程序設(shè)計基礎(chǔ) 課件 (羅剛)第7章 多態(tài)與接口、第8章 異常處理_第5頁
已閱讀5頁,還剩83頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第七章多態(tài)與接口7.1多態(tài)7.2多態(tài)的支撐技術(shù)7.3多態(tài)實現(xiàn)7.4多態(tài)分析7.5接口本章小結(jié)

7.1多態(tài)

本章將要介紹的面向?qū)ο笕筇卣鞯淖詈笠粋€特征——多態(tài)(Polymorphism),是面向?qū)ο缶幊讨蟹浅V匾募夹g(shù),能增強程序的擴展性,提高程序的可維護性。如果一個語言只支持類而不支持多態(tài),只能說明它是基于對象的,而不是面向?qū)ο蟮?。多態(tài)是什么?多種形態(tài)?這樣的回答是將問題還給提問者,任何意義,要理解多態(tài),必須要清楚下列幾個問題:

多態(tài)發(fā)生在什么地方?

多態(tài)是怎么發(fā)生的?

多態(tài)的支撐技術(shù)是什么?

多態(tài)有什么好處?

并且需要通過程序代碼理解和掌握多態(tài),否則就是紙上談兵。

7.2多態(tài)的支撐技術(shù)

7.2.1向上轉(zhuǎn)型1.向上轉(zhuǎn)型子類繼承父類,如Dog繼承Animal,這是繼承技術(shù)。有了繼承才會有向上轉(zhuǎn)型,即父類的引用變量指向子類的對象。這個在現(xiàn)實世界中很好理解:我們看到一只狗,可以說那只狗是一只動物,這種說法是沒有問題的,這就是向上轉(zhuǎn)型,其程序語句與內(nèi)存示意圖如圖7-1所示。

圖7-1向上轉(zhuǎn)型內(nèi)存示意圖

2.向下轉(zhuǎn)型

既然有向上轉(zhuǎn)型,那向下轉(zhuǎn)型呢?如果反過來,用子類的引用變量去指向父類的對象會如何呢?比如:

Dogdog=newAnimal("animal1",20); ×

這樣的語句在程序編譯階段會直接報錯,為什么呢?

現(xiàn)實世界中,不能隨便看到一個動物就說該動物是一條狗,這樣的說法是有問題的。當然也有成立的條件:這只動物本身就是一條狗。如果這只動物是別的動物,這個說法就是錯誤的。誰也不是魔法師,不能指著一條魚說是狗,這只動物就變成了一條狗。

對于Dog類的引用變量dog而言,Dog類中定義了color成員變量,但是它現(xiàn)在指向的是一個Animal的對象,而在Animal類中并沒有定義color成員,Animal對象在堆內(nèi)存中自然也沒有color這個成員,當dog對象想使用color成員的時候也會出錯。

父類所有成員是子類所有成員的子集,所以父類的引用變量指向子類對象不會出現(xiàn)問題,只會出現(xiàn)一些成員被屏蔽不可見的情況;但是子類的引用變量指向父類對象,就會缺少一些成員,導致調(diào)用成員出錯。在實際編程中,向下轉(zhuǎn)型是使用強制類型轉(zhuǎn)換來完成,但是應該謹慎使用,否則就會出現(xiàn)類型轉(zhuǎn)換出錯的異常。

7.2.2動態(tài)綁定

1.方法綁定

把一個方法與其主體關(guān)聯(lián)起來叫做方法的綁定,方法的主體主要是類或?qū)ο?。方法的綁定主要分為靜態(tài)綁定、動態(tài)綁定(也可以稱為前期綁定、后期綁定)。

2.靜態(tài)綁定

在程序執(zhí)行前方法已經(jīng)與主體綁定,這是由編譯器或其他連接程序來實現(xiàn)的,C語言的方法就是典型的靜態(tài)綁定,在編譯之前就可以通過閱讀程序知道程序運行的結(jié)果。Java中也有靜態(tài)綁定,如被final、static、private修飾的方法以及構(gòu)造方法,其它方法都屬于動態(tài)綁定。

3.動態(tài)綁定

動態(tài)綁定即方法與方法的主體在運行時才進行綁定。Java的大部分方法都屬于動態(tài)綁定。Java中的動態(tài)綁定是由Java虛擬機來實現(xiàn)的,不用顯式地聲明;C++則不同,必須明確地聲明某個方法具備后期綁定特性。

例如,程序語句“an.move();”中,move方法的調(diào)用主體是an,但是an只有在運行時才知道是什么動物,由于向上轉(zhuǎn)型的存在,Animal、Fish、Dog、Bird這些類的對象都可以傳遞給an,不同類對象調(diào)用move()方法的表現(xiàn)是不同的,從這里就可以看出多態(tài)的特征了。

7.3多態(tài)實現(xiàn)

現(xiàn)在通過一個動物行為展示程序來說明多態(tài)的具體實現(xiàn),該程序需要以下幾個類:(1)動物類的父類Animal(也可以使用抽象的動物父類Animal2);(2)動物類的子類:Dog、Fish和Bird;(3)展示動物信息和行為的類:ShowMoving。這幾個類及類關(guān)系的UML圖如圖7-2所示。

圖7-2動物類UML圖

程序結(jié)果:

7.4.1多態(tài)發(fā)生的地方上述動物展示的程序中,多態(tài)發(fā)生在showMove()方法上,該函數(shù)傳入的參數(shù)對象為Animal2類型。在運行該方法時,由實際傳入的具體對象來展示動物的信息及行為。如圖7-3所示,可以看出多態(tài)的支撐技術(shù)以及其發(fā)生的地方。

7.4多態(tài)分析

圖7-3多態(tài)的發(fā)生

7.4.2多態(tài)的作用

例如,對類ShowMoving來說,如果沒有動態(tài)綁定,沒有向上轉(zhuǎn)型,那么該類要展示動物類,就會變成這樣:

展示Fish類的showMove方法:

展示Dog類的showMove方法:

展示Bird類的showMove方法:

也就是說,ShowMoving類中會有很多showMove方法的重載,用以適應各種動物。當程序需要展示更多的動物類時,必須一是完成動物子類的編寫;二是在ShowMoving類中重載對應的showMove()方法,而這些方法的方法體中都是相同的代碼:“an.showInfo();”和“an.move();”,這樣就會在該類中出現(xiàn)很多重復代碼,并且難以維護。

有了多態(tài),就可以發(fā)現(xiàn)在前面編寫的程序中,不管增加多少個動物子類,對于ShowMoving類的showMove()方法都是不用改變的,而要展示更多的動物子類行為,只需完成動物子類的編寫,生成對象交給showMove()方法,就可以展示出該動物子類對象的行為。通過方法showMove(Animal2animal),我們知道,只要是實現(xiàn)了抽象類Animal2的子類,都可以將自己的對象傳入這個函數(shù)以展示自己的行為。多態(tài)消除了重復代碼,程序的擴展性和維護性得到了增強。

7.5接口

7.5.1接口聲明接口的定義和類的定義很相似,分為接口的聲明和接口體,只是使用關(guān)鍵字interface代替了關(guān)鍵字class:

接口體中包含常量的聲明(即使用final修飾的成員變量)和抽象方法兩部分。由于全是抽象方法,所以abstract修飾符可以省略不寫。接口體中所有的常量、所有的抽象方法的訪問權(quán)限都是public(可以省略public修飾符)。下面我們通過一個實例將Animal2抽象類的兩個方法抽取出來形成一個接口。

編譯該接口,可以看到該接口源文件名還是Showable.java,編譯之后的字節(jié)碼文件還是Showable.class。

為什么接口的名字很多都加上了一個后綴able?這是大多數(shù)程序員的習慣,也算是一種命名風格,如定義的這個Showable接口,該接口名暗示我們:誰實現(xiàn)這個接口,誰就具有顯示信息和展示行為的能力(也體現(xiàn)在兩個方法的聲明上)。

7.5.2實現(xiàn)接口

實現(xiàn)接口是指某個類通過繼承的方式獲得接口定義的方法,然后將接口中所有的抽象類的方法實現(xiàn)為非抽象方法。實現(xiàn)接口的類稱為該接口的實現(xiàn)類。實現(xiàn)接口是使用implements關(guān)鍵字完成的:

從語法上說,一個類要實現(xiàn)一個接口就要將該接口的所有非抽象方法都實現(xiàn)(讓方法具有方法體,如果方法體是空的{},則稱為空實現(xiàn));如果只是實現(xiàn)了部分抽象方法,按照抽象類的定義,該類就應該聲明為一個抽象類。

一個類只能繼承一個父類,但可以同時實現(xiàn)多個接口,如:

上述程序段的意思是:Dog類繼承了Animal類,獲得了Animal的成員,并且實現(xiàn)了Eatable和Sleepable,實現(xiàn)接口就具備了接口定義的能力,所以Dog類就可以eat,可以sleep了。

下面編寫一個石頭Stone類實現(xiàn)Showable接口。

Stone實現(xiàn)類實現(xiàn)接口Showable的UML圖如圖7-4所示。圖7-4接口實現(xiàn)的UML圖

7.5.3接口與多態(tài)

下面來看如圖7-5所示三組關(guān)系,并思考這三組關(guān)系的演變。圖7-5繼承與實現(xiàn)

父類與子類是繼承關(guān)系,抽象類與實現(xiàn)類實質(zhì)上也是繼承關(guān)系,接口是完全抽象類,所以接口與實現(xiàn)類也可以視為繼承關(guān)系(實現(xiàn)類獲得并重寫了接口定義的成員方法),因此接口與實現(xiàn)類之間同樣可以進行向上轉(zhuǎn)型:接口類型的引用變量指向?qū)崿F(xiàn)類的對象。

上面使用繼承來實現(xiàn)多態(tài)時,ShowMoving類暗示:只要是動物類的子類都可以進入到該類的showMove()方法中展示自己的信息與行為,程序局限于只能對動物類的子類進行展示。而剛剛編寫的Stone類從現(xiàn)實世界的觀點來看,不應該是動物類的子類;從語法的角度來看,Java是單繼承,Stone如果繼承了其它類,就不能再繼承動物類(Java并不支持多繼承),那么,如何讓Stone類也能夠進入到ShowMoving類中展示自己的信息和行動呢?這就需要使用接口的方式實現(xiàn)多態(tài)。下面在以上繼承實現(xiàn)多態(tài)的基礎(chǔ)上進行改寫。

(4)各個動物類的子類Fish、Dog、Bird均不變。

程序結(jié)果:

7.5.4面向接口編程

1.關(guān)于接口的幾點認識

(1)接口的定義關(guān)心的是做什么,具體怎么做是由其實現(xiàn)類來完成的。

(2)接口體現(xiàn)的是一種規(guī)范,對于接口的實現(xiàn)者而言,接口規(guī)定了實現(xiàn)者必須向外提供哪些服務(方法對外提供服務與功能);對于接口的調(diào)用者而言,接口規(guī)定了調(diào)用者可以調(diào)用哪些方法,獲得哪些服務,以及如何調(diào)用這些服務。

(3)當在一個程序中使用接口時,接口是多個模塊間的耦合標準;當在多個應用程序之間使用接口時,接口是多個程序之間的通信標準。

(4)接口作為系統(tǒng)與外界交互的窗口,體現(xiàn)的是一種規(guī)范。從某種程度上來看,接口類似于整個系統(tǒng)的“總綱”或“框架”,它制定了系統(tǒng)各模塊應該遵循的標準。

所以,接口體現(xiàn)的是一種規(guī)范和實現(xiàn)分離的設(shè)計理念,充分利用接口可以很好地降低程序各模塊之間的耦合性,從而提高系統(tǒng)的可擴展性和可維護性。

2.面向接口編程(Interface-OrientedProgramming)

我們開始是使用繼承來實現(xiàn)多態(tài)的,之后我們把要展示的兩個方法showInfo()和move()抽取出來,放到Showable接口中,使用接口實現(xiàn)多態(tài),然后發(fā)現(xiàn)程序的擴展性變得更強,具體的接口和類的結(jié)構(gòu)如圖7-6所示。

圖7-6面向接口編程的UML圖

如果能事先根據(jù)程序的需求預先定義好接口,由接口來定義規(guī)范搭建程序的框架,然后面向接口編程,編寫具體的實現(xiàn)類,就會發(fā)現(xiàn)程序的擴展性和可維護性獲得了增強,并且程序中的耦合性降低了,這是面向?qū)ο缶幊趟M龅降?,也是面向接口編程的主要思想?/p>

本章小結(jié)

1.實現(xiàn)多態(tài)程序需要的基本支撐技術(shù)包括繼承、動態(tài)綁定、向上轉(zhuǎn)型。2.動態(tài)綁定即Java的方法都是有主體的,只有當方法被調(diào)用的時候,才會和方法的主體進行綁定。3.向上轉(zhuǎn)型即父類的引用變量可以指向子類的對象。4.使用繼承來實現(xiàn)多態(tài)程序,程序的擴展性可以這樣描述:只要是Animal類的子類,都能夠進入到多態(tài)方法中展示自己的行為。

5.使用接口來實現(xiàn)多態(tài)程序,程序的擴展性可以這樣描述:誰實現(xiàn)了相應的接口,誰就能進入到多態(tài)方法中展示自己的行為。

6.接口就是完全抽象類,使用interface代替了abstractclass,接口中全是抽象方法,方法聲明可以省略public和abstract的修飾。

7.接口中可以包含字段,但是會被隱式地聲明為static和final。

8.接口的實現(xiàn)類,就是實現(xiàn)了接口所有方法的類,將接口的所有抽象方法重寫為非抽象方法,實現(xiàn)使用的是implements關(guān)鍵字。

9.我們可以使用接口來搭建程序的結(jié)構(gòu)框架,通過面向接口編程,降低程序的耦合性,增強程序的擴展性和可維護性。第八章異常處理8.1異常處理基礎(chǔ)8.2異常處理語法8.3自定義異常類本章小結(jié)

8.1異常處理基礎(chǔ)

所謂異常,就是程序運行時可能出現(xiàn)的一些不正常、錯誤的情況。例如,試圖打開一個根本不存在的文件、類型轉(zhuǎn)換失敗、數(shù)據(jù)庫連接異常等,異常處理機制將會改變程序的控制流程,讓程序有機會對錯誤做出處理。沒有異常處理機制的編程語言,對程序可能出現(xiàn)的異常情況,往往是用很多if-else的分支語句來盡可能地預知可能發(fā)生的情況,保證程序的容錯性。但是由于我們需要預測的可能性太多,代碼將會劇增,程序?qū)⒆兊脧碗s。

如圖8-1所示是Java異常類的層次結(jié)構(gòu)。

圖8-1Java異常層次圖

在這個異常類層次最上層的是Throwable,它是java.Lang包中的一個類,該類的名字類似接口名,但實際是一個Java類。Throwable類是Java語言中所有錯誤或異常的超類,該類派生了兩個子類java.lang.Error和java.lang.Exception。圖中最底層的三項分別說明如下。

(1)?RuntimeException(運行時異常)。運行時異常是程序運行時自動對某些錯誤做出反應而產(chǎn)生的,所以對于運行時異常不需要編寫異常處理的程序代碼,依然可以成功編譯,主要包括算術(shù)異常類、空指針異常類、下標越界異常類、數(shù)組元素個數(shù)為負異常類、類型強制轉(zhuǎn)換異常類、無效參數(shù)異常類等等。這類異常應通過檢查程序和程序調(diào)試來盡量避免,而不是使用try-catch-finally語句捕獲處理,比如a/b,其中的b有可能為0,但是這樣的表達式是不需要進行異常處理的。

(2)其它異常類。其它異常類指的是在Exception類下除了RuntimeException之外的異常類,可以稱之為受檢類異常(CheckedException)。這種異常經(jīng)常是在程序運行過程中由環(huán)境原因造成的,如輸入/輸出I/O異常、網(wǎng)絡地址不能打開、文件未找到等,對于受檢類異常必須在程序中使用try-catch-finally語句捕獲并進行相應的處理,否則不能通過編譯。這是語法上的強制要求,主要在Java的輸入/輸出程序中比較常見。

(3)各種Error子類。各種Error子類是由系統(tǒng)保留的異常類,該類定義了那些應用程序通常無法捕捉到的錯誤,一旦此類錯誤發(fā)生,程序就停止運行。該類主要包括內(nèi)存溢出錯誤類、棧溢出錯誤類、類定義未找到錯誤類、圖形界面錯誤類等。

各種Error子類主要包括OutOfMemoryError、StackOverflowError、NoClassDefFoundError、java.awt.AWTError等。

從上面的描述來看,Java的異常主要有三類:

(1)程序不能處理的錯誤Error;

(2)程序應避免而可以不去捕獲的運行時異常RuntimeException;

(3)必須捕獲的非運行時異常CheckedException。

8.2異常處理語法8.2.1try-catch-finally如果程序運行過程中發(fā)生了異常,系統(tǒng)會捕獲拋出的異常對象并輸出相應的信息,同時終止程序的運行,導致其后的程序無法運行。這可能并不是用戶所期望的,用戶可能更希望由程序來獲取和處理異常對象,其它的程序語句則能夠繼續(xù)運行,這就是捕獲異常的主要作用,也就是說,對可能發(fā)生受檢類異常的語句進行監(jiān)控,被監(jiān)控的語句一旦發(fā)生異常,由異常處理機制來接管程序,并讓后續(xù)的程序可以繼續(xù)運行。

try-catch-finally異常處理語法的格式為

說明:

(1)?try塊:監(jiān)控區(qū)主要是對可能發(fā)生受檢類異常CheckedException的程序語句段使用try進行包圍,而對于可能發(fā)生Error或者RuntimeException的語句一般不進行監(jiān)控。

(2)?catch塊:如果程序需要在catch塊中訪問異常對象的相關(guān)信息,可以通過catch后異常形參來獲得,然后在后面的語句塊中進行處理。在一個try-catch-finally的結(jié)構(gòu)中,try塊只有一個,而catch塊可以有多個,表示能對監(jiān)控區(qū)中的語句進行監(jiān)控,可以捕捉多個可能發(fā)生的異常。

(3)?finally塊:不管監(jiān)控區(qū)是否發(fā)生異常,異常被拋出是否被捕捉到,在try-catch-finally結(jié)構(gòu)的運行流程中如果有finally塊,就一定要執(zhí)行finally塊中的語句。finally塊是可以被省略的,它常常被用來回收一些物理資源,如數(shù)據(jù)庫連接、網(wǎng)絡連接、磁盤文件等。

Java異常處理機制如圖8-2所示。

圖8-2Java異常處理機制

如果沒有異常處理機制,會如何?

(1)異常一旦發(fā)生,整個程序就會在發(fā)生異常的地方終止運行。

(2)如果沒有異常處理機制,可以使用if-else的結(jié)構(gòu)來處理可能發(fā)生的異常,從圖8-2也可以看出異常處理機制類似于分支結(jié)構(gòu)(注:多分支對應了異常處理可以有多個catch語句)。

多個分支結(jié)構(gòu),類似于try-catch-finally中的多個catch結(jié)構(gòu)。如果有多個catch語句,要注意catch的排列順序,如果父類的異常類放在前面,子類放在后面,按照向上轉(zhuǎn)型的觀點,子類catch就不能捕捉到相應的異常對象,因為父類在前面對異常對象的捕獲進行了“攔截”(只要有一個catch捕捉到異常對象,后面的catch結(jié)構(gòu)就不再執(zhí)行,這個與多分支結(jié)構(gòu)if-elseif是邏輯一致的)。有不少“偷懶”的程序員,在監(jiān)控區(qū)后面就只寫一個catch(Exceptionex),表示對所有的異常類進行捕獲,這樣也滿足了異常處理的語法要求。

當try塊一旦發(fā)生異常,系統(tǒng)就會生成對應的異常類對象然后拋出,而異常處理機制就會依次使用后面的catch語句來進行捕捉;一旦捕獲該異常對象,會將該異常對象賦給catch塊后的異常引用變量,程序就可以通過該引用變量來獲取該異常的相關(guān)信息,并進行相應的處理。下面通過一個程序示例來進行說明。

程序結(jié)果:

程序結(jié)果:

8.2.2throw/throws

當異常發(fā)生時,系統(tǒng)會將相對應的異常類對象生成并拋出。如果是Java定義好的運行時異常或者錯誤,都由系統(tǒng)自動拋出;如果想在程序中主動拋出異?;蛘呗暶骱瘮?shù)要拋出異常,可以使用throw或者throws。

1.throw

如果在一個方法內(nèi)部需要主動拋出異常對象,可以使用throw(第一人稱)進行異常拋出,其語法為

throw異常類對象;

程序結(jié)果:

2.throws

如果是在一個方法的頭部聲明該方法可能要拋出異常,可以使用throws來完成(第三人稱單數(shù)加s),其語法為

使用throws聲明拋出異常的一般情況是:當前方法不知道如何(或者不想)處理方法中可能發(fā)生的異常,于是不使用try-catch監(jiān)控和捕捉異常,而是在方法聲明中說明該方法將有可能拋出異常,由方法的上一級調(diào)用者處理,自己不再處理。

函數(shù)嵌套調(diào)用可能會使得異常一層層向上拋出,最后會拋出到main方法。如果main方法也不進行處理,繼續(xù)使用throws聲明拋出異常,而該異常就會拋出到控制臺上顯示異常的棧軌跡信息,并中止程序運行。

當一個方法在頭部聲明拋出異常后,如果為受檢類異常,則該方法被調(diào)用的時候就必須用try-catch-finally結(jié)構(gòu)對該方法監(jiān)控并進行異常處理或者繼續(xù)聲明向上拋出,如下列程序所示。

8.3自定義異常類

用戶自定義異常都應該繼承Exception類,如果希望自定義運行時異常,則應該繼承RuntimeException類。定義異常類時通常需要提供兩種構(gòu)造函數(shù):一是無參數(shù)的構(gòu)造函數(shù);二是帶一個字符串的構(gòu)造函數(shù),這個字符串將作為該異常對象的詳細說明(也就是異常對象的getMessage方法的返回值)。

由于Exception類中具有以下方法,所以自定義異常類也繼承了這幾個方法:

(1)?getMessage():返回該異常的詳細描述字符串。

(2)?printStackTrace():將該異常的跟蹤棧軌跡

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論